From d989f4c29f500667f94d66ed9a8ae4aac3e83c0f Mon Sep 17 00:00:00 2001 From: Max Thompson Date: Tue, 27 Oct 2026 11:35:32 -0700 Subject: [PATCH 01/10] egress policy: add an RFC 8693 token exchange source to CredentialHeader A replace_headers entry can now set token_exchange, naming an RFC 8693 token endpoint, an audience, and scopes. The egress gateway will exchange the actor's JWT there and inject the returned access token. ateapi requires token_url to be https with a DNS hostname and no userinfo, query, or fragment. It also rejects a policy unless one of its https rules allows the token_url's host and port, so the endpoint stays within what the policy already allows. EgressPolicyTemplate gets the same check. The gateway does not exchange tokens yet and denies a request that carries a token_exchange header with a 500. Without that case the entry would reach the credential provider with an empty URI. Signed-off-by: Max Thompson --- api/ateapi/v1/ateapi.pb.go | 1664 +++++++++-------- api/ateapi/v1/ateapi.proto | 43 +- .../apivalidation/actor_template_test.go | 10 + .../internal/apivalidation/egress_policy.go | 60 +- .../apivalidation/egress_policy_test.go | 160 ++ .../apivalidation/zz_generated.validation.go | 170 +- .../internal/router/egress/credentials.go | 11 +- .../router/egress/credentials_test.go | 21 + internal/egresspolicy/egresspolicy.go | 24 + internal/egresspolicy/egresspolicy_test.go | 30 + 10 files changed, 1404 insertions(+), 789 deletions(-) diff --git a/api/ateapi/v1/ateapi.pb.go b/api/ateapi/v1/ateapi.pb.go index d63c010fb1..a343b410a4 100644 --- a/api/ateapi/v1/ateapi.pb.go +++ b/api/ateapi/v1/ateapi.pb.go @@ -1335,7 +1335,7 @@ type EgressPolicy struct { // +k8s:optional // +k8s:maxItems=256 // +k8s:listType=atomic - // +k8s:customValidation # no two rules tie on a name and port + // +k8s:customValidation # no two rules tie on a name and port, and an https rule allows each token_url Rules []*EgressRule `protobuf:"bytes,2,rep,name=rules,proto3" json:"rules,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -1397,7 +1397,7 @@ type EgressPolicyTemplate struct { // +k8s:required // +k8s:maxItems=256 // +k8s:listType=atomic - // +k8s:customValidation # no two rules tie on a name and port + // +k8s:customValidation # no two rules tie on a name and port, and an https rule allows each token_url Rules []*EgressRule `protobuf:"bytes,1,rep,name=rules,proto3" json:"rules,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache @@ -1960,7 +1960,7 @@ type CredentialHeader struct { // Source-agnostic reference interpreted by a registered credential provider: // `ate-secret:////` // - // Exactly one of credential_uri and actor_jwt must be set. + // Exactly one of credential_uri, actor_jwt, and token_exchange must be set. // // +k8s:optional // +k8s:unionMember @@ -1971,7 +1971,13 @@ type CredentialHeader struct { // // +k8s:optional // +k8s:unionMember - ActorJwt *ActorJwtSource `protobuf:"bytes,4,opt,name=actor_jwt,json=actorJwt,proto3" json:"actor_jwt,omitempty"` + ActorJwt *ActorJwtSource `protobuf:"bytes,4,opt,name=actor_jwt,json=actorJwt,proto3" json:"actor_jwt,omitempty"` + // An access token obtained by exchanging an actor JWT at an OAuth 2.0 + // token exchange endpoint (RFC 8693). + // + // +k8s:optional + // +k8s:unionMember + TokenExchange *TokenExchangeSource `protobuf:"bytes,5,opt,name=token_exchange,json=tokenExchange,proto3" json:"token_exchange,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -2034,6 +2040,95 @@ func (x *CredentialHeader) GetActorJwt() *ActorJwtSource { return nil } +func (x *CredentialHeader) GetTokenExchange() *TokenExchangeSource { + if x != nil { + return x.TokenExchange + } + return nil +} + +// TokenExchangeSource has the gateway mint a JWT for the Actor that sent the +// request, bound to audience, and exchange it at token_url (RFC 8693). The +// returned access token becomes the header value. +type TokenExchangeSource struct { + state protoimpl.MessageState `protogen:"open.v1"` + // HTTPS URL of the token endpoint, without userinfo, query, or fragment. + // An https rule in the same policy must allow its host and port. + // + // +k8s:required + // +k8s:maxLength=2048 + // +k8s:customValidation # format + TokenUrl string `protobuf:"bytes,1,opt,name=token_url,json=tokenUrl,proto3" json:"token_url,omitempty"` + // Becomes the actor JWT's only aud and the request's audience parameter. + // + // +k8s:required + // +k8s:minLength=1 + // +k8s:maxLength=512 + Audience string `protobuf:"bytes,2,opt,name=audience,proto3" json:"audience,omitempty"` + // The request's scope parameter, space-joined. + // + // +k8s:optional + // +k8s:maxItems=16 + // +k8s:listType=set + // +k8s:eachVal=+k8s:minLength=1 + // +k8s:eachVal=+k8s:maxLength=256 + // +k8s:customValidation # RFC 6749 scope-token characters + Scopes []string `protobuf:"bytes,3,rep,name=scopes,proto3" json:"scopes,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TokenExchangeSource) Reset() { + *x = TokenExchangeSource{} + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TokenExchangeSource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TokenExchangeSource) ProtoMessage() {} + +func (x *TokenExchangeSource) ProtoReflect() protoreflect.Message { + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use TokenExchangeSource.ProtoReflect.Descriptor instead. +func (*TokenExchangeSource) Descriptor() ([]byte, []int) { + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{18} +} + +func (x *TokenExchangeSource) GetTokenUrl() string { + if x != nil { + return x.TokenUrl + } + return "" +} + +func (x *TokenExchangeSource) GetAudience() string { + if x != nil { + return x.Audience + } + return "" +} + +func (x *TokenExchangeSource) GetScopes() []string { + if x != nil { + return x.Scopes + } + return nil +} + // ActorJwtSource has the gateway mint a JWT for the Actor that sent the // request. Relying parties verify it with OpenID Connect Discovery against its // issuer. The gateway reuses a JWT across requests while enough of its @@ -2060,7 +2155,7 @@ type ActorJwtSource struct { func (x *ActorJwtSource) Reset() { *x = ActorJwtSource{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[18] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2072,7 +2167,7 @@ func (x *ActorJwtSource) String() string { func (*ActorJwtSource) ProtoMessage() {} func (x *ActorJwtSource) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[18] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2085,7 +2180,7 @@ func (x *ActorJwtSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorJwtSource.ProtoReflect.Descriptor instead. func (*ActorJwtSource) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{18} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{19} } func (x *ActorJwtSource) GetAudiences() []string { @@ -2161,7 +2256,7 @@ type ActorStatus struct { func (x *ActorStatus) Reset() { *x = ActorStatus{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[19] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2173,7 +2268,7 @@ func (x *ActorStatus) String() string { func (*ActorStatus) ProtoMessage() {} func (x *ActorStatus) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[19] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2186,7 +2281,7 @@ func (x *ActorStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorStatus.ProtoReflect.Descriptor instead. func (*ActorStatus) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{19} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{20} } func (x *ActorStatus) GetState() ActorState { @@ -2256,7 +2351,7 @@ type ActorCrash struct { func (x *ActorCrash) Reset() { *x = ActorCrash{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[20] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2268,7 +2363,7 @@ func (x *ActorCrash) String() string { func (*ActorCrash) ProtoMessage() {} func (x *ActorCrash) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[20] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2281,7 +2376,7 @@ func (x *ActorCrash) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorCrash.ProtoReflect.Descriptor instead. func (*ActorCrash) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{20} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{21} } func (x *ActorCrash) GetMessage() string { @@ -2387,7 +2482,7 @@ type WorkerAssignment struct { func (x *WorkerAssignment) Reset() { *x = WorkerAssignment{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[21] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2399,7 +2494,7 @@ func (x *WorkerAssignment) String() string { func (*WorkerAssignment) ProtoMessage() {} func (x *WorkerAssignment) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[21] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2412,7 +2507,7 @@ func (x *WorkerAssignment) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerAssignment.ProtoReflect.Descriptor instead. func (*WorkerAssignment) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{21} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{22} } func (x *WorkerAssignment) GetWorker() *ObjectRef { @@ -2512,7 +2607,7 @@ type TagStatus struct { func (x *TagStatus) Reset() { *x = TagStatus{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[22] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2524,7 +2619,7 @@ func (x *TagStatus) String() string { func (*TagStatus) ProtoMessage() {} func (x *TagStatus) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[22] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2537,7 +2632,7 @@ func (x *TagStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use TagStatus.ProtoReflect.Descriptor instead. func (*TagStatus) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{22} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{23} } func (x *TagStatus) GetSnapshot() *Snapshot { @@ -2589,7 +2684,7 @@ type Tag struct { func (x *Tag) Reset() { *x = Tag{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[23] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2601,7 +2696,7 @@ func (x *Tag) String() string { func (*Tag) ProtoMessage() {} func (x *Tag) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[23] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2614,7 +2709,7 @@ func (x *Tag) ProtoReflect() protoreflect.Message { // Deprecated: Use Tag.ProtoReflect.Descriptor instead. func (*Tag) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{23} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{24} } func (x *Tag) GetMetadata() *ResourceMetadata { @@ -2660,7 +2755,7 @@ type Atespace struct { func (x *Atespace) Reset() { *x = Atespace{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[24] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2672,7 +2767,7 @@ func (x *Atespace) String() string { func (*Atespace) ProtoMessage() {} func (x *Atespace) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[24] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2685,7 +2780,7 @@ func (x *Atespace) ProtoReflect() protoreflect.Message { // Deprecated: Use Atespace.ProtoReflect.Descriptor instead. func (*Atespace) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{24} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{25} } func (x *Atespace) GetMetadata() *ResourceMetadata { @@ -2718,7 +2813,7 @@ type ObjectRef struct { func (x *ObjectRef) Reset() { *x = ObjectRef{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[25] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2730,7 +2825,7 @@ func (x *ObjectRef) String() string { func (*ObjectRef) ProtoMessage() {} func (x *ObjectRef) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[25] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2743,7 +2838,7 @@ func (x *ObjectRef) ProtoReflect() protoreflect.Message { // Deprecated: Use ObjectRef.ProtoReflect.Descriptor instead. func (*ObjectRef) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{25} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{26} } func (x *ObjectRef) GetAtespace() string { @@ -2819,7 +2914,7 @@ type ActorTemplate struct { func (x *ActorTemplate) Reset() { *x = ActorTemplate{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[26] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2831,7 +2926,7 @@ func (x *ActorTemplate) String() string { func (*ActorTemplate) ProtoMessage() {} func (x *ActorTemplate) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[26] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2844,7 +2939,7 @@ func (x *ActorTemplate) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorTemplate.ProtoReflect.Descriptor instead. func (*ActorTemplate) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{26} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{27} } func (x *ActorTemplate) GetMetadata() *ResourceMetadata { @@ -2916,7 +3011,7 @@ type ResourceRequirements struct { func (x *ResourceRequirements) Reset() { *x = ResourceRequirements{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[27] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2928,7 +3023,7 @@ func (x *ResourceRequirements) String() string { func (*ResourceRequirements) ProtoMessage() {} func (x *ResourceRequirements) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[27] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2941,7 +3036,7 @@ func (x *ResourceRequirements) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceRequirements.ProtoReflect.Descriptor instead. func (*ResourceRequirements) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{27} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{28} } func (x *ResourceRequirements) GetLimits() *Resources { @@ -2971,7 +3066,7 @@ type Resources struct { func (x *Resources) Reset() { *x = Resources{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[28] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2983,7 +3078,7 @@ func (x *Resources) String() string { func (*Resources) ProtoMessage() {} func (x *Resources) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[28] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2996,7 +3091,7 @@ func (x *Resources) ProtoReflect() protoreflect.Message { // Deprecated: Use Resources.ProtoReflect.Descriptor instead. func (*Resources) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{28} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{29} } func (x *Resources) GetCpuMillicores() int64 { @@ -3037,7 +3132,7 @@ type GoldenSnapshotStatus struct { func (x *GoldenSnapshotStatus) Reset() { *x = GoldenSnapshotStatus{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[29] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3049,7 +3144,7 @@ func (x *GoldenSnapshotStatus) String() string { func (*GoldenSnapshotStatus) ProtoMessage() {} func (x *GoldenSnapshotStatus) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[29] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3062,7 +3157,7 @@ func (x *GoldenSnapshotStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use GoldenSnapshotStatus.ProtoReflect.Descriptor instead. func (*GoldenSnapshotStatus) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{29} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{30} } func (x *GoldenSnapshotStatus) GetGoldenTag() *ObjectRef { @@ -3096,7 +3191,7 @@ type ActorTemplateStatus struct { func (x *ActorTemplateStatus) Reset() { *x = ActorTemplateStatus{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[30] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3108,7 +3203,7 @@ func (x *ActorTemplateStatus) String() string { func (*ActorTemplateStatus) ProtoMessage() {} func (x *ActorTemplateStatus) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[30] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3121,7 +3216,7 @@ func (x *ActorTemplateStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorTemplateStatus.ProtoReflect.Descriptor instead. func (*ActorTemplateStatus) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{30} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{31} } func (x *ActorTemplateStatus) GetGoldenSnapshotStatus() *GoldenSnapshotStatus { @@ -3172,7 +3267,7 @@ type SnapshotConfig struct { func (x *SnapshotConfig) Reset() { *x = SnapshotConfig{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[31] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3184,7 +3279,7 @@ func (x *SnapshotConfig) String() string { func (*SnapshotConfig) ProtoMessage() {} func (x *SnapshotConfig) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[31] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3197,7 +3292,7 @@ func (x *SnapshotConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use SnapshotConfig.ProtoReflect.Descriptor instead. func (*SnapshotConfig) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{31} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{32} } func (x *SnapshotConfig) GetStorageLocation() string { @@ -3237,7 +3332,7 @@ type GoldenSnapshotConfig struct { func (x *GoldenSnapshotConfig) Reset() { *x = GoldenSnapshotConfig{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[32] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3249,7 +3344,7 @@ func (x *GoldenSnapshotConfig) String() string { func (*GoldenSnapshotConfig) ProtoMessage() {} func (x *GoldenSnapshotConfig) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[32] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3262,7 +3357,7 @@ func (x *GoldenSnapshotConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use GoldenSnapshotConfig.ProtoReflect.Descriptor instead. func (*GoldenSnapshotConfig) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{32} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{33} } func (x *GoldenSnapshotConfig) GetMode() GoldenSnapshotMode { @@ -3342,7 +3437,7 @@ type Container struct { func (x *Container) Reset() { *x = Container{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[33] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3354,7 +3449,7 @@ func (x *Container) String() string { func (*Container) ProtoMessage() {} func (x *Container) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[33] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3367,7 +3462,7 @@ func (x *Container) ProtoReflect() protoreflect.Message { // Deprecated: Use Container.ProtoReflect.Descriptor instead. func (*Container) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{33} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{34} } func (x *Container) GetName() string { @@ -3447,7 +3542,7 @@ type SecurityContext struct { func (x *SecurityContext) Reset() { *x = SecurityContext{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[34] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3459,7 +3554,7 @@ func (x *SecurityContext) String() string { func (*SecurityContext) ProtoMessage() {} func (x *SecurityContext) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[34] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3472,7 +3567,7 @@ func (x *SecurityContext) ProtoReflect() protoreflect.Message { // Deprecated: Use SecurityContext.ProtoReflect.Descriptor instead. func (*SecurityContext) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{34} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{35} } func (x *SecurityContext) GetCapabilities() *Capabilities { @@ -3511,7 +3606,7 @@ type Capabilities struct { func (x *Capabilities) Reset() { *x = Capabilities{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[35] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3523,7 +3618,7 @@ func (x *Capabilities) String() string { func (*Capabilities) ProtoMessage() {} func (x *Capabilities) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[35] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3536,7 +3631,7 @@ func (x *Capabilities) ProtoReflect() protoreflect.Message { // Deprecated: Use Capabilities.ProtoReflect.Descriptor instead. func (*Capabilities) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{35} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{36} } func (x *Capabilities) GetAdd() []string { @@ -3574,7 +3669,7 @@ type EnvVar struct { func (x *EnvVar) Reset() { *x = EnvVar{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[36] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3586,7 +3681,7 @@ func (x *EnvVar) String() string { func (*EnvVar) ProtoMessage() {} func (x *EnvVar) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[36] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3599,7 +3694,7 @@ func (x *EnvVar) ProtoReflect() protoreflect.Message { // Deprecated: Use EnvVar.ProtoReflect.Descriptor instead. func (*EnvVar) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{36} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{37} } func (x *EnvVar) GetName() string { @@ -3639,7 +3734,7 @@ type WakeupProbe struct { func (x *WakeupProbe) Reset() { *x = WakeupProbe{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[37] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3651,7 +3746,7 @@ func (x *WakeupProbe) String() string { func (*WakeupProbe) ProtoMessage() {} func (x *WakeupProbe) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[37] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3664,7 +3759,7 @@ func (x *WakeupProbe) ProtoReflect() protoreflect.Message { // Deprecated: Use WakeupProbe.ProtoReflect.Descriptor instead. func (*WakeupProbe) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{37} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{38} } func (x *WakeupProbe) GetHttpGet() *HttpGetAction { @@ -3704,7 +3799,7 @@ type HttpGetAction struct { func (x *HttpGetAction) Reset() { *x = HttpGetAction{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[38] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3716,7 +3811,7 @@ func (x *HttpGetAction) String() string { func (*HttpGetAction) ProtoMessage() {} func (x *HttpGetAction) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[38] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3729,7 +3824,7 @@ func (x *HttpGetAction) ProtoReflect() protoreflect.Message { // Deprecated: Use HttpGetAction.ProtoReflect.Descriptor instead. func (*HttpGetAction) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{38} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{39} } func (x *HttpGetAction) GetPath() string { @@ -3780,7 +3875,7 @@ type Volume struct { func (x *Volume) Reset() { *x = Volume{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[39] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3792,7 +3887,7 @@ func (x *Volume) String() string { func (*Volume) ProtoMessage() {} func (x *Volume) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[39] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3805,7 +3900,7 @@ func (x *Volume) ProtoReflect() protoreflect.Message { // Deprecated: Use Volume.ProtoReflect.Descriptor instead. func (*Volume) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{39} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{40} } func (x *Volume) GetName() string { @@ -3858,7 +3953,7 @@ type ExternalVolumeSource struct { func (x *ExternalVolumeSource) Reset() { *x = ExternalVolumeSource{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[40] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3870,7 +3965,7 @@ func (x *ExternalVolumeSource) String() string { func (*ExternalVolumeSource) ProtoMessage() {} func (x *ExternalVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[40] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3883,7 +3978,7 @@ func (x *ExternalVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ExternalVolumeSource.ProtoReflect.Descriptor instead. func (*ExternalVolumeSource) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{40} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{41} } func (x *ExternalVolumeSource) GetTemplate() *ExternalVolumeTemplate { @@ -3911,7 +4006,7 @@ type ImageVolumeSource struct { func (x *ImageVolumeSource) Reset() { *x = ImageVolumeSource{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[41] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3923,7 +4018,7 @@ func (x *ImageVolumeSource) String() string { func (*ImageVolumeSource) ProtoMessage() {} func (x *ImageVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[41] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3936,7 +4031,7 @@ func (x *ImageVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ImageVolumeSource.ProtoReflect.Descriptor instead. func (*ImageVolumeSource) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{41} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{42} } func (x *ImageVolumeSource) GetReference() string { @@ -3956,7 +4051,7 @@ type DurableDirVolumeSource struct { func (x *DurableDirVolumeSource) Reset() { *x = DurableDirVolumeSource{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[42] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3968,7 +4063,7 @@ func (x *DurableDirVolumeSource) String() string { func (*DurableDirVolumeSource) ProtoMessage() {} func (x *DurableDirVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[42] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3981,7 +4076,7 @@ func (x *DurableDirVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use DurableDirVolumeSource.ProtoReflect.Descriptor instead. func (*DurableDirVolumeSource) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{42} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{43} } // ExternalVolumeTemplate provisions an external volume per actor; the volume @@ -4005,7 +4100,7 @@ type ExternalVolumeTemplate struct { func (x *ExternalVolumeTemplate) Reset() { *x = ExternalVolumeTemplate{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[43] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4017,7 +4112,7 @@ func (x *ExternalVolumeTemplate) String() string { func (*ExternalVolumeTemplate) ProtoMessage() {} func (x *ExternalVolumeTemplate) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[43] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4030,7 +4125,7 @@ func (x *ExternalVolumeTemplate) ProtoReflect() protoreflect.Message { // Deprecated: Use ExternalVolumeTemplate.ProtoReflect.Descriptor instead. func (*ExternalVolumeTemplate) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{43} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{44} } func (x *ExternalVolumeTemplate) GetCapacityBytes() int64 { @@ -4067,7 +4162,7 @@ type SystemInfoVolumeSource struct { func (x *SystemInfoVolumeSource) Reset() { *x = SystemInfoVolumeSource{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[44] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4079,7 +4174,7 @@ func (x *SystemInfoVolumeSource) String() string { func (*SystemInfoVolumeSource) ProtoMessage() {} func (x *SystemInfoVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[44] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4092,7 +4187,7 @@ func (x *SystemInfoVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use SystemInfoVolumeSource.ProtoReflect.Descriptor instead. func (*SystemInfoVolumeSource) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{44} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{45} } func (x *SystemInfoVolumeSource) GetDataSources() []*SystemInfoDataSource { @@ -4118,7 +4213,7 @@ type SystemInfoDataSource struct { func (x *SystemInfoDataSource) Reset() { *x = SystemInfoDataSource{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[45] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4130,7 +4225,7 @@ func (x *SystemInfoDataSource) String() string { func (*SystemInfoDataSource) ProtoMessage() {} func (x *SystemInfoDataSource) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[45] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[46] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4143,7 +4238,7 @@ func (x *SystemInfoDataSource) ProtoReflect() protoreflect.Message { // Deprecated: Use SystemInfoDataSource.ProtoReflect.Descriptor instead. func (*SystemInfoDataSource) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{45} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{46} } func (x *SystemInfoDataSource) GetActorMetadata() *ActorMetadataDataSource { @@ -4180,7 +4275,7 @@ type ActorMetadataDataSource struct { func (x *ActorMetadataDataSource) Reset() { *x = ActorMetadataDataSource{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[46] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4192,7 +4287,7 @@ func (x *ActorMetadataDataSource) String() string { func (*ActorMetadataDataSource) ProtoMessage() {} func (x *ActorMetadataDataSource) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[46] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4205,7 +4300,7 @@ func (x *ActorMetadataDataSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorMetadataDataSource.ProtoReflect.Descriptor instead. func (*ActorMetadataDataSource) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{46} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{47} } func (x *ActorMetadataDataSource) GetItems() []*ActorMetadataItem { @@ -4237,7 +4332,7 @@ type ActorMetadataItem struct { func (x *ActorMetadataItem) Reset() { *x = ActorMetadataItem{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[47] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4249,7 +4344,7 @@ func (x *ActorMetadataItem) String() string { func (*ActorMetadataItem) ProtoMessage() {} func (x *ActorMetadataItem) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[47] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4262,7 +4357,7 @@ func (x *ActorMetadataItem) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorMetadataItem.ProtoReflect.Descriptor instead. func (*ActorMetadataItem) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{47} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{48} } func (x *ActorMetadataItem) GetField() ActorMetadataField { @@ -4314,7 +4409,7 @@ type TrustBundleDataSource struct { func (x *TrustBundleDataSource) Reset() { *x = TrustBundleDataSource{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[48] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4326,7 +4421,7 @@ func (x *TrustBundleDataSource) String() string { func (*TrustBundleDataSource) ProtoMessage() {} func (x *TrustBundleDataSource) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[48] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4339,7 +4434,7 @@ func (x *TrustBundleDataSource) ProtoReflect() protoreflect.Message { // Deprecated: Use TrustBundleDataSource.ProtoReflect.Descriptor instead. func (*TrustBundleDataSource) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{48} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{49} } func (x *TrustBundleDataSource) GetNames() []string { @@ -4379,7 +4474,7 @@ type VolumeMount struct { func (x *VolumeMount) Reset() { *x = VolumeMount{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[49] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4391,7 +4486,7 @@ func (x *VolumeMount) String() string { func (*VolumeMount) ProtoMessage() {} func (x *VolumeMount) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[49] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4404,7 +4499,7 @@ func (x *VolumeMount) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeMount.ProtoReflect.Descriptor instead. func (*VolumeMount) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{49} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{50} } func (x *VolumeMount) GetName() string { @@ -4433,7 +4528,7 @@ type CreateAtespaceRequest struct { func (x *CreateAtespaceRequest) Reset() { *x = CreateAtespaceRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[50] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4445,7 +4540,7 @@ func (x *CreateAtespaceRequest) String() string { func (*CreateAtespaceRequest) ProtoMessage() {} func (x *CreateAtespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[50] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4458,7 +4553,7 @@ func (x *CreateAtespaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateAtespaceRequest.ProtoReflect.Descriptor instead. func (*CreateAtespaceRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{50} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{51} } func (x *CreateAtespaceRequest) GetAtespace() *Atespace { @@ -4479,7 +4574,7 @@ type GetAtespaceRequest struct { func (x *GetAtespaceRequest) Reset() { *x = GetAtespaceRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[51] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4491,7 +4586,7 @@ func (x *GetAtespaceRequest) String() string { func (*GetAtespaceRequest) ProtoMessage() {} func (x *GetAtespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[51] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4504,7 +4599,7 @@ func (x *GetAtespaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAtespaceRequest.ProtoReflect.Descriptor instead. func (*GetAtespaceRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{51} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{52} } func (x *GetAtespaceRequest) GetAtespace() *ObjectRef { @@ -4526,7 +4621,7 @@ type ListAtespacesRequest struct { func (x *ListAtespacesRequest) Reset() { *x = ListAtespacesRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[52] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4538,7 +4633,7 @@ func (x *ListAtespacesRequest) String() string { func (*ListAtespacesRequest) ProtoMessage() {} func (x *ListAtespacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[52] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4551,7 +4646,7 @@ func (x *ListAtespacesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListAtespacesRequest.ProtoReflect.Descriptor instead. func (*ListAtespacesRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{52} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{53} } func (x *ListAtespacesRequest) GetOptions() *ListOptions { @@ -4573,7 +4668,7 @@ type ListAtespacesResponse struct { func (x *ListAtespacesResponse) Reset() { *x = ListAtespacesResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[53] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4585,7 +4680,7 @@ func (x *ListAtespacesResponse) String() string { func (*ListAtespacesResponse) ProtoMessage() {} func (x *ListAtespacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[53] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4598,7 +4693,7 @@ func (x *ListAtespacesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListAtespacesResponse.ProtoReflect.Descriptor instead. func (*ListAtespacesResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{53} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{54} } func (x *ListAtespacesResponse) GetAtespaces() []*Atespace { @@ -4630,7 +4725,7 @@ type DeleteAtespaceRequest struct { func (x *DeleteAtespaceRequest) Reset() { *x = DeleteAtespaceRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[54] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4642,7 +4737,7 @@ func (x *DeleteAtespaceRequest) String() string { func (*DeleteAtespaceRequest) ProtoMessage() {} func (x *DeleteAtespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[54] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4655,7 +4750,7 @@ func (x *DeleteAtespaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteAtespaceRequest.ProtoReflect.Descriptor instead. func (*DeleteAtespaceRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{54} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{55} } func (x *DeleteAtespaceRequest) GetAtespace() *ObjectRef { @@ -4686,7 +4781,7 @@ type CreateActorTemplateRequest struct { func (x *CreateActorTemplateRequest) Reset() { *x = CreateActorTemplateRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[55] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4698,7 +4793,7 @@ func (x *CreateActorTemplateRequest) String() string { func (*CreateActorTemplateRequest) ProtoMessage() {} func (x *CreateActorTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[55] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4711,7 +4806,7 @@ func (x *CreateActorTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateActorTemplateRequest.ProtoReflect.Descriptor instead. func (*CreateActorTemplateRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{55} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{56} } func (x *CreateActorTemplateRequest) GetActorTemplate() *ActorTemplate { @@ -4732,7 +4827,7 @@ type GetActorTemplateRequest struct { func (x *GetActorTemplateRequest) Reset() { *x = GetActorTemplateRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[56] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4744,7 +4839,7 @@ func (x *GetActorTemplateRequest) String() string { func (*GetActorTemplateRequest) ProtoMessage() {} func (x *GetActorTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[56] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4757,7 +4852,7 @@ func (x *GetActorTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetActorTemplateRequest.ProtoReflect.Descriptor instead. func (*GetActorTemplateRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{56} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{57} } func (x *GetActorTemplateRequest) GetActorTemplate() *ObjectRef { @@ -4785,7 +4880,7 @@ type ListActorTemplatesRequest struct { func (x *ListActorTemplatesRequest) Reset() { *x = ListActorTemplatesRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[57] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4797,7 +4892,7 @@ func (x *ListActorTemplatesRequest) String() string { func (*ListActorTemplatesRequest) ProtoMessage() {} func (x *ListActorTemplatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[57] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4810,7 +4905,7 @@ func (x *ListActorTemplatesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorTemplatesRequest.ProtoReflect.Descriptor instead. func (*ListActorTemplatesRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{57} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{58} } func (x *ListActorTemplatesRequest) GetAtespace() string { @@ -4840,7 +4935,7 @@ type ListActorTemplatesResponse struct { func (x *ListActorTemplatesResponse) Reset() { *x = ListActorTemplatesResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[58] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4852,7 +4947,7 @@ func (x *ListActorTemplatesResponse) String() string { func (*ListActorTemplatesResponse) ProtoMessage() {} func (x *ListActorTemplatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[58] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4865,7 +4960,7 @@ func (x *ListActorTemplatesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorTemplatesResponse.ProtoReflect.Descriptor instead. func (*ListActorTemplatesResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{58} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{59} } func (x *ListActorTemplatesResponse) GetActorTemplates() []*ActorTemplate { @@ -4897,7 +4992,7 @@ type DeleteActorTemplateRequest struct { func (x *DeleteActorTemplateRequest) Reset() { *x = DeleteActorTemplateRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[59] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4909,7 +5004,7 @@ func (x *DeleteActorTemplateRequest) String() string { func (*DeleteActorTemplateRequest) ProtoMessage() {} func (x *DeleteActorTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[59] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4922,7 +5017,7 @@ func (x *DeleteActorTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteActorTemplateRequest.ProtoReflect.Descriptor instead. func (*DeleteActorTemplateRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{59} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{60} } func (x *DeleteActorTemplateRequest) GetActorTemplate() *ObjectRef { @@ -4950,7 +5045,7 @@ type GetActorRequest struct { func (x *GetActorRequest) Reset() { *x = GetActorRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[60] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4962,7 +5057,7 @@ func (x *GetActorRequest) String() string { func (*GetActorRequest) ProtoMessage() {} func (x *GetActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[60] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4975,7 +5070,7 @@ func (x *GetActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetActorRequest.ProtoReflect.Descriptor instead. func (*GetActorRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{60} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{61} } func (x *GetActorRequest) GetActor() *ObjectRef { @@ -4998,7 +5093,7 @@ type CreateActorRequest struct { func (x *CreateActorRequest) Reset() { *x = CreateActorRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[61] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5010,7 +5105,7 @@ func (x *CreateActorRequest) String() string { func (*CreateActorRequest) ProtoMessage() {} func (x *CreateActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[61] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5023,7 +5118,7 @@ func (x *CreateActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateActorRequest.ProtoReflect.Descriptor instead. func (*CreateActorRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{61} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{62} } func (x *CreateActorRequest) GetActor() *Actor { @@ -5054,7 +5149,7 @@ type UpdateActorRequest struct { func (x *UpdateActorRequest) Reset() { *x = UpdateActorRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[62] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5066,7 +5161,7 @@ func (x *UpdateActorRequest) String() string { func (*UpdateActorRequest) ProtoMessage() {} func (x *UpdateActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[62] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5079,7 +5174,7 @@ func (x *UpdateActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateActorRequest.ProtoReflect.Descriptor instead. func (*UpdateActorRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{62} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{63} } func (x *UpdateActorRequest) GetActor() *Actor { @@ -5104,7 +5199,7 @@ type SuspendActorRequest struct { func (x *SuspendActorRequest) Reset() { *x = SuspendActorRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[63] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5116,7 +5211,7 @@ func (x *SuspendActorRequest) String() string { func (*SuspendActorRequest) ProtoMessage() {} func (x *SuspendActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[63] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5129,7 +5224,7 @@ func (x *SuspendActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SuspendActorRequest.ProtoReflect.Descriptor instead. func (*SuspendActorRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{63} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{64} } func (x *SuspendActorRequest) GetActor() *ObjectRef { @@ -5155,7 +5250,7 @@ type SuspendActorResponse struct { func (x *SuspendActorResponse) Reset() { *x = SuspendActorResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[64] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5167,7 +5262,7 @@ func (x *SuspendActorResponse) String() string { func (*SuspendActorResponse) ProtoMessage() {} func (x *SuspendActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[64] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5180,7 +5275,7 @@ func (x *SuspendActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SuspendActorResponse.ProtoReflect.Descriptor instead. func (*SuspendActorResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{64} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{65} } func (x *SuspendActorResponse) GetActor() *Actor { @@ -5206,7 +5301,7 @@ type SuspendOptions struct { func (x *SuspendOptions) Reset() { *x = SuspendOptions{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[65] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5218,7 +5313,7 @@ func (x *SuspendOptions) String() string { func (*SuspendOptions) ProtoMessage() {} func (x *SuspendOptions) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[65] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5231,7 +5326,7 @@ func (x *SuspendOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use SuspendOptions.ProtoReflect.Descriptor instead. func (*SuspendOptions) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{65} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{66} } func (x *SuspendOptions) GetMode() DurabilityMode { @@ -5252,7 +5347,7 @@ type ResumeActorRequest struct { func (x *ResumeActorRequest) Reset() { *x = ResumeActorRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[66] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5264,7 +5359,7 @@ func (x *ResumeActorRequest) String() string { func (*ResumeActorRequest) ProtoMessage() {} func (x *ResumeActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[66] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5277,7 +5372,7 @@ func (x *ResumeActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResumeActorRequest.ProtoReflect.Descriptor instead. func (*ResumeActorRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{66} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{67} } func (x *ResumeActorRequest) GetActor() *ObjectRef { @@ -5299,7 +5394,7 @@ type ResumeActorResponse struct { func (x *ResumeActorResponse) Reset() { *x = ResumeActorResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[67] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5311,7 +5406,7 @@ func (x *ResumeActorResponse) String() string { func (*ResumeActorResponse) ProtoMessage() {} func (x *ResumeActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[67] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5324,7 +5419,7 @@ func (x *ResumeActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ResumeActorResponse.ProtoReflect.Descriptor instead. func (*ResumeActorResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{67} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{68} } func (x *ResumeActorResponse) GetActor() *Actor { @@ -5354,7 +5449,7 @@ type RevertActorRequest struct { func (x *RevertActorRequest) Reset() { *x = RevertActorRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[68] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5366,7 +5461,7 @@ func (x *RevertActorRequest) String() string { func (*RevertActorRequest) ProtoMessage() {} func (x *RevertActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[68] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5379,7 +5474,7 @@ func (x *RevertActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RevertActorRequest.ProtoReflect.Descriptor instead. func (*RevertActorRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{68} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{69} } func (x *RevertActorRequest) GetActor() *ObjectRef { @@ -5401,7 +5496,7 @@ type RevertActorResponse struct { func (x *RevertActorResponse) Reset() { *x = RevertActorResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[69] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5413,7 +5508,7 @@ func (x *RevertActorResponse) String() string { func (*RevertActorResponse) ProtoMessage() {} func (x *RevertActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[69] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5426,7 +5521,7 @@ func (x *RevertActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RevertActorResponse.ProtoReflect.Descriptor instead. func (*RevertActorResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{69} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{70} } func (x *RevertActorResponse) GetActor() *Actor { @@ -5451,7 +5546,7 @@ type DeleteActorRequest struct { func (x *DeleteActorRequest) Reset() { *x = DeleteActorRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[70] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5463,7 +5558,7 @@ func (x *DeleteActorRequest) String() string { func (*DeleteActorRequest) ProtoMessage() {} func (x *DeleteActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[70] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5476,7 +5571,7 @@ func (x *DeleteActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteActorRequest.ProtoReflect.Descriptor instead. func (*DeleteActorRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{70} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{71} } func (x *DeleteActorRequest) GetActor() *ObjectRef { @@ -5513,7 +5608,7 @@ type GetActorEgressPolicyRequest struct { func (x *GetActorEgressPolicyRequest) Reset() { *x = GetActorEgressPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[71] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5525,7 +5620,7 @@ func (x *GetActorEgressPolicyRequest) String() string { func (*GetActorEgressPolicyRequest) ProtoMessage() {} func (x *GetActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[71] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5538,7 +5633,7 @@ func (x *GetActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetActorEgressPolicyRequest.ProtoReflect.Descriptor instead. func (*GetActorEgressPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{71} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{72} } func (x *GetActorEgressPolicyRequest) GetActor() *ObjectRef { @@ -5576,7 +5671,7 @@ type CreateActorEgressPolicyRequest struct { func (x *CreateActorEgressPolicyRequest) Reset() { *x = CreateActorEgressPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[72] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5588,7 +5683,7 @@ func (x *CreateActorEgressPolicyRequest) String() string { func (*CreateActorEgressPolicyRequest) ProtoMessage() {} func (x *CreateActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[72] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5601,7 +5696,7 @@ func (x *CreateActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateActorEgressPolicyRequest.ProtoReflect.Descriptor instead. func (*CreateActorEgressPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{72} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{73} } func (x *CreateActorEgressPolicyRequest) GetActor() *ObjectRef { @@ -5639,7 +5734,7 @@ type UpdateActorEgressPolicyRequest struct { func (x *UpdateActorEgressPolicyRequest) Reset() { *x = UpdateActorEgressPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[73] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5651,7 +5746,7 @@ func (x *UpdateActorEgressPolicyRequest) String() string { func (*UpdateActorEgressPolicyRequest) ProtoMessage() {} func (x *UpdateActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[73] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5664,7 +5759,7 @@ func (x *UpdateActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateActorEgressPolicyRequest.ProtoReflect.Descriptor instead. func (*UpdateActorEgressPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{73} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{74} } func (x *UpdateActorEgressPolicyRequest) GetActor() *ObjectRef { @@ -5705,7 +5800,7 @@ type DeleteActorEgressPolicyRequest struct { func (x *DeleteActorEgressPolicyRequest) Reset() { *x = DeleteActorEgressPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[74] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5717,7 +5812,7 @@ func (x *DeleteActorEgressPolicyRequest) String() string { func (*DeleteActorEgressPolicyRequest) ProtoMessage() {} func (x *DeleteActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[74] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5730,7 +5825,7 @@ func (x *DeleteActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteActorEgressPolicyRequest.ProtoReflect.Descriptor instead. func (*DeleteActorEgressPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{74} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{75} } func (x *DeleteActorEgressPolicyRequest) GetActor() *ObjectRef { @@ -5765,7 +5860,7 @@ type GetTagRequest struct { func (x *GetTagRequest) Reset() { *x = GetTagRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[75] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5777,7 +5872,7 @@ func (x *GetTagRequest) String() string { func (*GetTagRequest) ProtoMessage() {} func (x *GetTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[75] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5790,7 +5885,7 @@ func (x *GetTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTagRequest.ProtoReflect.Descriptor instead. func (*GetTagRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{75} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{76} } func (x *GetTagRequest) GetTag() *ObjectRef { @@ -5829,7 +5924,7 @@ type MintActorJwtRequest struct { func (x *MintActorJwtRequest) Reset() { *x = MintActorJwtRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[76] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5841,7 +5936,7 @@ func (x *MintActorJwtRequest) String() string { func (*MintActorJwtRequest) ProtoMessage() {} func (x *MintActorJwtRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[76] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5854,7 +5949,7 @@ func (x *MintActorJwtRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MintActorJwtRequest.ProtoReflect.Descriptor instead. func (*MintActorJwtRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{76} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{77} } func (x *MintActorJwtRequest) GetActor() *ObjectRef { @@ -5909,7 +6004,7 @@ type MintActorJwtResponse struct { func (x *MintActorJwtResponse) Reset() { *x = MintActorJwtResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[77] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5921,7 +6016,7 @@ func (x *MintActorJwtResponse) String() string { func (*MintActorJwtResponse) ProtoMessage() {} func (x *MintActorJwtResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[77] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5934,7 +6029,7 @@ func (x *MintActorJwtResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MintActorJwtResponse.ProtoReflect.Descriptor instead. func (*MintActorJwtResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{77} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{78} } func (x *MintActorJwtResponse) GetActorJwt() string { @@ -5968,7 +6063,7 @@ type ListTagsRequest struct { func (x *ListTagsRequest) Reset() { *x = ListTagsRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[78] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5980,7 +6075,7 @@ func (x *ListTagsRequest) String() string { func (*ListTagsRequest) ProtoMessage() {} func (x *ListTagsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[78] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5993,7 +6088,7 @@ func (x *ListTagsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTagsRequest.ProtoReflect.Descriptor instead. func (*ListTagsRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{78} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{79} } func (x *ListTagsRequest) GetAtespace() string { @@ -6020,7 +6115,7 @@ type ListTagsResponse struct { func (x *ListTagsResponse) Reset() { *x = ListTagsResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[79] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6032,7 +6127,7 @@ func (x *ListTagsResponse) String() string { func (*ListTagsResponse) ProtoMessage() {} func (x *ListTagsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[79] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6045,7 +6140,7 @@ func (x *ListTagsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTagsResponse.ProtoReflect.Descriptor instead. func (*ListTagsResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{79} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{80} } func (x *ListTagsResponse) GetTags() []*Tag { @@ -6090,7 +6185,7 @@ type CreateTagRequest struct { func (x *CreateTagRequest) Reset() { *x = CreateTagRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[80] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6102,7 +6197,7 @@ func (x *CreateTagRequest) String() string { func (*CreateTagRequest) ProtoMessage() {} func (x *CreateTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[80] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6115,7 +6210,7 @@ func (x *CreateTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTagRequest.ProtoReflect.Descriptor instead. func (*CreateTagRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{80} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{81} } func (x *CreateTagRequest) GetTag() *Tag { @@ -6145,7 +6240,7 @@ type UpdateTagRequest struct { func (x *UpdateTagRequest) Reset() { *x = UpdateTagRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[81] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6157,7 +6252,7 @@ func (x *UpdateTagRequest) String() string { func (*UpdateTagRequest) ProtoMessage() {} func (x *UpdateTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[81] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6170,7 +6265,7 @@ func (x *UpdateTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateTagRequest.ProtoReflect.Descriptor instead. func (*UpdateTagRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{81} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{82} } func (x *UpdateTagRequest) GetTag() *Tag { @@ -6195,7 +6290,7 @@ type DeleteTagRequest struct { func (x *DeleteTagRequest) Reset() { *x = DeleteTagRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[82] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6207,7 +6302,7 @@ func (x *DeleteTagRequest) String() string { func (*DeleteTagRequest) ProtoMessage() {} func (x *DeleteTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[82] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[83] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6220,7 +6315,7 @@ func (x *DeleteTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteTagRequest.ProtoReflect.Descriptor instead. func (*DeleteTagRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{82} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{83} } func (x *DeleteTagRequest) GetTag() *ObjectRef { @@ -6259,7 +6354,7 @@ type DeleteOptions struct { func (x *DeleteOptions) Reset() { *x = DeleteOptions{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[83] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6271,7 +6366,7 @@ func (x *DeleteOptions) String() string { func (*DeleteOptions) ProtoMessage() {} func (x *DeleteOptions) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[83] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[84] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6284,7 +6379,7 @@ func (x *DeleteOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteOptions.ProtoReflect.Descriptor instead. func (*DeleteOptions) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{83} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{84} } func (x *DeleteOptions) GetVersion() int64 { @@ -6323,7 +6418,7 @@ type ListOptions struct { func (x *ListOptions) Reset() { *x = ListOptions{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[84] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6335,7 +6430,7 @@ func (x *ListOptions) String() string { func (*ListOptions) ProtoMessage() {} func (x *ListOptions) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[84] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[85] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6348,7 +6443,7 @@ func (x *ListOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use ListOptions.ProtoReflect.Descriptor instead. func (*ListOptions) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{84} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{85} } func (x *ListOptions) GetPageSize() int32 { @@ -6384,7 +6479,7 @@ type ListWorkerActorAssignmentsRequest struct { func (x *ListWorkerActorAssignmentsRequest) Reset() { *x = ListWorkerActorAssignmentsRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[85] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6396,7 +6491,7 @@ func (x *ListWorkerActorAssignmentsRequest) String() string { func (*ListWorkerActorAssignmentsRequest) ProtoMessage() {} func (x *ListWorkerActorAssignmentsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[85] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[86] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6409,7 +6504,7 @@ func (x *ListWorkerActorAssignmentsRequest) ProtoReflect() protoreflect.Message // Deprecated: Use ListWorkerActorAssignmentsRequest.ProtoReflect.Descriptor instead. func (*ListWorkerActorAssignmentsRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{85} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{86} } func (x *ListWorkerActorAssignmentsRequest) GetWorker() *ObjectRef { @@ -6439,7 +6534,7 @@ type ListWorkerActorAssignmentsResponse struct { func (x *ListWorkerActorAssignmentsResponse) Reset() { *x = ListWorkerActorAssignmentsResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[86] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6451,7 +6546,7 @@ func (x *ListWorkerActorAssignmentsResponse) String() string { func (*ListWorkerActorAssignmentsResponse) ProtoMessage() {} func (x *ListWorkerActorAssignmentsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[86] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[87] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6464,7 +6559,7 @@ func (x *ListWorkerActorAssignmentsResponse) ProtoReflect() protoreflect.Message // Deprecated: Use ListWorkerActorAssignmentsResponse.ProtoReflect.Descriptor instead. func (*ListWorkerActorAssignmentsResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{86} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{87} } func (x *ListWorkerActorAssignmentsResponse) GetActorAssignments() []*ActorAssignment { @@ -6493,7 +6588,7 @@ type ListWorkersRequest struct { func (x *ListWorkersRequest) Reset() { *x = ListWorkersRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[87] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6505,7 +6600,7 @@ func (x *ListWorkersRequest) String() string { func (*ListWorkersRequest) ProtoMessage() {} func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[87] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[88] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6518,7 +6613,7 @@ func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkersRequest.ProtoReflect.Descriptor instead. func (*ListWorkersRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{87} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{88} } func (x *ListWorkersRequest) GetOptions() *ListOptions { @@ -6540,7 +6635,7 @@ type ListWorkersResponse struct { func (x *ListWorkersResponse) Reset() { *x = ListWorkersResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[88] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6552,7 +6647,7 @@ func (x *ListWorkersResponse) String() string { func (*ListWorkersResponse) ProtoMessage() {} func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[88] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[89] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6565,7 +6660,7 @@ func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkersResponse.ProtoReflect.Descriptor instead. func (*ListWorkersResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{88} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{89} } func (x *ListWorkersResponse) GetWorkers() []*Worker { @@ -6595,7 +6690,7 @@ type GetWorkerRequest struct { func (x *GetWorkerRequest) Reset() { *x = GetWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[89] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6607,7 +6702,7 @@ func (x *GetWorkerRequest) String() string { func (*GetWorkerRequest) ProtoMessage() {} func (x *GetWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[89] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[90] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6620,7 +6715,7 @@ func (x *GetWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkerRequest.ProtoReflect.Descriptor instead. func (*GetWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{89} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{90} } func (x *GetWorkerRequest) GetWorker() *ObjectRef { @@ -6642,7 +6737,7 @@ type CreateWorkerRequest struct { func (x *CreateWorkerRequest) Reset() { *x = CreateWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[90] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6654,7 +6749,7 @@ func (x *CreateWorkerRequest) String() string { func (*CreateWorkerRequest) ProtoMessage() {} func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[90] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[91] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6667,7 +6762,7 @@ func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkerRequest.ProtoReflect.Descriptor instead. func (*CreateWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{90} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{91} } func (x *CreateWorkerRequest) GetWorker() *Worker { @@ -6702,7 +6797,7 @@ type UpdateWorkerRequest struct { func (x *UpdateWorkerRequest) Reset() { *x = UpdateWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[91] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6714,7 +6809,7 @@ func (x *UpdateWorkerRequest) String() string { func (*UpdateWorkerRequest) ProtoMessage() {} func (x *UpdateWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[91] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[92] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6727,7 +6822,7 @@ func (x *UpdateWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateWorkerRequest.ProtoReflect.Descriptor instead. func (*UpdateWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{91} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{92} } func (x *UpdateWorkerRequest) GetWorker() *Worker { @@ -6754,7 +6849,7 @@ type DeleteWorkerRequest struct { func (x *DeleteWorkerRequest) Reset() { *x = DeleteWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[92] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6766,7 +6861,7 @@ func (x *DeleteWorkerRequest) String() string { func (*DeleteWorkerRequest) ProtoMessage() {} func (x *DeleteWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[92] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[93] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6779,7 +6874,7 @@ func (x *DeleteWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkerRequest.ProtoReflect.Descriptor instead. func (*DeleteWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{92} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{93} } func (x *DeleteWorkerRequest) GetWorker() *ObjectRef { @@ -6809,7 +6904,7 @@ type DrainWorkerRequest struct { func (x *DrainWorkerRequest) Reset() { *x = DrainWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[93] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6821,7 +6916,7 @@ func (x *DrainWorkerRequest) String() string { func (*DrainWorkerRequest) ProtoMessage() {} func (x *DrainWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[93] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[94] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6834,7 +6929,7 @@ func (x *DrainWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DrainWorkerRequest.ProtoReflect.Descriptor instead. func (*DrainWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{93} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{94} } func (x *DrainWorkerRequest) GetWorker() *ObjectRef { @@ -6855,7 +6950,7 @@ type DrainWorkerResponse struct { func (x *DrainWorkerResponse) Reset() { *x = DrainWorkerResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[94] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6867,7 +6962,7 @@ func (x *DrainWorkerResponse) String() string { func (*DrainWorkerResponse) ProtoMessage() {} func (x *DrainWorkerResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[94] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[95] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6880,7 +6975,7 @@ func (x *DrainWorkerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DrainWorkerResponse.ProtoReflect.Descriptor instead. func (*DrainWorkerResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{94} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{95} } func (x *DrainWorkerResponse) GetWorker() *Worker { @@ -6904,7 +6999,7 @@ type MarkWorkerAvailableRequest struct { func (x *MarkWorkerAvailableRequest) Reset() { *x = MarkWorkerAvailableRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[95] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6916,7 +7011,7 @@ func (x *MarkWorkerAvailableRequest) String() string { func (*MarkWorkerAvailableRequest) ProtoMessage() {} func (x *MarkWorkerAvailableRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[95] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[96] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6929,7 +7024,7 @@ func (x *MarkWorkerAvailableRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerAvailableRequest.ProtoReflect.Descriptor instead. func (*MarkWorkerAvailableRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{95} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{96} } func (x *MarkWorkerAvailableRequest) GetWorker() *ObjectRef { @@ -6950,7 +7045,7 @@ type MarkWorkerAvailableResponse struct { func (x *MarkWorkerAvailableResponse) Reset() { *x = MarkWorkerAvailableResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[96] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6962,7 +7057,7 @@ func (x *MarkWorkerAvailableResponse) String() string { func (*MarkWorkerAvailableResponse) ProtoMessage() {} func (x *MarkWorkerAvailableResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[96] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[97] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6975,7 +7070,7 @@ func (x *MarkWorkerAvailableResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerAvailableResponse.ProtoReflect.Descriptor instead. func (*MarkWorkerAvailableResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{96} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{97} } func (x *MarkWorkerAvailableResponse) GetWorker() *Worker { @@ -6999,7 +7094,7 @@ type MarkWorkerUnavailableRequest struct { func (x *MarkWorkerUnavailableRequest) Reset() { *x = MarkWorkerUnavailableRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[97] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7011,7 +7106,7 @@ func (x *MarkWorkerUnavailableRequest) String() string { func (*MarkWorkerUnavailableRequest) ProtoMessage() {} func (x *MarkWorkerUnavailableRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[97] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[98] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7024,7 +7119,7 @@ func (x *MarkWorkerUnavailableRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerUnavailableRequest.ProtoReflect.Descriptor instead. func (*MarkWorkerUnavailableRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{97} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{98} } func (x *MarkWorkerUnavailableRequest) GetWorker() *ObjectRef { @@ -7045,7 +7140,7 @@ type MarkWorkerUnavailableResponse struct { func (x *MarkWorkerUnavailableResponse) Reset() { *x = MarkWorkerUnavailableResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[98] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7057,7 +7152,7 @@ func (x *MarkWorkerUnavailableResponse) String() string { func (*MarkWorkerUnavailableResponse) ProtoMessage() {} func (x *MarkWorkerUnavailableResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[98] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[99] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7070,7 +7165,7 @@ func (x *MarkWorkerUnavailableResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerUnavailableResponse.ProtoReflect.Descriptor instead. func (*MarkWorkerUnavailableResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{98} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{99} } func (x *MarkWorkerUnavailableResponse) GetWorker() *Worker { @@ -7099,7 +7194,7 @@ type ListActorsRequest struct { func (x *ListActorsRequest) Reset() { *x = ListActorsRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[99] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7111,7 +7206,7 @@ func (x *ListActorsRequest) String() string { func (*ListActorsRequest) ProtoMessage() {} func (x *ListActorsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[99] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[100] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7124,7 +7219,7 @@ func (x *ListActorsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorsRequest.ProtoReflect.Descriptor instead. func (*ListActorsRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{99} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{100} } func (x *ListActorsRequest) GetAtespace() string { @@ -7153,7 +7248,7 @@ type ListActorsResponse struct { func (x *ListActorsResponse) Reset() { *x = ListActorsResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[100] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7165,7 +7260,7 @@ func (x *ListActorsResponse) String() string { func (*ListActorsResponse) ProtoMessage() {} func (x *ListActorsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[100] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[101] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7178,7 +7273,7 @@ func (x *ListActorsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorsResponse.ProtoReflect.Descriptor instead. func (*ListActorsResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{100} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{101} } func (x *ListActorsResponse) GetActors() []*Actor { @@ -7266,7 +7361,7 @@ type Worker struct { func (x *Worker) Reset() { *x = Worker{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[101] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7278,7 +7373,7 @@ func (x *Worker) String() string { func (*Worker) ProtoMessage() {} func (x *Worker) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[101] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[102] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7291,7 +7386,7 @@ func (x *Worker) ProtoReflect() protoreflect.Message { // Deprecated: Use Worker.ProtoReflect.Descriptor instead. func (*Worker) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{101} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{102} } func (x *Worker) GetMetadata() *ResourceMetadata { @@ -7433,7 +7528,7 @@ type WorkerStatus struct { func (x *WorkerStatus) Reset() { *x = WorkerStatus{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[102] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7445,7 +7540,7 @@ func (x *WorkerStatus) String() string { func (*WorkerStatus) ProtoMessage() {} func (x *WorkerStatus) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[102] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[103] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7458,7 +7553,7 @@ func (x *WorkerStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerStatus.ProtoReflect.Descriptor instead. func (*WorkerStatus) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{102} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{103} } func (x *WorkerStatus) GetState() WorkerState { @@ -7546,7 +7641,7 @@ type WorkerSandboxClass struct { func (x *WorkerSandboxClass) Reset() { *x = WorkerSandboxClass{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[103] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7558,7 +7653,7 @@ func (x *WorkerSandboxClass) String() string { func (*WorkerSandboxClass) ProtoMessage() {} func (x *WorkerSandboxClass) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[103] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[104] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7571,7 +7666,7 @@ func (x *WorkerSandboxClass) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerSandboxClass.ProtoReflect.Descriptor instead. func (*WorkerSandboxClass) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{103} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{104} } func (x *WorkerSandboxClass) GetSandboxClass() string { @@ -7623,7 +7718,7 @@ type WorkerSandboxRuntime struct { func (x *WorkerSandboxRuntime) Reset() { *x = WorkerSandboxRuntime{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[104] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7635,7 +7730,7 @@ func (x *WorkerSandboxRuntime) String() string { func (*WorkerSandboxRuntime) ProtoMessage() {} func (x *WorkerSandboxRuntime) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[104] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[105] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7648,7 +7743,7 @@ func (x *WorkerSandboxRuntime) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerSandboxRuntime.ProtoReflect.Descriptor instead. func (*WorkerSandboxRuntime) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{104} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{105} } func (x *WorkerSandboxRuntime) GetVersion() *VersionedSandboxCompat { @@ -7689,7 +7784,7 @@ type RuntimeOverhead struct { func (x *RuntimeOverhead) Reset() { *x = RuntimeOverhead{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[105] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7701,7 +7796,7 @@ func (x *RuntimeOverhead) String() string { func (*RuntimeOverhead) ProtoMessage() {} func (x *RuntimeOverhead) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[105] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[106] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7714,7 +7809,7 @@ func (x *RuntimeOverhead) ProtoReflect() protoreflect.Message { // Deprecated: Use RuntimeOverhead.ProtoReflect.Descriptor instead. func (*RuntimeOverhead) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{105} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{106} } func (x *RuntimeOverhead) GetMemoryBytes() int64 { @@ -7753,7 +7848,7 @@ type ActorSandboxRuntime struct { func (x *ActorSandboxRuntime) Reset() { *x = ActorSandboxRuntime{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[106] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7765,7 +7860,7 @@ func (x *ActorSandboxRuntime) String() string { func (*ActorSandboxRuntime) ProtoMessage() {} func (x *ActorSandboxRuntime) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[106] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[107] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7778,7 +7873,7 @@ func (x *ActorSandboxRuntime) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorSandboxRuntime.ProtoReflect.Descriptor instead. func (*ActorSandboxRuntime) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{106} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{107} } func (x *ActorSandboxRuntime) GetSandboxClass() string { @@ -7829,7 +7924,7 @@ type VersionedSandboxCompat struct { func (x *VersionedSandboxCompat) Reset() { *x = VersionedSandboxCompat{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[107] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7841,7 +7936,7 @@ func (x *VersionedSandboxCompat) String() string { func (*VersionedSandboxCompat) ProtoMessage() {} func (x *VersionedSandboxCompat) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[107] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[108] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7854,7 +7949,7 @@ func (x *VersionedSandboxCompat) ProtoReflect() protoreflect.Message { // Deprecated: Use VersionedSandboxCompat.ProtoReflect.Descriptor instead. func (*VersionedSandboxCompat) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{107} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{108} } func (x *VersionedSandboxCompat) GetSchemaVersion() string { @@ -7890,7 +7985,7 @@ type AttributeEntry struct { func (x *AttributeEntry) Reset() { *x = AttributeEntry{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[108] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7902,7 +7997,7 @@ func (x *AttributeEntry) String() string { func (*AttributeEntry) ProtoMessage() {} func (x *AttributeEntry) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[108] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[109] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7915,7 +8010,7 @@ func (x *AttributeEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use AttributeEntry.ProtoReflect.Descriptor instead. func (*AttributeEntry) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{108} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{109} } func (x *AttributeEntry) GetKey() string { @@ -7962,7 +8057,7 @@ type WorkerResources struct { func (x *WorkerResources) Reset() { *x = WorkerResources{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[109] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7974,7 +8069,7 @@ func (x *WorkerResources) String() string { func (*WorkerResources) ProtoMessage() {} func (x *WorkerResources) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[109] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[110] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7987,7 +8082,7 @@ func (x *WorkerResources) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerResources.ProtoReflect.Descriptor instead. func (*WorkerResources) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{109} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{110} } func (x *WorkerResources) GetCpuMillicores() int64 { @@ -8043,7 +8138,7 @@ type ActorAssignment struct { func (x *ActorAssignment) Reset() { *x = ActorAssignment{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[110] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8055,7 +8150,7 @@ func (x *ActorAssignment) String() string { func (*ActorAssignment) ProtoMessage() {} func (x *ActorAssignment) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[110] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[111] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8068,7 +8163,7 @@ func (x *ActorAssignment) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorAssignment.ProtoReflect.Descriptor instead. func (*ActorAssignment) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{110} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{111} } func (x *ActorAssignment) GetMetadata() *ResourceMetadata { @@ -8113,7 +8208,7 @@ type AcquireWorkerEpochRequest struct { func (x *AcquireWorkerEpochRequest) Reset() { *x = AcquireWorkerEpochRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[111] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8125,7 +8220,7 @@ func (x *AcquireWorkerEpochRequest) String() string { func (*AcquireWorkerEpochRequest) ProtoMessage() {} func (x *AcquireWorkerEpochRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[111] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[112] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8138,7 +8233,7 @@ func (x *AcquireWorkerEpochRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AcquireWorkerEpochRequest.ProtoReflect.Descriptor instead. func (*AcquireWorkerEpochRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{111} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{112} } func (x *AcquireWorkerEpochRequest) GetWorker() *ObjectRef { @@ -8158,7 +8253,7 @@ type AcquireWorkerEpochResponse struct { func (x *AcquireWorkerEpochResponse) Reset() { *x = AcquireWorkerEpochResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[112] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8170,7 +8265,7 @@ func (x *AcquireWorkerEpochResponse) String() string { func (*AcquireWorkerEpochResponse) ProtoMessage() {} func (x *AcquireWorkerEpochResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[112] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[113] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8183,7 +8278,7 @@ func (x *AcquireWorkerEpochResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AcquireWorkerEpochResponse.ProtoReflect.Descriptor instead. func (*AcquireWorkerEpochResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{112} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{113} } func (x *AcquireWorkerEpochResponse) GetEpoch() int64 { @@ -8226,7 +8321,7 @@ type RegisterWorkerRequest struct { func (x *RegisterWorkerRequest) Reset() { *x = RegisterWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[113] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8238,7 +8333,7 @@ func (x *RegisterWorkerRequest) String() string { func (*RegisterWorkerRequest) ProtoMessage() {} func (x *RegisterWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[113] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[114] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8251,7 +8346,7 @@ func (x *RegisterWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterWorkerRequest.ProtoReflect.Descriptor instead. func (*RegisterWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{113} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{114} } func (x *RegisterWorkerRequest) GetWorker() *ObjectRef { @@ -8292,7 +8387,7 @@ type RegisterWorkerResponse struct { func (x *RegisterWorkerResponse) Reset() { *x = RegisterWorkerResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[114] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8304,7 +8399,7 @@ func (x *RegisterWorkerResponse) String() string { func (*RegisterWorkerResponse) ProtoMessage() {} func (x *RegisterWorkerResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[114] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[115] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8317,7 +8412,7 @@ func (x *RegisterWorkerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterWorkerResponse.ProtoReflect.Descriptor instead. func (*RegisterWorkerResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{114} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{115} } func (x *RegisterWorkerResponse) GetWorker() *Worker { @@ -8357,7 +8452,7 @@ type SetWorkerSandboxRuntimesRequest struct { func (x *SetWorkerSandboxRuntimesRequest) Reset() { *x = SetWorkerSandboxRuntimesRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[115] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8369,7 +8464,7 @@ func (x *SetWorkerSandboxRuntimesRequest) String() string { func (*SetWorkerSandboxRuntimesRequest) ProtoMessage() {} func (x *SetWorkerSandboxRuntimesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[115] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[116] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8382,7 +8477,7 @@ func (x *SetWorkerSandboxRuntimesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetWorkerSandboxRuntimesRequest.ProtoReflect.Descriptor instead. func (*SetWorkerSandboxRuntimesRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{115} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{116} } func (x *SetWorkerSandboxRuntimesRequest) GetWorker() *ObjectRef { @@ -8416,7 +8511,7 @@ type SetWorkerSandboxRuntimesResponse struct { func (x *SetWorkerSandboxRuntimesResponse) Reset() { *x = SetWorkerSandboxRuntimesResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[116] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8428,7 +8523,7 @@ func (x *SetWorkerSandboxRuntimesResponse) String() string { func (*SetWorkerSandboxRuntimesResponse) ProtoMessage() {} func (x *SetWorkerSandboxRuntimesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[116] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[117] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8441,7 +8536,7 @@ func (x *SetWorkerSandboxRuntimesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetWorkerSandboxRuntimesResponse.ProtoReflect.Descriptor instead. func (*SetWorkerSandboxRuntimesResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{116} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{117} } func (x *SetWorkerSandboxRuntimesResponse) GetWorker() *Worker { @@ -8488,7 +8583,7 @@ type MintWorkerActorCertificateRequest struct { func (x *MintWorkerActorCertificateRequest) Reset() { *x = MintWorkerActorCertificateRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[117] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8500,7 +8595,7 @@ func (x *MintWorkerActorCertificateRequest) String() string { func (*MintWorkerActorCertificateRequest) ProtoMessage() {} func (x *MintWorkerActorCertificateRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[117] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[118] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8513,7 +8608,7 @@ func (x *MintWorkerActorCertificateRequest) ProtoReflect() protoreflect.Message // Deprecated: Use MintWorkerActorCertificateRequest.ProtoReflect.Descriptor instead. func (*MintWorkerActorCertificateRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{117} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{118} } func (x *MintWorkerActorCertificateRequest) GetActor() *ObjectRef { @@ -8557,7 +8652,7 @@ type MintWorkerActorCertificateResponse struct { func (x *MintWorkerActorCertificateResponse) Reset() { *x = MintWorkerActorCertificateResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[118] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8569,7 +8664,7 @@ func (x *MintWorkerActorCertificateResponse) String() string { func (*MintWorkerActorCertificateResponse) ProtoMessage() {} func (x *MintWorkerActorCertificateResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[118] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[119] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8582,7 +8677,7 @@ func (x *MintWorkerActorCertificateResponse) ProtoReflect() protoreflect.Message // Deprecated: Use MintWorkerActorCertificateResponse.ProtoReflect.Descriptor instead. func (*MintWorkerActorCertificateResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{118} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{119} } func (x *MintWorkerActorCertificateResponse) GetActorCertificates() [][]byte { @@ -8623,7 +8718,7 @@ type MintWorkerActorJwtRequest struct { func (x *MintWorkerActorJwtRequest) Reset() { *x = MintWorkerActorJwtRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[119] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8635,7 +8730,7 @@ func (x *MintWorkerActorJwtRequest) String() string { func (*MintWorkerActorJwtRequest) ProtoMessage() {} func (x *MintWorkerActorJwtRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[119] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[120] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8648,7 +8743,7 @@ func (x *MintWorkerActorJwtRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MintWorkerActorJwtRequest.ProtoReflect.Descriptor instead. func (*MintWorkerActorJwtRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{119} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{120} } func (x *MintWorkerActorJwtRequest) GetActor() *ObjectRef { @@ -8696,7 +8791,7 @@ type MintWorkerActorJwtResponse struct { func (x *MintWorkerActorJwtResponse) Reset() { *x = MintWorkerActorJwtResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[120] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8708,7 +8803,7 @@ func (x *MintWorkerActorJwtResponse) String() string { func (*MintWorkerActorJwtResponse) ProtoMessage() {} func (x *MintWorkerActorJwtResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[120] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[121] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8721,7 +8816,7 @@ func (x *MintWorkerActorJwtResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MintWorkerActorJwtResponse.ProtoReflect.Descriptor instead. func (*MintWorkerActorJwtResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{120} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{121} } func (x *MintWorkerActorJwtResponse) GetActorJwt() string { @@ -8772,7 +8867,7 @@ type RequestActorSuspendRequest struct { func (x *RequestActorSuspendRequest) Reset() { *x = RequestActorSuspendRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[121] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8784,7 +8879,7 @@ func (x *RequestActorSuspendRequest) String() string { func (*RequestActorSuspendRequest) ProtoMessage() {} func (x *RequestActorSuspendRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[121] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[122] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8797,7 +8892,7 @@ func (x *RequestActorSuspendRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestActorSuspendRequest.ProtoReflect.Descriptor instead. func (*RequestActorSuspendRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{121} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{122} } func (x *RequestActorSuspendRequest) GetWorker() *ObjectRef { @@ -8831,7 +8926,7 @@ type RequestActorSuspendResponse struct { func (x *RequestActorSuspendResponse) Reset() { *x = RequestActorSuspendResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[122] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8843,7 +8938,7 @@ func (x *RequestActorSuspendResponse) String() string { func (*RequestActorSuspendResponse) ProtoMessage() {} func (x *RequestActorSuspendResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[122] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[123] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8856,7 +8951,7 @@ func (x *RequestActorSuspendResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestActorSuspendResponse.ProtoReflect.Descriptor instead. func (*RequestActorSuspendResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{122} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{123} } func (x *RequestActorSuspendResponse) GetActor() *Actor { @@ -8896,7 +8991,7 @@ type ReportActorCrashedRequest struct { func (x *ReportActorCrashedRequest) Reset() { *x = ReportActorCrashedRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[123] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8908,7 +9003,7 @@ func (x *ReportActorCrashedRequest) String() string { func (*ReportActorCrashedRequest) ProtoMessage() {} func (x *ReportActorCrashedRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[123] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[124] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8921,7 +9016,7 @@ func (x *ReportActorCrashedRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportActorCrashedRequest.ProtoReflect.Descriptor instead. func (*ReportActorCrashedRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{123} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{124} } func (x *ReportActorCrashedRequest) GetWorker() *ObjectRef { @@ -8962,7 +9057,7 @@ type ReportActorCrashedResponse struct { func (x *ReportActorCrashedResponse) Reset() { *x = ReportActorCrashedResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[124] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8974,7 +9069,7 @@ func (x *ReportActorCrashedResponse) String() string { func (*ReportActorCrashedResponse) ProtoMessage() {} func (x *ReportActorCrashedResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[124] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[125] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8987,7 +9082,7 @@ func (x *ReportActorCrashedResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportActorCrashedResponse.ProtoReflect.Descriptor instead. func (*ReportActorCrashedResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{124} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{125} } func (x *ReportActorCrashedResponse) GetActor() *Actor { @@ -9018,7 +9113,7 @@ type ReportActorSnapshotStateRequest struct { func (x *ReportActorSnapshotStateRequest) Reset() { *x = ReportActorSnapshotStateRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[125] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9030,7 +9125,7 @@ func (x *ReportActorSnapshotStateRequest) String() string { func (*ReportActorSnapshotStateRequest) ProtoMessage() {} func (x *ReportActorSnapshotStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[125] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[126] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9043,7 +9138,7 @@ func (x *ReportActorSnapshotStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportActorSnapshotStateRequest.ProtoReflect.Descriptor instead. func (*ReportActorSnapshotStateRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{125} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{126} } func (x *ReportActorSnapshotStateRequest) GetActor() *ObjectRef { @@ -9075,7 +9170,7 @@ type ReportActorSnapshotStateResponse struct { func (x *ReportActorSnapshotStateResponse) Reset() { *x = ReportActorSnapshotStateResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[126] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9087,7 +9182,7 @@ func (x *ReportActorSnapshotStateResponse) String() string { func (*ReportActorSnapshotStateResponse) ProtoMessage() {} func (x *ReportActorSnapshotStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[126] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[127] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9100,7 +9195,7 @@ func (x *ReportActorSnapshotStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportActorSnapshotStateResponse.ProtoReflect.Descriptor instead. func (*ReportActorSnapshotStateResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{126} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{127} } // ActorSnapshotState is what atelet holds for one Actor on its node. It @@ -9134,7 +9229,7 @@ type ActorSnapshotState struct { func (x *ActorSnapshotState) Reset() { *x = ActorSnapshotState{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[127] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9146,7 +9241,7 @@ func (x *ActorSnapshotState) String() string { func (*ActorSnapshotState) ProtoMessage() {} func (x *ActorSnapshotState) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[127] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[128] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9159,7 +9254,7 @@ func (x *ActorSnapshotState) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorSnapshotState.ProtoReflect.Descriptor instead. func (*ActorSnapshotState) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{127} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{128} } func (x *ActorSnapshotState) GetLatestLocal() *SnapshotReport { @@ -9231,7 +9326,7 @@ type SnapshotReport struct { func (x *SnapshotReport) Reset() { *x = SnapshotReport{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[128] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9243,7 +9338,7 @@ func (x *SnapshotReport) String() string { func (*SnapshotReport) ProtoMessage() {} func (x *SnapshotReport) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[128] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[129] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9256,7 +9351,7 @@ func (x *SnapshotReport) ProtoReflect() protoreflect.Message { // Deprecated: Use SnapshotReport.ProtoReflect.Descriptor instead. func (*SnapshotReport) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{128} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{129} } func (x *SnapshotReport) GetUuid() string { @@ -9326,7 +9421,7 @@ type AccessPolicy struct { func (x *AccessPolicy) Reset() { *x = AccessPolicy{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[129] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9338,7 +9433,7 @@ func (x *AccessPolicy) String() string { func (*AccessPolicy) ProtoMessage() {} func (x *AccessPolicy) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[129] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[130] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9351,7 +9446,7 @@ func (x *AccessPolicy) ProtoReflect() protoreflect.Message { // Deprecated: Use AccessPolicy.ProtoReflect.Descriptor instead. func (*AccessPolicy) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{129} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{130} } func (x *AccessPolicy) GetMetadata() *ResourceMetadata { @@ -9393,7 +9488,7 @@ type Binding struct { func (x *Binding) Reset() { *x = Binding{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[130] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9405,7 +9500,7 @@ func (x *Binding) String() string { func (*Binding) ProtoMessage() {} func (x *Binding) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[130] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[131] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9418,7 +9513,7 @@ func (x *Binding) ProtoReflect() protoreflect.Message { // Deprecated: Use Binding.ProtoReflect.Descriptor instead. func (*Binding) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{130} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{131} } func (x *Binding) GetRole() string { @@ -9450,7 +9545,7 @@ type GetGlobalAccessPolicyRequest struct { func (x *GetGlobalAccessPolicyRequest) Reset() { *x = GetGlobalAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[131] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9462,7 +9557,7 @@ func (x *GetGlobalAccessPolicyRequest) String() string { func (*GetGlobalAccessPolicyRequest) ProtoMessage() {} func (x *GetGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[131] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[132] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9475,7 +9570,7 @@ func (x *GetGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGlobalAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*GetGlobalAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{131} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{132} } func (x *GetGlobalAccessPolicyRequest) GetName() string { @@ -9499,7 +9594,7 @@ type CreateGlobalAccessPolicyRequest struct { func (x *CreateGlobalAccessPolicyRequest) Reset() { *x = CreateGlobalAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[132] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9511,7 +9606,7 @@ func (x *CreateGlobalAccessPolicyRequest) String() string { func (*CreateGlobalAccessPolicyRequest) ProtoMessage() {} func (x *CreateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[132] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[133] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9524,7 +9619,7 @@ func (x *CreateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateGlobalAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*CreateGlobalAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{132} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{133} } func (x *CreateGlobalAccessPolicyRequest) GetAccessPolicy() *AccessPolicy { @@ -9548,7 +9643,7 @@ type UpdateGlobalAccessPolicyRequest struct { func (x *UpdateGlobalAccessPolicyRequest) Reset() { *x = UpdateGlobalAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[133] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9560,7 +9655,7 @@ func (x *UpdateGlobalAccessPolicyRequest) String() string { func (*UpdateGlobalAccessPolicyRequest) ProtoMessage() {} func (x *UpdateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[133] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[134] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9573,7 +9668,7 @@ func (x *UpdateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateGlobalAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*UpdateGlobalAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{133} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{134} } func (x *UpdateGlobalAccessPolicyRequest) GetAccessPolicy() *AccessPolicy { @@ -9603,7 +9698,7 @@ type GetAtespaceAccessPolicyRequest struct { func (x *GetAtespaceAccessPolicyRequest) Reset() { *x = GetAtespaceAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[134] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9615,7 +9710,7 @@ func (x *GetAtespaceAccessPolicyRequest) String() string { func (*GetAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *GetAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[134] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[135] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9628,7 +9723,7 @@ func (x *GetAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*GetAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{134} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{135} } func (x *GetAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9664,7 +9759,7 @@ type CreateAtespaceAccessPolicyRequest struct { func (x *CreateAtespaceAccessPolicyRequest) Reset() { *x = CreateAtespaceAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[135] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9676,7 +9771,7 @@ func (x *CreateAtespaceAccessPolicyRequest) String() string { func (*CreateAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *CreateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[135] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[136] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9689,7 +9784,7 @@ func (x *CreateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message // Deprecated: Use CreateAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*CreateAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{135} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{136} } func (x *CreateAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9725,7 +9820,7 @@ type UpdateAtespaceAccessPolicyRequest struct { func (x *UpdateAtespaceAccessPolicyRequest) Reset() { *x = UpdateAtespaceAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[136] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9737,7 +9832,7 @@ func (x *UpdateAtespaceAccessPolicyRequest) String() string { func (*UpdateAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *UpdateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[136] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[137] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9750,7 +9845,7 @@ func (x *UpdateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message // Deprecated: Use UpdateAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*UpdateAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{136} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{137} } func (x *UpdateAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9791,7 +9886,7 @@ type DeleteAtespaceAccessPolicyRequest struct { func (x *DeleteAtespaceAccessPolicyRequest) Reset() { *x = DeleteAtespaceAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[137] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9803,7 +9898,7 @@ func (x *DeleteAtespaceAccessPolicyRequest) String() string { func (*DeleteAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *DeleteAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[137] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[138] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9816,7 +9911,7 @@ func (x *DeleteAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message // Deprecated: Use DeleteAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*DeleteAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{137} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{138} } func (x *DeleteAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9862,7 +9957,7 @@ type ResourceAnnotation struct { func (x *ResourceAnnotation) Reset() { *x = ResourceAnnotation{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[138] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9874,7 +9969,7 @@ func (x *ResourceAnnotation) String() string { func (*ResourceAnnotation) ProtoMessage() {} func (x *ResourceAnnotation) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[138] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[139] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9887,7 +9982,7 @@ func (x *ResourceAnnotation) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceAnnotation.ProtoReflect.Descriptor instead. func (*ResourceAnnotation) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{138} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{139} } func (x *ResourceAnnotation) GetParents() []string { @@ -9930,7 +10025,7 @@ type MethodAnnotation struct { func (x *MethodAnnotation) Reset() { *x = MethodAnnotation{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[139] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9942,7 +10037,7 @@ func (x *MethodAnnotation) String() string { func (*MethodAnnotation) ProtoMessage() {} func (x *MethodAnnotation) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[139] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[140] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9955,7 +10050,7 @@ func (x *MethodAnnotation) ProtoReflect() protoreflect.Message { // Deprecated: Use MethodAnnotation.ProtoReflect.Descriptor instead. func (*MethodAnnotation) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{139} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{140} } func (x *MethodAnnotation) GetResource() string { @@ -10080,12 +10175,17 @@ const file_api_ateapi_v1_ateapi_proto_rawDesc = "" + "\n" + "\bAllPorts\"a\n" + "\x0fHttpRuleEffects\x12N\n" + - "\x0freplace_headers\x18\x01 \x03(\v2%.substrate.ateapi.v1.CredentialHeaderR\x0ereplaceHeaders\"\xab\x01\n" + + "\x0freplace_headers\x18\x01 \x03(\v2%.substrate.ateapi.v1.CredentialHeaderR\x0ereplaceHeaders\"\xfc\x01\n" + "\x10CredentialHeader\x12\x16\n" + "\x06header\x18\x01 \x01(\tR\x06header\x12\x16\n" + "\x06prefix\x18\x02 \x01(\tR\x06prefix\x12%\n" + "\x0ecredential_uri\x18\x03 \x01(\tR\rcredentialUri\x12@\n" + - "\tactor_jwt\x18\x04 \x01(\v2#.substrate.ateapi.v1.ActorJwtSourceR\bactorJwt\"]\n" + + "\tactor_jwt\x18\x04 \x01(\v2#.substrate.ateapi.v1.ActorJwtSourceR\bactorJwt\x12O\n" + + "\x0etoken_exchange\x18\x05 \x01(\v2(.substrate.ateapi.v1.TokenExchangeSourceR\rtokenExchange\"f\n" + + "\x13TokenExchangeSource\x12\x1b\n" + + "\ttoken_url\x18\x01 \x01(\tR\btokenUrl\x12\x1a\n" + + "\baudience\x18\x02 \x01(\tR\baudience\x12\x16\n" + + "\x06scopes\x18\x03 \x03(\tR\x06scopes\"]\n" + "\x0eActorJwtSource\x12\x1c\n" + "\taudiences\x18\x01 \x03(\tR\taudiences\x12-\n" + "\x12expiration_seconds\x18\x02 \x01(\x03R\x11expirationSeconds\"\xbc\x03\n" + @@ -10663,7 +10763,7 @@ func file_api_ateapi_v1_ateapi_proto_rawDescGZIP() []byte { } var file_api_ateapi_v1_ateapi_proto_enumTypes = make([]protoimpl.EnumInfo, 10) -var file_api_ateapi_v1_ateapi_proto_msgTypes = make([]protoimpl.MessageInfo, 143) +var file_api_ateapi_v1_ateapi_proto_msgTypes = make([]protoimpl.MessageInfo, 144) var file_api_ateapi_v1_ateapi_proto_goTypes = []any{ (SnapshotOwner)(0), // 0: substrate.ateapi.v1.SnapshotOwner (SnapshotStorageState)(0), // 1: substrate.ateapi.v1.SnapshotStorageState @@ -10693,134 +10793,135 @@ var file_api_ateapi_v1_ateapi_proto_goTypes = []any{ (*AllPorts)(nil), // 25: substrate.ateapi.v1.AllPorts (*HttpRuleEffects)(nil), // 26: substrate.ateapi.v1.HttpRuleEffects (*CredentialHeader)(nil), // 27: substrate.ateapi.v1.CredentialHeader - (*ActorJwtSource)(nil), // 28: substrate.ateapi.v1.ActorJwtSource - (*ActorStatus)(nil), // 29: substrate.ateapi.v1.ActorStatus - (*ActorCrash)(nil), // 30: substrate.ateapi.v1.ActorCrash - (*WorkerAssignment)(nil), // 31: substrate.ateapi.v1.WorkerAssignment - (*TagStatus)(nil), // 32: substrate.ateapi.v1.TagStatus - (*Tag)(nil), // 33: substrate.ateapi.v1.Tag - (*Atespace)(nil), // 34: substrate.ateapi.v1.Atespace - (*ObjectRef)(nil), // 35: substrate.ateapi.v1.ObjectRef - (*ActorTemplate)(nil), // 36: substrate.ateapi.v1.ActorTemplate - (*ResourceRequirements)(nil), // 37: substrate.ateapi.v1.ResourceRequirements - (*Resources)(nil), // 38: substrate.ateapi.v1.Resources - (*GoldenSnapshotStatus)(nil), // 39: substrate.ateapi.v1.GoldenSnapshotStatus - (*ActorTemplateStatus)(nil), // 40: substrate.ateapi.v1.ActorTemplateStatus - (*SnapshotConfig)(nil), // 41: substrate.ateapi.v1.SnapshotConfig - (*GoldenSnapshotConfig)(nil), // 42: substrate.ateapi.v1.GoldenSnapshotConfig - (*Container)(nil), // 43: substrate.ateapi.v1.Container - (*SecurityContext)(nil), // 44: substrate.ateapi.v1.SecurityContext - (*Capabilities)(nil), // 45: substrate.ateapi.v1.Capabilities - (*EnvVar)(nil), // 46: substrate.ateapi.v1.EnvVar - (*WakeupProbe)(nil), // 47: substrate.ateapi.v1.WakeupProbe - (*HttpGetAction)(nil), // 48: substrate.ateapi.v1.HttpGetAction - (*Volume)(nil), // 49: substrate.ateapi.v1.Volume - (*ExternalVolumeSource)(nil), // 50: substrate.ateapi.v1.ExternalVolumeSource - (*ImageVolumeSource)(nil), // 51: substrate.ateapi.v1.ImageVolumeSource - (*DurableDirVolumeSource)(nil), // 52: substrate.ateapi.v1.DurableDirVolumeSource - (*ExternalVolumeTemplate)(nil), // 53: substrate.ateapi.v1.ExternalVolumeTemplate - (*SystemInfoVolumeSource)(nil), // 54: substrate.ateapi.v1.SystemInfoVolumeSource - (*SystemInfoDataSource)(nil), // 55: substrate.ateapi.v1.SystemInfoDataSource - (*ActorMetadataDataSource)(nil), // 56: substrate.ateapi.v1.ActorMetadataDataSource - (*ActorMetadataItem)(nil), // 57: substrate.ateapi.v1.ActorMetadataItem - (*TrustBundleDataSource)(nil), // 58: substrate.ateapi.v1.TrustBundleDataSource - (*VolumeMount)(nil), // 59: substrate.ateapi.v1.VolumeMount - (*CreateAtespaceRequest)(nil), // 60: substrate.ateapi.v1.CreateAtespaceRequest - (*GetAtespaceRequest)(nil), // 61: substrate.ateapi.v1.GetAtespaceRequest - (*ListAtespacesRequest)(nil), // 62: substrate.ateapi.v1.ListAtespacesRequest - (*ListAtespacesResponse)(nil), // 63: substrate.ateapi.v1.ListAtespacesResponse - (*DeleteAtespaceRequest)(nil), // 64: substrate.ateapi.v1.DeleteAtespaceRequest - (*CreateActorTemplateRequest)(nil), // 65: substrate.ateapi.v1.CreateActorTemplateRequest - (*GetActorTemplateRequest)(nil), // 66: substrate.ateapi.v1.GetActorTemplateRequest - (*ListActorTemplatesRequest)(nil), // 67: substrate.ateapi.v1.ListActorTemplatesRequest - (*ListActorTemplatesResponse)(nil), // 68: substrate.ateapi.v1.ListActorTemplatesResponse - (*DeleteActorTemplateRequest)(nil), // 69: substrate.ateapi.v1.DeleteActorTemplateRequest - (*GetActorRequest)(nil), // 70: substrate.ateapi.v1.GetActorRequest - (*CreateActorRequest)(nil), // 71: substrate.ateapi.v1.CreateActorRequest - (*UpdateActorRequest)(nil), // 72: substrate.ateapi.v1.UpdateActorRequest - (*SuspendActorRequest)(nil), // 73: substrate.ateapi.v1.SuspendActorRequest - (*SuspendActorResponse)(nil), // 74: substrate.ateapi.v1.SuspendActorResponse - (*SuspendOptions)(nil), // 75: substrate.ateapi.v1.SuspendOptions - (*ResumeActorRequest)(nil), // 76: substrate.ateapi.v1.ResumeActorRequest - (*ResumeActorResponse)(nil), // 77: substrate.ateapi.v1.ResumeActorResponse - (*RevertActorRequest)(nil), // 78: substrate.ateapi.v1.RevertActorRequest - (*RevertActorResponse)(nil), // 79: substrate.ateapi.v1.RevertActorResponse - (*DeleteActorRequest)(nil), // 80: substrate.ateapi.v1.DeleteActorRequest - (*GetActorEgressPolicyRequest)(nil), // 81: substrate.ateapi.v1.GetActorEgressPolicyRequest - (*CreateActorEgressPolicyRequest)(nil), // 82: substrate.ateapi.v1.CreateActorEgressPolicyRequest - (*UpdateActorEgressPolicyRequest)(nil), // 83: substrate.ateapi.v1.UpdateActorEgressPolicyRequest - (*DeleteActorEgressPolicyRequest)(nil), // 84: substrate.ateapi.v1.DeleteActorEgressPolicyRequest - (*GetTagRequest)(nil), // 85: substrate.ateapi.v1.GetTagRequest - (*MintActorJwtRequest)(nil), // 86: substrate.ateapi.v1.MintActorJwtRequest - (*MintActorJwtResponse)(nil), // 87: substrate.ateapi.v1.MintActorJwtResponse - (*ListTagsRequest)(nil), // 88: substrate.ateapi.v1.ListTagsRequest - (*ListTagsResponse)(nil), // 89: substrate.ateapi.v1.ListTagsResponse - (*CreateTagRequest)(nil), // 90: substrate.ateapi.v1.CreateTagRequest - (*UpdateTagRequest)(nil), // 91: substrate.ateapi.v1.UpdateTagRequest - (*DeleteTagRequest)(nil), // 92: substrate.ateapi.v1.DeleteTagRequest - (*DeleteOptions)(nil), // 93: substrate.ateapi.v1.DeleteOptions - (*ListOptions)(nil), // 94: substrate.ateapi.v1.ListOptions - (*ListWorkerActorAssignmentsRequest)(nil), // 95: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest - (*ListWorkerActorAssignmentsResponse)(nil), // 96: substrate.ateapi.v1.ListWorkerActorAssignmentsResponse - (*ListWorkersRequest)(nil), // 97: substrate.ateapi.v1.ListWorkersRequest - (*ListWorkersResponse)(nil), // 98: substrate.ateapi.v1.ListWorkersResponse - (*GetWorkerRequest)(nil), // 99: substrate.ateapi.v1.GetWorkerRequest - (*CreateWorkerRequest)(nil), // 100: substrate.ateapi.v1.CreateWorkerRequest - (*UpdateWorkerRequest)(nil), // 101: substrate.ateapi.v1.UpdateWorkerRequest - (*DeleteWorkerRequest)(nil), // 102: substrate.ateapi.v1.DeleteWorkerRequest - (*DrainWorkerRequest)(nil), // 103: substrate.ateapi.v1.DrainWorkerRequest - (*DrainWorkerResponse)(nil), // 104: substrate.ateapi.v1.DrainWorkerResponse - (*MarkWorkerAvailableRequest)(nil), // 105: substrate.ateapi.v1.MarkWorkerAvailableRequest - (*MarkWorkerAvailableResponse)(nil), // 106: substrate.ateapi.v1.MarkWorkerAvailableResponse - (*MarkWorkerUnavailableRequest)(nil), // 107: substrate.ateapi.v1.MarkWorkerUnavailableRequest - (*MarkWorkerUnavailableResponse)(nil), // 108: substrate.ateapi.v1.MarkWorkerUnavailableResponse - (*ListActorsRequest)(nil), // 109: substrate.ateapi.v1.ListActorsRequest - (*ListActorsResponse)(nil), // 110: substrate.ateapi.v1.ListActorsResponse - (*Worker)(nil), // 111: substrate.ateapi.v1.Worker - (*WorkerStatus)(nil), // 112: substrate.ateapi.v1.WorkerStatus - (*WorkerSandboxClass)(nil), // 113: substrate.ateapi.v1.WorkerSandboxClass - (*WorkerSandboxRuntime)(nil), // 114: substrate.ateapi.v1.WorkerSandboxRuntime - (*RuntimeOverhead)(nil), // 115: substrate.ateapi.v1.RuntimeOverhead - (*ActorSandboxRuntime)(nil), // 116: substrate.ateapi.v1.ActorSandboxRuntime - (*VersionedSandboxCompat)(nil), // 117: substrate.ateapi.v1.VersionedSandboxCompat - (*AttributeEntry)(nil), // 118: substrate.ateapi.v1.AttributeEntry - (*WorkerResources)(nil), // 119: substrate.ateapi.v1.WorkerResources - (*ActorAssignment)(nil), // 120: substrate.ateapi.v1.ActorAssignment - (*AcquireWorkerEpochRequest)(nil), // 121: substrate.ateapi.v1.AcquireWorkerEpochRequest - (*AcquireWorkerEpochResponse)(nil), // 122: substrate.ateapi.v1.AcquireWorkerEpochResponse - (*RegisterWorkerRequest)(nil), // 123: substrate.ateapi.v1.RegisterWorkerRequest - (*RegisterWorkerResponse)(nil), // 124: substrate.ateapi.v1.RegisterWorkerResponse - (*SetWorkerSandboxRuntimesRequest)(nil), // 125: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest - (*SetWorkerSandboxRuntimesResponse)(nil), // 126: substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse - (*MintWorkerActorCertificateRequest)(nil), // 127: substrate.ateapi.v1.MintWorkerActorCertificateRequest - (*MintWorkerActorCertificateResponse)(nil), // 128: substrate.ateapi.v1.MintWorkerActorCertificateResponse - (*MintWorkerActorJwtRequest)(nil), // 129: substrate.ateapi.v1.MintWorkerActorJwtRequest - (*MintWorkerActorJwtResponse)(nil), // 130: substrate.ateapi.v1.MintWorkerActorJwtResponse - (*RequestActorSuspendRequest)(nil), // 131: substrate.ateapi.v1.RequestActorSuspendRequest - (*RequestActorSuspendResponse)(nil), // 132: substrate.ateapi.v1.RequestActorSuspendResponse - (*ReportActorCrashedRequest)(nil), // 133: substrate.ateapi.v1.ReportActorCrashedRequest - (*ReportActorCrashedResponse)(nil), // 134: substrate.ateapi.v1.ReportActorCrashedResponse - (*ReportActorSnapshotStateRequest)(nil), // 135: substrate.ateapi.v1.ReportActorSnapshotStateRequest - (*ReportActorSnapshotStateResponse)(nil), // 136: substrate.ateapi.v1.ReportActorSnapshotStateResponse - (*ActorSnapshotState)(nil), // 137: substrate.ateapi.v1.ActorSnapshotState - (*SnapshotReport)(nil), // 138: substrate.ateapi.v1.SnapshotReport - (*AccessPolicy)(nil), // 139: substrate.ateapi.v1.AccessPolicy - (*Binding)(nil), // 140: substrate.ateapi.v1.Binding - (*GetGlobalAccessPolicyRequest)(nil), // 141: substrate.ateapi.v1.GetGlobalAccessPolicyRequest - (*CreateGlobalAccessPolicyRequest)(nil), // 142: substrate.ateapi.v1.CreateGlobalAccessPolicyRequest - (*UpdateGlobalAccessPolicyRequest)(nil), // 143: substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest - (*GetAtespaceAccessPolicyRequest)(nil), // 144: substrate.ateapi.v1.GetAtespaceAccessPolicyRequest - (*CreateAtespaceAccessPolicyRequest)(nil), // 145: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest - (*UpdateAtespaceAccessPolicyRequest)(nil), // 146: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest - (*DeleteAtespaceAccessPolicyRequest)(nil), // 147: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest - (*ResourceAnnotation)(nil), // 148: substrate.ateapi.v1.ResourceAnnotation - (*MethodAnnotation)(nil), // 149: substrate.ateapi.v1.MethodAnnotation - nil, // 150: substrate.ateapi.v1.Selector.MatchLabelsEntry - nil, // 151: substrate.ateapi.v1.ExternalVolumeStatus.VolumeContextEntry - nil, // 152: substrate.ateapi.v1.Worker.LabelsEntry - (*timestamppb.Timestamp)(nil), // 153: google.protobuf.Timestamp - (*descriptorpb.MessageOptions)(nil), // 154: google.protobuf.MessageOptions - (*descriptorpb.MethodOptions)(nil), // 155: google.protobuf.MethodOptions + (*TokenExchangeSource)(nil), // 28: substrate.ateapi.v1.TokenExchangeSource + (*ActorJwtSource)(nil), // 29: substrate.ateapi.v1.ActorJwtSource + (*ActorStatus)(nil), // 30: substrate.ateapi.v1.ActorStatus + (*ActorCrash)(nil), // 31: substrate.ateapi.v1.ActorCrash + (*WorkerAssignment)(nil), // 32: substrate.ateapi.v1.WorkerAssignment + (*TagStatus)(nil), // 33: substrate.ateapi.v1.TagStatus + (*Tag)(nil), // 34: substrate.ateapi.v1.Tag + (*Atespace)(nil), // 35: substrate.ateapi.v1.Atespace + (*ObjectRef)(nil), // 36: substrate.ateapi.v1.ObjectRef + (*ActorTemplate)(nil), // 37: substrate.ateapi.v1.ActorTemplate + (*ResourceRequirements)(nil), // 38: substrate.ateapi.v1.ResourceRequirements + (*Resources)(nil), // 39: substrate.ateapi.v1.Resources + (*GoldenSnapshotStatus)(nil), // 40: substrate.ateapi.v1.GoldenSnapshotStatus + (*ActorTemplateStatus)(nil), // 41: substrate.ateapi.v1.ActorTemplateStatus + (*SnapshotConfig)(nil), // 42: substrate.ateapi.v1.SnapshotConfig + (*GoldenSnapshotConfig)(nil), // 43: substrate.ateapi.v1.GoldenSnapshotConfig + (*Container)(nil), // 44: substrate.ateapi.v1.Container + (*SecurityContext)(nil), // 45: substrate.ateapi.v1.SecurityContext + (*Capabilities)(nil), // 46: substrate.ateapi.v1.Capabilities + (*EnvVar)(nil), // 47: substrate.ateapi.v1.EnvVar + (*WakeupProbe)(nil), // 48: substrate.ateapi.v1.WakeupProbe + (*HttpGetAction)(nil), // 49: substrate.ateapi.v1.HttpGetAction + (*Volume)(nil), // 50: substrate.ateapi.v1.Volume + (*ExternalVolumeSource)(nil), // 51: substrate.ateapi.v1.ExternalVolumeSource + (*ImageVolumeSource)(nil), // 52: substrate.ateapi.v1.ImageVolumeSource + (*DurableDirVolumeSource)(nil), // 53: substrate.ateapi.v1.DurableDirVolumeSource + (*ExternalVolumeTemplate)(nil), // 54: substrate.ateapi.v1.ExternalVolumeTemplate + (*SystemInfoVolumeSource)(nil), // 55: substrate.ateapi.v1.SystemInfoVolumeSource + (*SystemInfoDataSource)(nil), // 56: substrate.ateapi.v1.SystemInfoDataSource + (*ActorMetadataDataSource)(nil), // 57: substrate.ateapi.v1.ActorMetadataDataSource + (*ActorMetadataItem)(nil), // 58: substrate.ateapi.v1.ActorMetadataItem + (*TrustBundleDataSource)(nil), // 59: substrate.ateapi.v1.TrustBundleDataSource + (*VolumeMount)(nil), // 60: substrate.ateapi.v1.VolumeMount + (*CreateAtespaceRequest)(nil), // 61: substrate.ateapi.v1.CreateAtespaceRequest + (*GetAtespaceRequest)(nil), // 62: substrate.ateapi.v1.GetAtespaceRequest + (*ListAtespacesRequest)(nil), // 63: substrate.ateapi.v1.ListAtespacesRequest + (*ListAtespacesResponse)(nil), // 64: substrate.ateapi.v1.ListAtespacesResponse + (*DeleteAtespaceRequest)(nil), // 65: substrate.ateapi.v1.DeleteAtespaceRequest + (*CreateActorTemplateRequest)(nil), // 66: substrate.ateapi.v1.CreateActorTemplateRequest + (*GetActorTemplateRequest)(nil), // 67: substrate.ateapi.v1.GetActorTemplateRequest + (*ListActorTemplatesRequest)(nil), // 68: substrate.ateapi.v1.ListActorTemplatesRequest + (*ListActorTemplatesResponse)(nil), // 69: substrate.ateapi.v1.ListActorTemplatesResponse + (*DeleteActorTemplateRequest)(nil), // 70: substrate.ateapi.v1.DeleteActorTemplateRequest + (*GetActorRequest)(nil), // 71: substrate.ateapi.v1.GetActorRequest + (*CreateActorRequest)(nil), // 72: substrate.ateapi.v1.CreateActorRequest + (*UpdateActorRequest)(nil), // 73: substrate.ateapi.v1.UpdateActorRequest + (*SuspendActorRequest)(nil), // 74: substrate.ateapi.v1.SuspendActorRequest + (*SuspendActorResponse)(nil), // 75: substrate.ateapi.v1.SuspendActorResponse + (*SuspendOptions)(nil), // 76: substrate.ateapi.v1.SuspendOptions + (*ResumeActorRequest)(nil), // 77: substrate.ateapi.v1.ResumeActorRequest + (*ResumeActorResponse)(nil), // 78: substrate.ateapi.v1.ResumeActorResponse + (*RevertActorRequest)(nil), // 79: substrate.ateapi.v1.RevertActorRequest + (*RevertActorResponse)(nil), // 80: substrate.ateapi.v1.RevertActorResponse + (*DeleteActorRequest)(nil), // 81: substrate.ateapi.v1.DeleteActorRequest + (*GetActorEgressPolicyRequest)(nil), // 82: substrate.ateapi.v1.GetActorEgressPolicyRequest + (*CreateActorEgressPolicyRequest)(nil), // 83: substrate.ateapi.v1.CreateActorEgressPolicyRequest + (*UpdateActorEgressPolicyRequest)(nil), // 84: substrate.ateapi.v1.UpdateActorEgressPolicyRequest + (*DeleteActorEgressPolicyRequest)(nil), // 85: substrate.ateapi.v1.DeleteActorEgressPolicyRequest + (*GetTagRequest)(nil), // 86: substrate.ateapi.v1.GetTagRequest + (*MintActorJwtRequest)(nil), // 87: substrate.ateapi.v1.MintActorJwtRequest + (*MintActorJwtResponse)(nil), // 88: substrate.ateapi.v1.MintActorJwtResponse + (*ListTagsRequest)(nil), // 89: substrate.ateapi.v1.ListTagsRequest + (*ListTagsResponse)(nil), // 90: substrate.ateapi.v1.ListTagsResponse + (*CreateTagRequest)(nil), // 91: substrate.ateapi.v1.CreateTagRequest + (*UpdateTagRequest)(nil), // 92: substrate.ateapi.v1.UpdateTagRequest + (*DeleteTagRequest)(nil), // 93: substrate.ateapi.v1.DeleteTagRequest + (*DeleteOptions)(nil), // 94: substrate.ateapi.v1.DeleteOptions + (*ListOptions)(nil), // 95: substrate.ateapi.v1.ListOptions + (*ListWorkerActorAssignmentsRequest)(nil), // 96: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest + (*ListWorkerActorAssignmentsResponse)(nil), // 97: substrate.ateapi.v1.ListWorkerActorAssignmentsResponse + (*ListWorkersRequest)(nil), // 98: substrate.ateapi.v1.ListWorkersRequest + (*ListWorkersResponse)(nil), // 99: substrate.ateapi.v1.ListWorkersResponse + (*GetWorkerRequest)(nil), // 100: substrate.ateapi.v1.GetWorkerRequest + (*CreateWorkerRequest)(nil), // 101: substrate.ateapi.v1.CreateWorkerRequest + (*UpdateWorkerRequest)(nil), // 102: substrate.ateapi.v1.UpdateWorkerRequest + (*DeleteWorkerRequest)(nil), // 103: substrate.ateapi.v1.DeleteWorkerRequest + (*DrainWorkerRequest)(nil), // 104: substrate.ateapi.v1.DrainWorkerRequest + (*DrainWorkerResponse)(nil), // 105: substrate.ateapi.v1.DrainWorkerResponse + (*MarkWorkerAvailableRequest)(nil), // 106: substrate.ateapi.v1.MarkWorkerAvailableRequest + (*MarkWorkerAvailableResponse)(nil), // 107: substrate.ateapi.v1.MarkWorkerAvailableResponse + (*MarkWorkerUnavailableRequest)(nil), // 108: substrate.ateapi.v1.MarkWorkerUnavailableRequest + (*MarkWorkerUnavailableResponse)(nil), // 109: substrate.ateapi.v1.MarkWorkerUnavailableResponse + (*ListActorsRequest)(nil), // 110: substrate.ateapi.v1.ListActorsRequest + (*ListActorsResponse)(nil), // 111: substrate.ateapi.v1.ListActorsResponse + (*Worker)(nil), // 112: substrate.ateapi.v1.Worker + (*WorkerStatus)(nil), // 113: substrate.ateapi.v1.WorkerStatus + (*WorkerSandboxClass)(nil), // 114: substrate.ateapi.v1.WorkerSandboxClass + (*WorkerSandboxRuntime)(nil), // 115: substrate.ateapi.v1.WorkerSandboxRuntime + (*RuntimeOverhead)(nil), // 116: substrate.ateapi.v1.RuntimeOverhead + (*ActorSandboxRuntime)(nil), // 117: substrate.ateapi.v1.ActorSandboxRuntime + (*VersionedSandboxCompat)(nil), // 118: substrate.ateapi.v1.VersionedSandboxCompat + (*AttributeEntry)(nil), // 119: substrate.ateapi.v1.AttributeEntry + (*WorkerResources)(nil), // 120: substrate.ateapi.v1.WorkerResources + (*ActorAssignment)(nil), // 121: substrate.ateapi.v1.ActorAssignment + (*AcquireWorkerEpochRequest)(nil), // 122: substrate.ateapi.v1.AcquireWorkerEpochRequest + (*AcquireWorkerEpochResponse)(nil), // 123: substrate.ateapi.v1.AcquireWorkerEpochResponse + (*RegisterWorkerRequest)(nil), // 124: substrate.ateapi.v1.RegisterWorkerRequest + (*RegisterWorkerResponse)(nil), // 125: substrate.ateapi.v1.RegisterWorkerResponse + (*SetWorkerSandboxRuntimesRequest)(nil), // 126: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest + (*SetWorkerSandboxRuntimesResponse)(nil), // 127: substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse + (*MintWorkerActorCertificateRequest)(nil), // 128: substrate.ateapi.v1.MintWorkerActorCertificateRequest + (*MintWorkerActorCertificateResponse)(nil), // 129: substrate.ateapi.v1.MintWorkerActorCertificateResponse + (*MintWorkerActorJwtRequest)(nil), // 130: substrate.ateapi.v1.MintWorkerActorJwtRequest + (*MintWorkerActorJwtResponse)(nil), // 131: substrate.ateapi.v1.MintWorkerActorJwtResponse + (*RequestActorSuspendRequest)(nil), // 132: substrate.ateapi.v1.RequestActorSuspendRequest + (*RequestActorSuspendResponse)(nil), // 133: substrate.ateapi.v1.RequestActorSuspendResponse + (*ReportActorCrashedRequest)(nil), // 134: substrate.ateapi.v1.ReportActorCrashedRequest + (*ReportActorCrashedResponse)(nil), // 135: substrate.ateapi.v1.ReportActorCrashedResponse + (*ReportActorSnapshotStateRequest)(nil), // 136: substrate.ateapi.v1.ReportActorSnapshotStateRequest + (*ReportActorSnapshotStateResponse)(nil), // 137: substrate.ateapi.v1.ReportActorSnapshotStateResponse + (*ActorSnapshotState)(nil), // 138: substrate.ateapi.v1.ActorSnapshotState + (*SnapshotReport)(nil), // 139: substrate.ateapi.v1.SnapshotReport + (*AccessPolicy)(nil), // 140: substrate.ateapi.v1.AccessPolicy + (*Binding)(nil), // 141: substrate.ateapi.v1.Binding + (*GetGlobalAccessPolicyRequest)(nil), // 142: substrate.ateapi.v1.GetGlobalAccessPolicyRequest + (*CreateGlobalAccessPolicyRequest)(nil), // 143: substrate.ateapi.v1.CreateGlobalAccessPolicyRequest + (*UpdateGlobalAccessPolicyRequest)(nil), // 144: substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest + (*GetAtespaceAccessPolicyRequest)(nil), // 145: substrate.ateapi.v1.GetAtespaceAccessPolicyRequest + (*CreateAtespaceAccessPolicyRequest)(nil), // 146: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest + (*UpdateAtespaceAccessPolicyRequest)(nil), // 147: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest + (*DeleteAtespaceAccessPolicyRequest)(nil), // 148: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest + (*ResourceAnnotation)(nil), // 149: substrate.ateapi.v1.ResourceAnnotation + (*MethodAnnotation)(nil), // 150: substrate.ateapi.v1.MethodAnnotation + nil, // 151: substrate.ateapi.v1.Selector.MatchLabelsEntry + nil, // 152: substrate.ateapi.v1.ExternalVolumeStatus.VolumeContextEntry + nil, // 153: substrate.ateapi.v1.Worker.LabelsEntry + (*timestamppb.Timestamp)(nil), // 154: google.protobuf.Timestamp + (*descriptorpb.MessageOptions)(nil), // 155: google.protobuf.MessageOptions + (*descriptorpb.MethodOptions)(nil), // 156: google.protobuf.MethodOptions } var file_api_ateapi_v1_ateapi_proto_depIdxs = []int32{ 1, // 0: substrate.ateapi.v1.SnapshotStorage.state:type_name -> substrate.ateapi.v1.SnapshotStorageState @@ -10828,18 +10929,18 @@ var file_api_ateapi_v1_ateapi_proto_depIdxs = []int32{ 0, // 2: substrate.ateapi.v1.Snapshot.owner:type_name -> substrate.ateapi.v1.SnapshotOwner 2, // 3: substrate.ateapi.v1.Snapshot.fidelity:type_name -> substrate.ateapi.v1.SnapshotFidelity 11, // 4: substrate.ateapi.v1.Snapshot.durable_snapshot:type_name -> substrate.ateapi.v1.SnapshotStorage - 116, // 5: substrate.ateapi.v1.Snapshot.sandbox_runtime:type_name -> substrate.ateapi.v1.ActorSandboxRuntime - 150, // 6: substrate.ateapi.v1.Selector.match_labels:type_name -> substrate.ateapi.v1.Selector.MatchLabelsEntry - 153, // 7: substrate.ateapi.v1.ResourceMetadata.create_time:type_name -> google.protobuf.Timestamp - 153, // 8: substrate.ateapi.v1.ResourceMetadata.update_time:type_name -> google.protobuf.Timestamp - 153, // 9: substrate.ateapi.v1.ResourceMetadata.delete_time:type_name -> google.protobuf.Timestamp + 117, // 5: substrate.ateapi.v1.Snapshot.sandbox_runtime:type_name -> substrate.ateapi.v1.ActorSandboxRuntime + 151, // 6: substrate.ateapi.v1.Selector.match_labels:type_name -> substrate.ateapi.v1.Selector.MatchLabelsEntry + 154, // 7: substrate.ateapi.v1.ResourceMetadata.create_time:type_name -> google.protobuf.Timestamp + 154, // 8: substrate.ateapi.v1.ResourceMetadata.update_time:type_name -> google.protobuf.Timestamp + 154, // 9: substrate.ateapi.v1.ResourceMetadata.delete_time:type_name -> google.protobuf.Timestamp 16, // 10: substrate.ateapi.v1.ExternalVolumes.volumes:type_name -> substrate.ateapi.v1.ExternalVolumeStatus - 151, // 11: substrate.ateapi.v1.ExternalVolumeStatus.volume_context:type_name -> substrate.ateapi.v1.ExternalVolumeStatus.VolumeContextEntry + 152, // 11: substrate.ateapi.v1.ExternalVolumeStatus.volume_context:type_name -> substrate.ateapi.v1.ExternalVolumeStatus.VolumeContextEntry 14, // 12: substrate.ateapi.v1.Actor.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata - 35, // 13: substrate.ateapi.v1.Actor.actor_template:type_name -> substrate.ateapi.v1.ObjectRef + 36, // 13: substrate.ateapi.v1.Actor.actor_template:type_name -> substrate.ateapi.v1.ObjectRef 13, // 14: substrate.ateapi.v1.Actor.worker_selector:type_name -> substrate.ateapi.v1.Selector - 35, // 15: substrate.ateapi.v1.Actor.source_tag:type_name -> substrate.ateapi.v1.ObjectRef - 29, // 16: substrate.ateapi.v1.Actor.status:type_name -> substrate.ateapi.v1.ActorStatus + 36, // 15: substrate.ateapi.v1.Actor.source_tag:type_name -> substrate.ateapi.v1.ObjectRef + 30, // 16: substrate.ateapi.v1.Actor.status:type_name -> substrate.ateapi.v1.ActorStatus 14, // 17: substrate.ateapi.v1.EgressPolicy.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata 20, // 18: substrate.ateapi.v1.EgressPolicy.rules:type_name -> substrate.ateapi.v1.EgressRule 20, // 19: substrate.ateapi.v1.EgressPolicyTemplate.rules:type_name -> substrate.ateapi.v1.EgressRule @@ -10853,276 +10954,277 @@ var file_api_ateapi_v1_ateapi_proto_depIdxs = []int32{ 24, // 27: substrate.ateapi.v1.TlsPassthroughRule.ports:type_name -> substrate.ateapi.v1.Ports 25, // 28: substrate.ateapi.v1.Ports.all:type_name -> substrate.ateapi.v1.AllPorts 27, // 29: substrate.ateapi.v1.HttpRuleEffects.replace_headers:type_name -> substrate.ateapi.v1.CredentialHeader - 28, // 30: substrate.ateapi.v1.CredentialHeader.actor_jwt:type_name -> substrate.ateapi.v1.ActorJwtSource - 4, // 31: substrate.ateapi.v1.ActorStatus.state:type_name -> substrate.ateapi.v1.ActorState - 31, // 32: substrate.ateapi.v1.ActorStatus.worker_assignment:type_name -> substrate.ateapi.v1.WorkerAssignment - 15, // 33: substrate.ateapi.v1.ActorStatus.external_volumes:type_name -> substrate.ateapi.v1.ExternalVolumes - 30, // 34: substrate.ateapi.v1.ActorStatus.crash:type_name -> substrate.ateapi.v1.ActorCrash - 12, // 35: substrate.ateapi.v1.ActorStatus.snapshots:type_name -> substrate.ateapi.v1.Snapshot - 153, // 36: substrate.ateapi.v1.ActorCrash.crash_time:type_name -> google.protobuf.Timestamp - 35, // 37: substrate.ateapi.v1.WorkerAssignment.worker:type_name -> substrate.ateapi.v1.ObjectRef - 116, // 38: substrate.ateapi.v1.WorkerAssignment.sandbox_runtime:type_name -> substrate.ateapi.v1.ActorSandboxRuntime - 12, // 39: substrate.ateapi.v1.TagStatus.snapshot:type_name -> substrate.ateapi.v1.Snapshot - 14, // 40: substrate.ateapi.v1.Tag.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata - 32, // 41: substrate.ateapi.v1.Tag.status:type_name -> substrate.ateapi.v1.TagStatus - 3, // 42: substrate.ateapi.v1.Tag.scope:type_name -> substrate.ateapi.v1.TagScope - 35, // 43: substrate.ateapi.v1.Tag.source_actor:type_name -> substrate.ateapi.v1.ObjectRef - 14, // 44: substrate.ateapi.v1.Atespace.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata - 14, // 45: substrate.ateapi.v1.ActorTemplate.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata - 13, // 46: substrate.ateapi.v1.ActorTemplate.worker_selector:type_name -> substrate.ateapi.v1.Selector - 43, // 47: substrate.ateapi.v1.ActorTemplate.containers:type_name -> substrate.ateapi.v1.Container - 49, // 48: substrate.ateapi.v1.ActorTemplate.volumes:type_name -> substrate.ateapi.v1.Volume - 41, // 49: substrate.ateapi.v1.ActorTemplate.snapshot_config:type_name -> substrate.ateapi.v1.SnapshotConfig - 37, // 50: substrate.ateapi.v1.ActorTemplate.resources:type_name -> substrate.ateapi.v1.ResourceRequirements - 19, // 51: substrate.ateapi.v1.ActorTemplate.golden_egress_policy:type_name -> substrate.ateapi.v1.EgressPolicyTemplate - 40, // 52: substrate.ateapi.v1.ActorTemplate.status:type_name -> substrate.ateapi.v1.ActorTemplateStatus - 38, // 53: substrate.ateapi.v1.ResourceRequirements.limits:type_name -> substrate.ateapi.v1.Resources - 35, // 54: substrate.ateapi.v1.GoldenSnapshotStatus.golden_tag:type_name -> substrate.ateapi.v1.ObjectRef - 153, // 55: substrate.ateapi.v1.GoldenSnapshotStatus.snapshot_time:type_name -> google.protobuf.Timestamp - 39, // 56: substrate.ateapi.v1.ActorTemplateStatus.golden_snapshot_status:type_name -> substrate.ateapi.v1.GoldenSnapshotStatus - 2, // 57: substrate.ateapi.v1.SnapshotConfig.preferred_fidelity:type_name -> substrate.ateapi.v1.SnapshotFidelity - 42, // 58: substrate.ateapi.v1.SnapshotConfig.golden_snapshot_config:type_name -> substrate.ateapi.v1.GoldenSnapshotConfig - 5, // 59: substrate.ateapi.v1.GoldenSnapshotConfig.mode:type_name -> substrate.ateapi.v1.GoldenSnapshotMode - 46, // 60: substrate.ateapi.v1.Container.env:type_name -> substrate.ateapi.v1.EnvVar - 47, // 61: substrate.ateapi.v1.Container.wakeup_probe:type_name -> substrate.ateapi.v1.WakeupProbe - 59, // 62: substrate.ateapi.v1.Container.volume_mounts:type_name -> substrate.ateapi.v1.VolumeMount - 44, // 63: substrate.ateapi.v1.Container.security_context:type_name -> substrate.ateapi.v1.SecurityContext - 37, // 64: substrate.ateapi.v1.Container.resources:type_name -> substrate.ateapi.v1.ResourceRequirements - 45, // 65: substrate.ateapi.v1.SecurityContext.capabilities:type_name -> substrate.ateapi.v1.Capabilities - 48, // 66: substrate.ateapi.v1.WakeupProbe.http_get:type_name -> substrate.ateapi.v1.HttpGetAction - 52, // 67: substrate.ateapi.v1.Volume.durable_dir:type_name -> substrate.ateapi.v1.DurableDirVolumeSource - 50, // 68: substrate.ateapi.v1.Volume.external:type_name -> substrate.ateapi.v1.ExternalVolumeSource - 54, // 69: substrate.ateapi.v1.Volume.system_info:type_name -> substrate.ateapi.v1.SystemInfoVolumeSource - 51, // 70: substrate.ateapi.v1.Volume.image:type_name -> substrate.ateapi.v1.ImageVolumeSource - 53, // 71: substrate.ateapi.v1.ExternalVolumeSource.template:type_name -> substrate.ateapi.v1.ExternalVolumeTemplate - 55, // 72: substrate.ateapi.v1.SystemInfoVolumeSource.data_sources:type_name -> substrate.ateapi.v1.SystemInfoDataSource - 56, // 73: substrate.ateapi.v1.SystemInfoDataSource.actor_metadata:type_name -> substrate.ateapi.v1.ActorMetadataDataSource - 58, // 74: substrate.ateapi.v1.SystemInfoDataSource.trust_bundle:type_name -> substrate.ateapi.v1.TrustBundleDataSource - 57, // 75: substrate.ateapi.v1.ActorMetadataDataSource.items:type_name -> substrate.ateapi.v1.ActorMetadataItem - 6, // 76: substrate.ateapi.v1.ActorMetadataItem.field:type_name -> substrate.ateapi.v1.ActorMetadataField - 34, // 77: substrate.ateapi.v1.CreateAtespaceRequest.atespace:type_name -> substrate.ateapi.v1.Atespace - 35, // 78: substrate.ateapi.v1.GetAtespaceRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 94, // 79: substrate.ateapi.v1.ListAtespacesRequest.options:type_name -> substrate.ateapi.v1.ListOptions - 34, // 80: substrate.ateapi.v1.ListAtespacesResponse.atespaces:type_name -> substrate.ateapi.v1.Atespace - 35, // 81: substrate.ateapi.v1.DeleteAtespaceRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 93, // 82: substrate.ateapi.v1.DeleteAtespaceRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions - 36, // 83: substrate.ateapi.v1.CreateActorTemplateRequest.actor_template:type_name -> substrate.ateapi.v1.ActorTemplate - 35, // 84: substrate.ateapi.v1.GetActorTemplateRequest.actor_template:type_name -> substrate.ateapi.v1.ObjectRef - 94, // 85: substrate.ateapi.v1.ListActorTemplatesRequest.options:type_name -> substrate.ateapi.v1.ListOptions - 36, // 86: substrate.ateapi.v1.ListActorTemplatesResponse.actor_templates:type_name -> substrate.ateapi.v1.ActorTemplate - 35, // 87: substrate.ateapi.v1.DeleteActorTemplateRequest.actor_template:type_name -> substrate.ateapi.v1.ObjectRef - 93, // 88: substrate.ateapi.v1.DeleteActorTemplateRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions - 35, // 89: substrate.ateapi.v1.GetActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 17, // 90: substrate.ateapi.v1.CreateActorRequest.actor:type_name -> substrate.ateapi.v1.Actor - 17, // 91: substrate.ateapi.v1.UpdateActorRequest.actor:type_name -> substrate.ateapi.v1.Actor - 35, // 92: substrate.ateapi.v1.SuspendActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 75, // 93: substrate.ateapi.v1.SuspendActorRequest.options:type_name -> substrate.ateapi.v1.SuspendOptions - 17, // 94: substrate.ateapi.v1.SuspendActorResponse.actor:type_name -> substrate.ateapi.v1.Actor - 7, // 95: substrate.ateapi.v1.SuspendOptions.mode:type_name -> substrate.ateapi.v1.DurabilityMode - 35, // 96: substrate.ateapi.v1.ResumeActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 17, // 97: substrate.ateapi.v1.ResumeActorResponse.actor:type_name -> substrate.ateapi.v1.Actor - 35, // 98: substrate.ateapi.v1.RevertActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 17, // 99: substrate.ateapi.v1.RevertActorResponse.actor:type_name -> substrate.ateapi.v1.Actor - 35, // 100: substrate.ateapi.v1.DeleteActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 93, // 101: substrate.ateapi.v1.DeleteActorRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions - 35, // 102: substrate.ateapi.v1.GetActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 103: substrate.ateapi.v1.CreateActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 18, // 104: substrate.ateapi.v1.CreateActorEgressPolicyRequest.egress_policy:type_name -> substrate.ateapi.v1.EgressPolicy - 35, // 105: substrate.ateapi.v1.UpdateActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 18, // 106: substrate.ateapi.v1.UpdateActorEgressPolicyRequest.egress_policy:type_name -> substrate.ateapi.v1.EgressPolicy - 35, // 107: substrate.ateapi.v1.DeleteActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 93, // 108: substrate.ateapi.v1.DeleteActorEgressPolicyRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions - 35, // 109: substrate.ateapi.v1.GetTagRequest.tag:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 110: substrate.ateapi.v1.MintActorJwtRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 153, // 111: substrate.ateapi.v1.MintActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp - 94, // 112: substrate.ateapi.v1.ListTagsRequest.options:type_name -> substrate.ateapi.v1.ListOptions - 33, // 113: substrate.ateapi.v1.ListTagsResponse.tags:type_name -> substrate.ateapi.v1.Tag - 33, // 114: substrate.ateapi.v1.CreateTagRequest.tag:type_name -> substrate.ateapi.v1.Tag - 33, // 115: substrate.ateapi.v1.UpdateTagRequest.tag:type_name -> substrate.ateapi.v1.Tag - 35, // 116: substrate.ateapi.v1.DeleteTagRequest.tag:type_name -> substrate.ateapi.v1.ObjectRef - 93, // 117: substrate.ateapi.v1.DeleteTagRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions - 35, // 118: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 94, // 119: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest.options:type_name -> substrate.ateapi.v1.ListOptions - 120, // 120: substrate.ateapi.v1.ListWorkerActorAssignmentsResponse.actor_assignments:type_name -> substrate.ateapi.v1.ActorAssignment - 94, // 121: substrate.ateapi.v1.ListWorkersRequest.options:type_name -> substrate.ateapi.v1.ListOptions - 111, // 122: substrate.ateapi.v1.ListWorkersResponse.workers:type_name -> substrate.ateapi.v1.Worker - 35, // 123: substrate.ateapi.v1.GetWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 111, // 124: substrate.ateapi.v1.CreateWorkerRequest.worker:type_name -> substrate.ateapi.v1.Worker - 111, // 125: substrate.ateapi.v1.UpdateWorkerRequest.worker:type_name -> substrate.ateapi.v1.Worker - 35, // 126: substrate.ateapi.v1.DeleteWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 93, // 127: substrate.ateapi.v1.DeleteWorkerRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions - 35, // 128: substrate.ateapi.v1.DrainWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 111, // 129: substrate.ateapi.v1.DrainWorkerResponse.worker:type_name -> substrate.ateapi.v1.Worker - 35, // 130: substrate.ateapi.v1.MarkWorkerAvailableRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 111, // 131: substrate.ateapi.v1.MarkWorkerAvailableResponse.worker:type_name -> substrate.ateapi.v1.Worker - 35, // 132: substrate.ateapi.v1.MarkWorkerUnavailableRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 111, // 133: substrate.ateapi.v1.MarkWorkerUnavailableResponse.worker:type_name -> substrate.ateapi.v1.Worker - 94, // 134: substrate.ateapi.v1.ListActorsRequest.options:type_name -> substrate.ateapi.v1.ListOptions - 17, // 135: substrate.ateapi.v1.ListActorsResponse.actors:type_name -> substrate.ateapi.v1.Actor - 14, // 136: substrate.ateapi.v1.Worker.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata - 152, // 137: substrate.ateapi.v1.Worker.labels:type_name -> substrate.ateapi.v1.Worker.LabelsEntry - 112, // 138: substrate.ateapi.v1.Worker.status:type_name -> substrate.ateapi.v1.WorkerStatus - 8, // 139: substrate.ateapi.v1.WorkerStatus.state:type_name -> substrate.ateapi.v1.WorkerState - 119, // 140: substrate.ateapi.v1.WorkerStatus.capacity:type_name -> substrate.ateapi.v1.WorkerResources - 119, // 141: substrate.ateapi.v1.WorkerStatus.allocated:type_name -> substrate.ateapi.v1.WorkerResources - 113, // 142: substrate.ateapi.v1.WorkerStatus.sandbox_classes:type_name -> substrate.ateapi.v1.WorkerSandboxClass - 114, // 143: substrate.ateapi.v1.WorkerSandboxClass.default_runtime:type_name -> substrate.ateapi.v1.WorkerSandboxRuntime - 114, // 144: substrate.ateapi.v1.WorkerSandboxClass.restorable_runtimes:type_name -> substrate.ateapi.v1.WorkerSandboxRuntime - 117, // 145: substrate.ateapi.v1.WorkerSandboxRuntime.version:type_name -> substrate.ateapi.v1.VersionedSandboxCompat - 115, // 146: substrate.ateapi.v1.WorkerSandboxRuntime.actor_overhead:type_name -> substrate.ateapi.v1.RuntimeOverhead - 117, // 147: substrate.ateapi.v1.ActorSandboxRuntime.version:type_name -> substrate.ateapi.v1.VersionedSandboxCompat - 118, // 148: substrate.ateapi.v1.VersionedSandboxCompat.attributes:type_name -> substrate.ateapi.v1.AttributeEntry - 14, // 149: substrate.ateapi.v1.ActorAssignment.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata - 35, // 150: substrate.ateapi.v1.ActorAssignment.actor:type_name -> substrate.ateapi.v1.ObjectRef - 38, // 151: substrate.ateapi.v1.ActorAssignment.resources:type_name -> substrate.ateapi.v1.Resources - 35, // 152: substrate.ateapi.v1.AcquireWorkerEpochRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 153: substrate.ateapi.v1.RegisterWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 119, // 154: substrate.ateapi.v1.RegisterWorkerRequest.capacity:type_name -> substrate.ateapi.v1.WorkerResources - 111, // 155: substrate.ateapi.v1.RegisterWorkerResponse.worker:type_name -> substrate.ateapi.v1.Worker - 35, // 156: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 113, // 157: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest.sandbox_classes:type_name -> substrate.ateapi.v1.WorkerSandboxClass - 111, // 158: substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse.worker:type_name -> substrate.ateapi.v1.Worker - 35, // 159: substrate.ateapi.v1.MintWorkerActorCertificateRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 160: substrate.ateapi.v1.MintWorkerActorCertificateRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 161: substrate.ateapi.v1.MintWorkerActorJwtRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 153, // 162: substrate.ateapi.v1.MintWorkerActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp - 35, // 163: substrate.ateapi.v1.RequestActorSuspendRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 164: substrate.ateapi.v1.RequestActorSuspendRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 17, // 165: substrate.ateapi.v1.RequestActorSuspendResponse.actor:type_name -> substrate.ateapi.v1.Actor - 35, // 166: substrate.ateapi.v1.ReportActorCrashedRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 167: substrate.ateapi.v1.ReportActorCrashedRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 17, // 168: substrate.ateapi.v1.ReportActorCrashedResponse.actor:type_name -> substrate.ateapi.v1.Actor - 35, // 169: substrate.ateapi.v1.ReportActorSnapshotStateRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 137, // 170: substrate.ateapi.v1.ReportActorSnapshotStateRequest.state:type_name -> substrate.ateapi.v1.ActorSnapshotState - 138, // 171: substrate.ateapi.v1.ActorSnapshotState.latest_local:type_name -> substrate.ateapi.v1.SnapshotReport - 138, // 172: substrate.ateapi.v1.ActorSnapshotState.uploading:type_name -> substrate.ateapi.v1.SnapshotReport - 138, // 173: substrate.ateapi.v1.ActorSnapshotState.latest_durable:type_name -> substrate.ateapi.v1.SnapshotReport - 153, // 174: substrate.ateapi.v1.ActorSnapshotState.upload_due_time:type_name -> google.protobuf.Timestamp - 2, // 175: substrate.ateapi.v1.SnapshotReport.fidelity:type_name -> substrate.ateapi.v1.SnapshotFidelity - 10, // 176: substrate.ateapi.v1.SnapshotReport.object:type_name -> substrate.ateapi.v1.ObjectSnapshot - 153, // 177: substrate.ateapi.v1.SnapshotReport.local_commit_time:type_name -> google.protobuf.Timestamp - 153, // 178: substrate.ateapi.v1.SnapshotReport.durable_complete_time:type_name -> google.protobuf.Timestamp - 14, // 179: substrate.ateapi.v1.AccessPolicy.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata - 140, // 180: substrate.ateapi.v1.AccessPolicy.bindings:type_name -> substrate.ateapi.v1.Binding - 139, // 181: substrate.ateapi.v1.CreateGlobalAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy - 139, // 182: substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy - 35, // 183: substrate.ateapi.v1.GetAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 184: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 139, // 185: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy - 35, // 186: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 139, // 187: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy - 35, // 188: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 93, // 189: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions - 9, // 190: substrate.ateapi.v1.ResourceAnnotation.scope:type_name -> substrate.ateapi.v1.Scope - 154, // 191: substrate.ateapi.v1.resource:extendee -> google.protobuf.MessageOptions - 155, // 192: substrate.ateapi.v1.method:extendee -> google.protobuf.MethodOptions - 148, // 193: substrate.ateapi.v1.resource:type_name -> substrate.ateapi.v1.ResourceAnnotation - 149, // 194: substrate.ateapi.v1.method:type_name -> substrate.ateapi.v1.MethodAnnotation - 70, // 195: substrate.ateapi.v1.Control.GetActor:input_type -> substrate.ateapi.v1.GetActorRequest - 71, // 196: substrate.ateapi.v1.Control.CreateActor:input_type -> substrate.ateapi.v1.CreateActorRequest - 72, // 197: substrate.ateapi.v1.Control.UpdateActor:input_type -> substrate.ateapi.v1.UpdateActorRequest - 73, // 198: substrate.ateapi.v1.Control.SuspendActor:input_type -> substrate.ateapi.v1.SuspendActorRequest - 76, // 199: substrate.ateapi.v1.Control.ResumeActor:input_type -> substrate.ateapi.v1.ResumeActorRequest - 78, // 200: substrate.ateapi.v1.Control.RevertActor:input_type -> substrate.ateapi.v1.RevertActorRequest - 80, // 201: substrate.ateapi.v1.Control.DeleteActor:input_type -> substrate.ateapi.v1.DeleteActorRequest - 81, // 202: substrate.ateapi.v1.Control.GetActorEgressPolicy:input_type -> substrate.ateapi.v1.GetActorEgressPolicyRequest - 82, // 203: substrate.ateapi.v1.Control.CreateActorEgressPolicy:input_type -> substrate.ateapi.v1.CreateActorEgressPolicyRequest - 83, // 204: substrate.ateapi.v1.Control.UpdateActorEgressPolicy:input_type -> substrate.ateapi.v1.UpdateActorEgressPolicyRequest - 84, // 205: substrate.ateapi.v1.Control.DeleteActorEgressPolicy:input_type -> substrate.ateapi.v1.DeleteActorEgressPolicyRequest - 86, // 206: substrate.ateapi.v1.Control.MintActorJwt:input_type -> substrate.ateapi.v1.MintActorJwtRequest - 90, // 207: substrate.ateapi.v1.Control.CreateTag:input_type -> substrate.ateapi.v1.CreateTagRequest - 85, // 208: substrate.ateapi.v1.Control.GetTag:input_type -> substrate.ateapi.v1.GetTagRequest - 88, // 209: substrate.ateapi.v1.Control.ListTags:input_type -> substrate.ateapi.v1.ListTagsRequest - 91, // 210: substrate.ateapi.v1.Control.UpdateTag:input_type -> substrate.ateapi.v1.UpdateTagRequest - 92, // 211: substrate.ateapi.v1.Control.DeleteTag:input_type -> substrate.ateapi.v1.DeleteTagRequest - 97, // 212: substrate.ateapi.v1.Control.ListWorkers:input_type -> substrate.ateapi.v1.ListWorkersRequest - 99, // 213: substrate.ateapi.v1.Control.GetWorker:input_type -> substrate.ateapi.v1.GetWorkerRequest - 100, // 214: substrate.ateapi.v1.Control.CreateWorker:input_type -> substrate.ateapi.v1.CreateWorkerRequest - 101, // 215: substrate.ateapi.v1.Control.UpdateWorker:input_type -> substrate.ateapi.v1.UpdateWorkerRequest - 102, // 216: substrate.ateapi.v1.Control.DeleteWorker:input_type -> substrate.ateapi.v1.DeleteWorkerRequest - 103, // 217: substrate.ateapi.v1.Control.DrainWorker:input_type -> substrate.ateapi.v1.DrainWorkerRequest - 105, // 218: substrate.ateapi.v1.Control.MarkWorkerAvailable:input_type -> substrate.ateapi.v1.MarkWorkerAvailableRequest - 107, // 219: substrate.ateapi.v1.Control.MarkWorkerUnavailable:input_type -> substrate.ateapi.v1.MarkWorkerUnavailableRequest - 95, // 220: substrate.ateapi.v1.Control.ListWorkerActorAssignments:input_type -> substrate.ateapi.v1.ListWorkerActorAssignmentsRequest - 109, // 221: substrate.ateapi.v1.Control.ListActors:input_type -> substrate.ateapi.v1.ListActorsRequest - 60, // 222: substrate.ateapi.v1.Control.CreateAtespace:input_type -> substrate.ateapi.v1.CreateAtespaceRequest - 61, // 223: substrate.ateapi.v1.Control.GetAtespace:input_type -> substrate.ateapi.v1.GetAtespaceRequest - 62, // 224: substrate.ateapi.v1.Control.ListAtespaces:input_type -> substrate.ateapi.v1.ListAtespacesRequest - 64, // 225: substrate.ateapi.v1.Control.DeleteAtespace:input_type -> substrate.ateapi.v1.DeleteAtespaceRequest - 65, // 226: substrate.ateapi.v1.Control.CreateActorTemplate:input_type -> substrate.ateapi.v1.CreateActorTemplateRequest - 66, // 227: substrate.ateapi.v1.Control.GetActorTemplate:input_type -> substrate.ateapi.v1.GetActorTemplateRequest - 67, // 228: substrate.ateapi.v1.Control.ListActorTemplates:input_type -> substrate.ateapi.v1.ListActorTemplatesRequest - 69, // 229: substrate.ateapi.v1.Control.DeleteActorTemplate:input_type -> substrate.ateapi.v1.DeleteActorTemplateRequest - 141, // 230: substrate.ateapi.v1.Control.GetGlobalAccessPolicy:input_type -> substrate.ateapi.v1.GetGlobalAccessPolicyRequest - 142, // 231: substrate.ateapi.v1.Control.CreateGlobalAccessPolicy:input_type -> substrate.ateapi.v1.CreateGlobalAccessPolicyRequest - 143, // 232: substrate.ateapi.v1.Control.UpdateGlobalAccessPolicy:input_type -> substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest - 144, // 233: substrate.ateapi.v1.Control.GetAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.GetAtespaceAccessPolicyRequest - 145, // 234: substrate.ateapi.v1.Control.CreateAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest - 146, // 235: substrate.ateapi.v1.Control.UpdateAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest - 147, // 236: substrate.ateapi.v1.Control.DeleteAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest - 121, // 237: substrate.ateapi.v1.WorkerService.AcquireWorkerEpoch:input_type -> substrate.ateapi.v1.AcquireWorkerEpochRequest - 123, // 238: substrate.ateapi.v1.WorkerService.RegisterWorker:input_type -> substrate.ateapi.v1.RegisterWorkerRequest - 125, // 239: substrate.ateapi.v1.WorkerService.SetWorkerSandboxRuntimes:input_type -> substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest - 127, // 240: substrate.ateapi.v1.WorkerService.MintWorkerActorCertificate:input_type -> substrate.ateapi.v1.MintWorkerActorCertificateRequest - 129, // 241: substrate.ateapi.v1.WorkerService.MintWorkerActorJwt:input_type -> substrate.ateapi.v1.MintWorkerActorJwtRequest - 131, // 242: substrate.ateapi.v1.WorkerService.RequestActorSuspend:input_type -> substrate.ateapi.v1.RequestActorSuspendRequest - 135, // 243: substrate.ateapi.v1.WorkerService.ReportActorSnapshotState:input_type -> substrate.ateapi.v1.ReportActorSnapshotStateRequest - 133, // 244: substrate.ateapi.v1.WorkerService.ReportActorCrashed:input_type -> substrate.ateapi.v1.ReportActorCrashedRequest - 17, // 245: substrate.ateapi.v1.Control.GetActor:output_type -> substrate.ateapi.v1.Actor - 17, // 246: substrate.ateapi.v1.Control.CreateActor:output_type -> substrate.ateapi.v1.Actor - 17, // 247: substrate.ateapi.v1.Control.UpdateActor:output_type -> substrate.ateapi.v1.Actor - 74, // 248: substrate.ateapi.v1.Control.SuspendActor:output_type -> substrate.ateapi.v1.SuspendActorResponse - 77, // 249: substrate.ateapi.v1.Control.ResumeActor:output_type -> substrate.ateapi.v1.ResumeActorResponse - 79, // 250: substrate.ateapi.v1.Control.RevertActor:output_type -> substrate.ateapi.v1.RevertActorResponse - 17, // 251: substrate.ateapi.v1.Control.DeleteActor:output_type -> substrate.ateapi.v1.Actor - 18, // 252: substrate.ateapi.v1.Control.GetActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy - 18, // 253: substrate.ateapi.v1.Control.CreateActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy - 18, // 254: substrate.ateapi.v1.Control.UpdateActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy - 18, // 255: substrate.ateapi.v1.Control.DeleteActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy - 87, // 256: substrate.ateapi.v1.Control.MintActorJwt:output_type -> substrate.ateapi.v1.MintActorJwtResponse - 33, // 257: substrate.ateapi.v1.Control.CreateTag:output_type -> substrate.ateapi.v1.Tag - 33, // 258: substrate.ateapi.v1.Control.GetTag:output_type -> substrate.ateapi.v1.Tag - 89, // 259: substrate.ateapi.v1.Control.ListTags:output_type -> substrate.ateapi.v1.ListTagsResponse - 33, // 260: substrate.ateapi.v1.Control.UpdateTag:output_type -> substrate.ateapi.v1.Tag - 33, // 261: substrate.ateapi.v1.Control.DeleteTag:output_type -> substrate.ateapi.v1.Tag - 98, // 262: substrate.ateapi.v1.Control.ListWorkers:output_type -> substrate.ateapi.v1.ListWorkersResponse - 111, // 263: substrate.ateapi.v1.Control.GetWorker:output_type -> substrate.ateapi.v1.Worker - 111, // 264: substrate.ateapi.v1.Control.CreateWorker:output_type -> substrate.ateapi.v1.Worker - 111, // 265: substrate.ateapi.v1.Control.UpdateWorker:output_type -> substrate.ateapi.v1.Worker - 111, // 266: substrate.ateapi.v1.Control.DeleteWorker:output_type -> substrate.ateapi.v1.Worker - 104, // 267: substrate.ateapi.v1.Control.DrainWorker:output_type -> substrate.ateapi.v1.DrainWorkerResponse - 106, // 268: substrate.ateapi.v1.Control.MarkWorkerAvailable:output_type -> substrate.ateapi.v1.MarkWorkerAvailableResponse - 108, // 269: substrate.ateapi.v1.Control.MarkWorkerUnavailable:output_type -> substrate.ateapi.v1.MarkWorkerUnavailableResponse - 96, // 270: substrate.ateapi.v1.Control.ListWorkerActorAssignments:output_type -> substrate.ateapi.v1.ListWorkerActorAssignmentsResponse - 110, // 271: substrate.ateapi.v1.Control.ListActors:output_type -> substrate.ateapi.v1.ListActorsResponse - 34, // 272: substrate.ateapi.v1.Control.CreateAtespace:output_type -> substrate.ateapi.v1.Atespace - 34, // 273: substrate.ateapi.v1.Control.GetAtespace:output_type -> substrate.ateapi.v1.Atespace - 63, // 274: substrate.ateapi.v1.Control.ListAtespaces:output_type -> substrate.ateapi.v1.ListAtespacesResponse - 34, // 275: substrate.ateapi.v1.Control.DeleteAtespace:output_type -> substrate.ateapi.v1.Atespace - 36, // 276: substrate.ateapi.v1.Control.CreateActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate - 36, // 277: substrate.ateapi.v1.Control.GetActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate - 68, // 278: substrate.ateapi.v1.Control.ListActorTemplates:output_type -> substrate.ateapi.v1.ListActorTemplatesResponse - 36, // 279: substrate.ateapi.v1.Control.DeleteActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate - 139, // 280: substrate.ateapi.v1.Control.GetGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 139, // 281: substrate.ateapi.v1.Control.CreateGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 139, // 282: substrate.ateapi.v1.Control.UpdateGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 139, // 283: substrate.ateapi.v1.Control.GetAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 139, // 284: substrate.ateapi.v1.Control.CreateAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 139, // 285: substrate.ateapi.v1.Control.UpdateAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 139, // 286: substrate.ateapi.v1.Control.DeleteAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 122, // 287: substrate.ateapi.v1.WorkerService.AcquireWorkerEpoch:output_type -> substrate.ateapi.v1.AcquireWorkerEpochResponse - 124, // 288: substrate.ateapi.v1.WorkerService.RegisterWorker:output_type -> substrate.ateapi.v1.RegisterWorkerResponse - 126, // 289: substrate.ateapi.v1.WorkerService.SetWorkerSandboxRuntimes:output_type -> substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse - 128, // 290: substrate.ateapi.v1.WorkerService.MintWorkerActorCertificate:output_type -> substrate.ateapi.v1.MintWorkerActorCertificateResponse - 130, // 291: substrate.ateapi.v1.WorkerService.MintWorkerActorJwt:output_type -> substrate.ateapi.v1.MintWorkerActorJwtResponse - 132, // 292: substrate.ateapi.v1.WorkerService.RequestActorSuspend:output_type -> substrate.ateapi.v1.RequestActorSuspendResponse - 136, // 293: substrate.ateapi.v1.WorkerService.ReportActorSnapshotState:output_type -> substrate.ateapi.v1.ReportActorSnapshotStateResponse - 134, // 294: substrate.ateapi.v1.WorkerService.ReportActorCrashed:output_type -> substrate.ateapi.v1.ReportActorCrashedResponse - 245, // [245:295] is the sub-list for method output_type - 195, // [195:245] is the sub-list for method input_type - 193, // [193:195] is the sub-list for extension type_name - 191, // [191:193] is the sub-list for extension extendee - 0, // [0:191] is the sub-list for field type_name + 29, // 30: substrate.ateapi.v1.CredentialHeader.actor_jwt:type_name -> substrate.ateapi.v1.ActorJwtSource + 28, // 31: substrate.ateapi.v1.CredentialHeader.token_exchange:type_name -> substrate.ateapi.v1.TokenExchangeSource + 4, // 32: substrate.ateapi.v1.ActorStatus.state:type_name -> substrate.ateapi.v1.ActorState + 32, // 33: substrate.ateapi.v1.ActorStatus.worker_assignment:type_name -> substrate.ateapi.v1.WorkerAssignment + 15, // 34: substrate.ateapi.v1.ActorStatus.external_volumes:type_name -> substrate.ateapi.v1.ExternalVolumes + 31, // 35: substrate.ateapi.v1.ActorStatus.crash:type_name -> substrate.ateapi.v1.ActorCrash + 12, // 36: substrate.ateapi.v1.ActorStatus.snapshots:type_name -> substrate.ateapi.v1.Snapshot + 154, // 37: substrate.ateapi.v1.ActorCrash.crash_time:type_name -> google.protobuf.Timestamp + 36, // 38: substrate.ateapi.v1.WorkerAssignment.worker:type_name -> substrate.ateapi.v1.ObjectRef + 117, // 39: substrate.ateapi.v1.WorkerAssignment.sandbox_runtime:type_name -> substrate.ateapi.v1.ActorSandboxRuntime + 12, // 40: substrate.ateapi.v1.TagStatus.snapshot:type_name -> substrate.ateapi.v1.Snapshot + 14, // 41: substrate.ateapi.v1.Tag.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata + 33, // 42: substrate.ateapi.v1.Tag.status:type_name -> substrate.ateapi.v1.TagStatus + 3, // 43: substrate.ateapi.v1.Tag.scope:type_name -> substrate.ateapi.v1.TagScope + 36, // 44: substrate.ateapi.v1.Tag.source_actor:type_name -> substrate.ateapi.v1.ObjectRef + 14, // 45: substrate.ateapi.v1.Atespace.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata + 14, // 46: substrate.ateapi.v1.ActorTemplate.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata + 13, // 47: substrate.ateapi.v1.ActorTemplate.worker_selector:type_name -> substrate.ateapi.v1.Selector + 44, // 48: substrate.ateapi.v1.ActorTemplate.containers:type_name -> substrate.ateapi.v1.Container + 50, // 49: substrate.ateapi.v1.ActorTemplate.volumes:type_name -> substrate.ateapi.v1.Volume + 42, // 50: substrate.ateapi.v1.ActorTemplate.snapshot_config:type_name -> substrate.ateapi.v1.SnapshotConfig + 38, // 51: substrate.ateapi.v1.ActorTemplate.resources:type_name -> substrate.ateapi.v1.ResourceRequirements + 19, // 52: substrate.ateapi.v1.ActorTemplate.golden_egress_policy:type_name -> substrate.ateapi.v1.EgressPolicyTemplate + 41, // 53: substrate.ateapi.v1.ActorTemplate.status:type_name -> substrate.ateapi.v1.ActorTemplateStatus + 39, // 54: substrate.ateapi.v1.ResourceRequirements.limits:type_name -> substrate.ateapi.v1.Resources + 36, // 55: substrate.ateapi.v1.GoldenSnapshotStatus.golden_tag:type_name -> substrate.ateapi.v1.ObjectRef + 154, // 56: substrate.ateapi.v1.GoldenSnapshotStatus.snapshot_time:type_name -> google.protobuf.Timestamp + 40, // 57: substrate.ateapi.v1.ActorTemplateStatus.golden_snapshot_status:type_name -> substrate.ateapi.v1.GoldenSnapshotStatus + 2, // 58: substrate.ateapi.v1.SnapshotConfig.preferred_fidelity:type_name -> substrate.ateapi.v1.SnapshotFidelity + 43, // 59: substrate.ateapi.v1.SnapshotConfig.golden_snapshot_config:type_name -> substrate.ateapi.v1.GoldenSnapshotConfig + 5, // 60: substrate.ateapi.v1.GoldenSnapshotConfig.mode:type_name -> substrate.ateapi.v1.GoldenSnapshotMode + 47, // 61: substrate.ateapi.v1.Container.env:type_name -> substrate.ateapi.v1.EnvVar + 48, // 62: substrate.ateapi.v1.Container.wakeup_probe:type_name -> substrate.ateapi.v1.WakeupProbe + 60, // 63: substrate.ateapi.v1.Container.volume_mounts:type_name -> substrate.ateapi.v1.VolumeMount + 45, // 64: substrate.ateapi.v1.Container.security_context:type_name -> substrate.ateapi.v1.SecurityContext + 38, // 65: substrate.ateapi.v1.Container.resources:type_name -> substrate.ateapi.v1.ResourceRequirements + 46, // 66: substrate.ateapi.v1.SecurityContext.capabilities:type_name -> substrate.ateapi.v1.Capabilities + 49, // 67: substrate.ateapi.v1.WakeupProbe.http_get:type_name -> substrate.ateapi.v1.HttpGetAction + 53, // 68: substrate.ateapi.v1.Volume.durable_dir:type_name -> substrate.ateapi.v1.DurableDirVolumeSource + 51, // 69: substrate.ateapi.v1.Volume.external:type_name -> substrate.ateapi.v1.ExternalVolumeSource + 55, // 70: substrate.ateapi.v1.Volume.system_info:type_name -> substrate.ateapi.v1.SystemInfoVolumeSource + 52, // 71: substrate.ateapi.v1.Volume.image:type_name -> substrate.ateapi.v1.ImageVolumeSource + 54, // 72: substrate.ateapi.v1.ExternalVolumeSource.template:type_name -> substrate.ateapi.v1.ExternalVolumeTemplate + 56, // 73: substrate.ateapi.v1.SystemInfoVolumeSource.data_sources:type_name -> substrate.ateapi.v1.SystemInfoDataSource + 57, // 74: substrate.ateapi.v1.SystemInfoDataSource.actor_metadata:type_name -> substrate.ateapi.v1.ActorMetadataDataSource + 59, // 75: substrate.ateapi.v1.SystemInfoDataSource.trust_bundle:type_name -> substrate.ateapi.v1.TrustBundleDataSource + 58, // 76: substrate.ateapi.v1.ActorMetadataDataSource.items:type_name -> substrate.ateapi.v1.ActorMetadataItem + 6, // 77: substrate.ateapi.v1.ActorMetadataItem.field:type_name -> substrate.ateapi.v1.ActorMetadataField + 35, // 78: substrate.ateapi.v1.CreateAtespaceRequest.atespace:type_name -> substrate.ateapi.v1.Atespace + 36, // 79: substrate.ateapi.v1.GetAtespaceRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef + 95, // 80: substrate.ateapi.v1.ListAtespacesRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 35, // 81: substrate.ateapi.v1.ListAtespacesResponse.atespaces:type_name -> substrate.ateapi.v1.Atespace + 36, // 82: substrate.ateapi.v1.DeleteAtespaceRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef + 94, // 83: substrate.ateapi.v1.DeleteAtespaceRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 37, // 84: substrate.ateapi.v1.CreateActorTemplateRequest.actor_template:type_name -> substrate.ateapi.v1.ActorTemplate + 36, // 85: substrate.ateapi.v1.GetActorTemplateRequest.actor_template:type_name -> substrate.ateapi.v1.ObjectRef + 95, // 86: substrate.ateapi.v1.ListActorTemplatesRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 37, // 87: substrate.ateapi.v1.ListActorTemplatesResponse.actor_templates:type_name -> substrate.ateapi.v1.ActorTemplate + 36, // 88: substrate.ateapi.v1.DeleteActorTemplateRequest.actor_template:type_name -> substrate.ateapi.v1.ObjectRef + 94, // 89: substrate.ateapi.v1.DeleteActorTemplateRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 36, // 90: substrate.ateapi.v1.GetActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 17, // 91: substrate.ateapi.v1.CreateActorRequest.actor:type_name -> substrate.ateapi.v1.Actor + 17, // 92: substrate.ateapi.v1.UpdateActorRequest.actor:type_name -> substrate.ateapi.v1.Actor + 36, // 93: substrate.ateapi.v1.SuspendActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 76, // 94: substrate.ateapi.v1.SuspendActorRequest.options:type_name -> substrate.ateapi.v1.SuspendOptions + 17, // 95: substrate.ateapi.v1.SuspendActorResponse.actor:type_name -> substrate.ateapi.v1.Actor + 7, // 96: substrate.ateapi.v1.SuspendOptions.mode:type_name -> substrate.ateapi.v1.DurabilityMode + 36, // 97: substrate.ateapi.v1.ResumeActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 17, // 98: substrate.ateapi.v1.ResumeActorResponse.actor:type_name -> substrate.ateapi.v1.Actor + 36, // 99: substrate.ateapi.v1.RevertActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 17, // 100: substrate.ateapi.v1.RevertActorResponse.actor:type_name -> substrate.ateapi.v1.Actor + 36, // 101: substrate.ateapi.v1.DeleteActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 94, // 102: substrate.ateapi.v1.DeleteActorRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 36, // 103: substrate.ateapi.v1.GetActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 36, // 104: substrate.ateapi.v1.CreateActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 18, // 105: substrate.ateapi.v1.CreateActorEgressPolicyRequest.egress_policy:type_name -> substrate.ateapi.v1.EgressPolicy + 36, // 106: substrate.ateapi.v1.UpdateActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 18, // 107: substrate.ateapi.v1.UpdateActorEgressPolicyRequest.egress_policy:type_name -> substrate.ateapi.v1.EgressPolicy + 36, // 108: substrate.ateapi.v1.DeleteActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 94, // 109: substrate.ateapi.v1.DeleteActorEgressPolicyRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 36, // 110: substrate.ateapi.v1.GetTagRequest.tag:type_name -> substrate.ateapi.v1.ObjectRef + 36, // 111: substrate.ateapi.v1.MintActorJwtRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 154, // 112: substrate.ateapi.v1.MintActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp + 95, // 113: substrate.ateapi.v1.ListTagsRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 34, // 114: substrate.ateapi.v1.ListTagsResponse.tags:type_name -> substrate.ateapi.v1.Tag + 34, // 115: substrate.ateapi.v1.CreateTagRequest.tag:type_name -> substrate.ateapi.v1.Tag + 34, // 116: substrate.ateapi.v1.UpdateTagRequest.tag:type_name -> substrate.ateapi.v1.Tag + 36, // 117: substrate.ateapi.v1.DeleteTagRequest.tag:type_name -> substrate.ateapi.v1.ObjectRef + 94, // 118: substrate.ateapi.v1.DeleteTagRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 36, // 119: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 95, // 120: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 121, // 121: substrate.ateapi.v1.ListWorkerActorAssignmentsResponse.actor_assignments:type_name -> substrate.ateapi.v1.ActorAssignment + 95, // 122: substrate.ateapi.v1.ListWorkersRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 112, // 123: substrate.ateapi.v1.ListWorkersResponse.workers:type_name -> substrate.ateapi.v1.Worker + 36, // 124: substrate.ateapi.v1.GetWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 112, // 125: substrate.ateapi.v1.CreateWorkerRequest.worker:type_name -> substrate.ateapi.v1.Worker + 112, // 126: substrate.ateapi.v1.UpdateWorkerRequest.worker:type_name -> substrate.ateapi.v1.Worker + 36, // 127: substrate.ateapi.v1.DeleteWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 94, // 128: substrate.ateapi.v1.DeleteWorkerRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 36, // 129: substrate.ateapi.v1.DrainWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 112, // 130: substrate.ateapi.v1.DrainWorkerResponse.worker:type_name -> substrate.ateapi.v1.Worker + 36, // 131: substrate.ateapi.v1.MarkWorkerAvailableRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 112, // 132: substrate.ateapi.v1.MarkWorkerAvailableResponse.worker:type_name -> substrate.ateapi.v1.Worker + 36, // 133: substrate.ateapi.v1.MarkWorkerUnavailableRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 112, // 134: substrate.ateapi.v1.MarkWorkerUnavailableResponse.worker:type_name -> substrate.ateapi.v1.Worker + 95, // 135: substrate.ateapi.v1.ListActorsRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 17, // 136: substrate.ateapi.v1.ListActorsResponse.actors:type_name -> substrate.ateapi.v1.Actor + 14, // 137: substrate.ateapi.v1.Worker.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata + 153, // 138: substrate.ateapi.v1.Worker.labels:type_name -> substrate.ateapi.v1.Worker.LabelsEntry + 113, // 139: substrate.ateapi.v1.Worker.status:type_name -> substrate.ateapi.v1.WorkerStatus + 8, // 140: substrate.ateapi.v1.WorkerStatus.state:type_name -> substrate.ateapi.v1.WorkerState + 120, // 141: substrate.ateapi.v1.WorkerStatus.capacity:type_name -> substrate.ateapi.v1.WorkerResources + 120, // 142: substrate.ateapi.v1.WorkerStatus.allocated:type_name -> substrate.ateapi.v1.WorkerResources + 114, // 143: substrate.ateapi.v1.WorkerStatus.sandbox_classes:type_name -> substrate.ateapi.v1.WorkerSandboxClass + 115, // 144: substrate.ateapi.v1.WorkerSandboxClass.default_runtime:type_name -> substrate.ateapi.v1.WorkerSandboxRuntime + 115, // 145: substrate.ateapi.v1.WorkerSandboxClass.restorable_runtimes:type_name -> substrate.ateapi.v1.WorkerSandboxRuntime + 118, // 146: substrate.ateapi.v1.WorkerSandboxRuntime.version:type_name -> substrate.ateapi.v1.VersionedSandboxCompat + 116, // 147: substrate.ateapi.v1.WorkerSandboxRuntime.actor_overhead:type_name -> substrate.ateapi.v1.RuntimeOverhead + 118, // 148: substrate.ateapi.v1.ActorSandboxRuntime.version:type_name -> substrate.ateapi.v1.VersionedSandboxCompat + 119, // 149: substrate.ateapi.v1.VersionedSandboxCompat.attributes:type_name -> substrate.ateapi.v1.AttributeEntry + 14, // 150: substrate.ateapi.v1.ActorAssignment.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata + 36, // 151: substrate.ateapi.v1.ActorAssignment.actor:type_name -> substrate.ateapi.v1.ObjectRef + 39, // 152: substrate.ateapi.v1.ActorAssignment.resources:type_name -> substrate.ateapi.v1.Resources + 36, // 153: substrate.ateapi.v1.AcquireWorkerEpochRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 36, // 154: substrate.ateapi.v1.RegisterWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 120, // 155: substrate.ateapi.v1.RegisterWorkerRequest.capacity:type_name -> substrate.ateapi.v1.WorkerResources + 112, // 156: substrate.ateapi.v1.RegisterWorkerResponse.worker:type_name -> substrate.ateapi.v1.Worker + 36, // 157: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 114, // 158: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest.sandbox_classes:type_name -> substrate.ateapi.v1.WorkerSandboxClass + 112, // 159: substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse.worker:type_name -> substrate.ateapi.v1.Worker + 36, // 160: substrate.ateapi.v1.MintWorkerActorCertificateRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 36, // 161: substrate.ateapi.v1.MintWorkerActorCertificateRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 36, // 162: substrate.ateapi.v1.MintWorkerActorJwtRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 154, // 163: substrate.ateapi.v1.MintWorkerActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp + 36, // 164: substrate.ateapi.v1.RequestActorSuspendRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 36, // 165: substrate.ateapi.v1.RequestActorSuspendRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 17, // 166: substrate.ateapi.v1.RequestActorSuspendResponse.actor:type_name -> substrate.ateapi.v1.Actor + 36, // 167: substrate.ateapi.v1.ReportActorCrashedRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 36, // 168: substrate.ateapi.v1.ReportActorCrashedRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 17, // 169: substrate.ateapi.v1.ReportActorCrashedResponse.actor:type_name -> substrate.ateapi.v1.Actor + 36, // 170: substrate.ateapi.v1.ReportActorSnapshotStateRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 138, // 171: substrate.ateapi.v1.ReportActorSnapshotStateRequest.state:type_name -> substrate.ateapi.v1.ActorSnapshotState + 139, // 172: substrate.ateapi.v1.ActorSnapshotState.latest_local:type_name -> substrate.ateapi.v1.SnapshotReport + 139, // 173: substrate.ateapi.v1.ActorSnapshotState.uploading:type_name -> substrate.ateapi.v1.SnapshotReport + 139, // 174: substrate.ateapi.v1.ActorSnapshotState.latest_durable:type_name -> substrate.ateapi.v1.SnapshotReport + 154, // 175: substrate.ateapi.v1.ActorSnapshotState.upload_due_time:type_name -> google.protobuf.Timestamp + 2, // 176: substrate.ateapi.v1.SnapshotReport.fidelity:type_name -> substrate.ateapi.v1.SnapshotFidelity + 10, // 177: substrate.ateapi.v1.SnapshotReport.object:type_name -> substrate.ateapi.v1.ObjectSnapshot + 154, // 178: substrate.ateapi.v1.SnapshotReport.local_commit_time:type_name -> google.protobuf.Timestamp + 154, // 179: substrate.ateapi.v1.SnapshotReport.durable_complete_time:type_name -> google.protobuf.Timestamp + 14, // 180: substrate.ateapi.v1.AccessPolicy.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata + 141, // 181: substrate.ateapi.v1.AccessPolicy.bindings:type_name -> substrate.ateapi.v1.Binding + 140, // 182: substrate.ateapi.v1.CreateGlobalAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy + 140, // 183: substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy + 36, // 184: substrate.ateapi.v1.GetAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef + 36, // 185: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef + 140, // 186: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy + 36, // 187: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef + 140, // 188: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy + 36, // 189: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef + 94, // 190: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 9, // 191: substrate.ateapi.v1.ResourceAnnotation.scope:type_name -> substrate.ateapi.v1.Scope + 155, // 192: substrate.ateapi.v1.resource:extendee -> google.protobuf.MessageOptions + 156, // 193: substrate.ateapi.v1.method:extendee -> google.protobuf.MethodOptions + 149, // 194: substrate.ateapi.v1.resource:type_name -> substrate.ateapi.v1.ResourceAnnotation + 150, // 195: substrate.ateapi.v1.method:type_name -> substrate.ateapi.v1.MethodAnnotation + 71, // 196: substrate.ateapi.v1.Control.GetActor:input_type -> substrate.ateapi.v1.GetActorRequest + 72, // 197: substrate.ateapi.v1.Control.CreateActor:input_type -> substrate.ateapi.v1.CreateActorRequest + 73, // 198: substrate.ateapi.v1.Control.UpdateActor:input_type -> substrate.ateapi.v1.UpdateActorRequest + 74, // 199: substrate.ateapi.v1.Control.SuspendActor:input_type -> substrate.ateapi.v1.SuspendActorRequest + 77, // 200: substrate.ateapi.v1.Control.ResumeActor:input_type -> substrate.ateapi.v1.ResumeActorRequest + 79, // 201: substrate.ateapi.v1.Control.RevertActor:input_type -> substrate.ateapi.v1.RevertActorRequest + 81, // 202: substrate.ateapi.v1.Control.DeleteActor:input_type -> substrate.ateapi.v1.DeleteActorRequest + 82, // 203: substrate.ateapi.v1.Control.GetActorEgressPolicy:input_type -> substrate.ateapi.v1.GetActorEgressPolicyRequest + 83, // 204: substrate.ateapi.v1.Control.CreateActorEgressPolicy:input_type -> substrate.ateapi.v1.CreateActorEgressPolicyRequest + 84, // 205: substrate.ateapi.v1.Control.UpdateActorEgressPolicy:input_type -> substrate.ateapi.v1.UpdateActorEgressPolicyRequest + 85, // 206: substrate.ateapi.v1.Control.DeleteActorEgressPolicy:input_type -> substrate.ateapi.v1.DeleteActorEgressPolicyRequest + 87, // 207: substrate.ateapi.v1.Control.MintActorJwt:input_type -> substrate.ateapi.v1.MintActorJwtRequest + 91, // 208: substrate.ateapi.v1.Control.CreateTag:input_type -> substrate.ateapi.v1.CreateTagRequest + 86, // 209: substrate.ateapi.v1.Control.GetTag:input_type -> substrate.ateapi.v1.GetTagRequest + 89, // 210: substrate.ateapi.v1.Control.ListTags:input_type -> substrate.ateapi.v1.ListTagsRequest + 92, // 211: substrate.ateapi.v1.Control.UpdateTag:input_type -> substrate.ateapi.v1.UpdateTagRequest + 93, // 212: substrate.ateapi.v1.Control.DeleteTag:input_type -> substrate.ateapi.v1.DeleteTagRequest + 98, // 213: substrate.ateapi.v1.Control.ListWorkers:input_type -> substrate.ateapi.v1.ListWorkersRequest + 100, // 214: substrate.ateapi.v1.Control.GetWorker:input_type -> substrate.ateapi.v1.GetWorkerRequest + 101, // 215: substrate.ateapi.v1.Control.CreateWorker:input_type -> substrate.ateapi.v1.CreateWorkerRequest + 102, // 216: substrate.ateapi.v1.Control.UpdateWorker:input_type -> substrate.ateapi.v1.UpdateWorkerRequest + 103, // 217: substrate.ateapi.v1.Control.DeleteWorker:input_type -> substrate.ateapi.v1.DeleteWorkerRequest + 104, // 218: substrate.ateapi.v1.Control.DrainWorker:input_type -> substrate.ateapi.v1.DrainWorkerRequest + 106, // 219: substrate.ateapi.v1.Control.MarkWorkerAvailable:input_type -> substrate.ateapi.v1.MarkWorkerAvailableRequest + 108, // 220: substrate.ateapi.v1.Control.MarkWorkerUnavailable:input_type -> substrate.ateapi.v1.MarkWorkerUnavailableRequest + 96, // 221: substrate.ateapi.v1.Control.ListWorkerActorAssignments:input_type -> substrate.ateapi.v1.ListWorkerActorAssignmentsRequest + 110, // 222: substrate.ateapi.v1.Control.ListActors:input_type -> substrate.ateapi.v1.ListActorsRequest + 61, // 223: substrate.ateapi.v1.Control.CreateAtespace:input_type -> substrate.ateapi.v1.CreateAtespaceRequest + 62, // 224: substrate.ateapi.v1.Control.GetAtespace:input_type -> substrate.ateapi.v1.GetAtespaceRequest + 63, // 225: substrate.ateapi.v1.Control.ListAtespaces:input_type -> substrate.ateapi.v1.ListAtespacesRequest + 65, // 226: substrate.ateapi.v1.Control.DeleteAtespace:input_type -> substrate.ateapi.v1.DeleteAtespaceRequest + 66, // 227: substrate.ateapi.v1.Control.CreateActorTemplate:input_type -> substrate.ateapi.v1.CreateActorTemplateRequest + 67, // 228: substrate.ateapi.v1.Control.GetActorTemplate:input_type -> substrate.ateapi.v1.GetActorTemplateRequest + 68, // 229: substrate.ateapi.v1.Control.ListActorTemplates:input_type -> substrate.ateapi.v1.ListActorTemplatesRequest + 70, // 230: substrate.ateapi.v1.Control.DeleteActorTemplate:input_type -> substrate.ateapi.v1.DeleteActorTemplateRequest + 142, // 231: substrate.ateapi.v1.Control.GetGlobalAccessPolicy:input_type -> substrate.ateapi.v1.GetGlobalAccessPolicyRequest + 143, // 232: substrate.ateapi.v1.Control.CreateGlobalAccessPolicy:input_type -> substrate.ateapi.v1.CreateGlobalAccessPolicyRequest + 144, // 233: substrate.ateapi.v1.Control.UpdateGlobalAccessPolicy:input_type -> substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest + 145, // 234: substrate.ateapi.v1.Control.GetAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.GetAtespaceAccessPolicyRequest + 146, // 235: substrate.ateapi.v1.Control.CreateAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest + 147, // 236: substrate.ateapi.v1.Control.UpdateAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest + 148, // 237: substrate.ateapi.v1.Control.DeleteAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest + 122, // 238: substrate.ateapi.v1.WorkerService.AcquireWorkerEpoch:input_type -> substrate.ateapi.v1.AcquireWorkerEpochRequest + 124, // 239: substrate.ateapi.v1.WorkerService.RegisterWorker:input_type -> substrate.ateapi.v1.RegisterWorkerRequest + 126, // 240: substrate.ateapi.v1.WorkerService.SetWorkerSandboxRuntimes:input_type -> substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest + 128, // 241: substrate.ateapi.v1.WorkerService.MintWorkerActorCertificate:input_type -> substrate.ateapi.v1.MintWorkerActorCertificateRequest + 130, // 242: substrate.ateapi.v1.WorkerService.MintWorkerActorJwt:input_type -> substrate.ateapi.v1.MintWorkerActorJwtRequest + 132, // 243: substrate.ateapi.v1.WorkerService.RequestActorSuspend:input_type -> substrate.ateapi.v1.RequestActorSuspendRequest + 136, // 244: substrate.ateapi.v1.WorkerService.ReportActorSnapshotState:input_type -> substrate.ateapi.v1.ReportActorSnapshotStateRequest + 134, // 245: substrate.ateapi.v1.WorkerService.ReportActorCrashed:input_type -> substrate.ateapi.v1.ReportActorCrashedRequest + 17, // 246: substrate.ateapi.v1.Control.GetActor:output_type -> substrate.ateapi.v1.Actor + 17, // 247: substrate.ateapi.v1.Control.CreateActor:output_type -> substrate.ateapi.v1.Actor + 17, // 248: substrate.ateapi.v1.Control.UpdateActor:output_type -> substrate.ateapi.v1.Actor + 75, // 249: substrate.ateapi.v1.Control.SuspendActor:output_type -> substrate.ateapi.v1.SuspendActorResponse + 78, // 250: substrate.ateapi.v1.Control.ResumeActor:output_type -> substrate.ateapi.v1.ResumeActorResponse + 80, // 251: substrate.ateapi.v1.Control.RevertActor:output_type -> substrate.ateapi.v1.RevertActorResponse + 17, // 252: substrate.ateapi.v1.Control.DeleteActor:output_type -> substrate.ateapi.v1.Actor + 18, // 253: substrate.ateapi.v1.Control.GetActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy + 18, // 254: substrate.ateapi.v1.Control.CreateActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy + 18, // 255: substrate.ateapi.v1.Control.UpdateActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy + 18, // 256: substrate.ateapi.v1.Control.DeleteActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy + 88, // 257: substrate.ateapi.v1.Control.MintActorJwt:output_type -> substrate.ateapi.v1.MintActorJwtResponse + 34, // 258: substrate.ateapi.v1.Control.CreateTag:output_type -> substrate.ateapi.v1.Tag + 34, // 259: substrate.ateapi.v1.Control.GetTag:output_type -> substrate.ateapi.v1.Tag + 90, // 260: substrate.ateapi.v1.Control.ListTags:output_type -> substrate.ateapi.v1.ListTagsResponse + 34, // 261: substrate.ateapi.v1.Control.UpdateTag:output_type -> substrate.ateapi.v1.Tag + 34, // 262: substrate.ateapi.v1.Control.DeleteTag:output_type -> substrate.ateapi.v1.Tag + 99, // 263: substrate.ateapi.v1.Control.ListWorkers:output_type -> substrate.ateapi.v1.ListWorkersResponse + 112, // 264: substrate.ateapi.v1.Control.GetWorker:output_type -> substrate.ateapi.v1.Worker + 112, // 265: substrate.ateapi.v1.Control.CreateWorker:output_type -> substrate.ateapi.v1.Worker + 112, // 266: substrate.ateapi.v1.Control.UpdateWorker:output_type -> substrate.ateapi.v1.Worker + 112, // 267: substrate.ateapi.v1.Control.DeleteWorker:output_type -> substrate.ateapi.v1.Worker + 105, // 268: substrate.ateapi.v1.Control.DrainWorker:output_type -> substrate.ateapi.v1.DrainWorkerResponse + 107, // 269: substrate.ateapi.v1.Control.MarkWorkerAvailable:output_type -> substrate.ateapi.v1.MarkWorkerAvailableResponse + 109, // 270: substrate.ateapi.v1.Control.MarkWorkerUnavailable:output_type -> substrate.ateapi.v1.MarkWorkerUnavailableResponse + 97, // 271: substrate.ateapi.v1.Control.ListWorkerActorAssignments:output_type -> substrate.ateapi.v1.ListWorkerActorAssignmentsResponse + 111, // 272: substrate.ateapi.v1.Control.ListActors:output_type -> substrate.ateapi.v1.ListActorsResponse + 35, // 273: substrate.ateapi.v1.Control.CreateAtespace:output_type -> substrate.ateapi.v1.Atespace + 35, // 274: substrate.ateapi.v1.Control.GetAtespace:output_type -> substrate.ateapi.v1.Atespace + 64, // 275: substrate.ateapi.v1.Control.ListAtespaces:output_type -> substrate.ateapi.v1.ListAtespacesResponse + 35, // 276: substrate.ateapi.v1.Control.DeleteAtespace:output_type -> substrate.ateapi.v1.Atespace + 37, // 277: substrate.ateapi.v1.Control.CreateActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate + 37, // 278: substrate.ateapi.v1.Control.GetActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate + 69, // 279: substrate.ateapi.v1.Control.ListActorTemplates:output_type -> substrate.ateapi.v1.ListActorTemplatesResponse + 37, // 280: substrate.ateapi.v1.Control.DeleteActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate + 140, // 281: substrate.ateapi.v1.Control.GetGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 140, // 282: substrate.ateapi.v1.Control.CreateGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 140, // 283: substrate.ateapi.v1.Control.UpdateGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 140, // 284: substrate.ateapi.v1.Control.GetAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 140, // 285: substrate.ateapi.v1.Control.CreateAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 140, // 286: substrate.ateapi.v1.Control.UpdateAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 140, // 287: substrate.ateapi.v1.Control.DeleteAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 123, // 288: substrate.ateapi.v1.WorkerService.AcquireWorkerEpoch:output_type -> substrate.ateapi.v1.AcquireWorkerEpochResponse + 125, // 289: substrate.ateapi.v1.WorkerService.RegisterWorker:output_type -> substrate.ateapi.v1.RegisterWorkerResponse + 127, // 290: substrate.ateapi.v1.WorkerService.SetWorkerSandboxRuntimes:output_type -> substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse + 129, // 291: substrate.ateapi.v1.WorkerService.MintWorkerActorCertificate:output_type -> substrate.ateapi.v1.MintWorkerActorCertificateResponse + 131, // 292: substrate.ateapi.v1.WorkerService.MintWorkerActorJwt:output_type -> substrate.ateapi.v1.MintWorkerActorJwtResponse + 133, // 293: substrate.ateapi.v1.WorkerService.RequestActorSuspend:output_type -> substrate.ateapi.v1.RequestActorSuspendResponse + 137, // 294: substrate.ateapi.v1.WorkerService.ReportActorSnapshotState:output_type -> substrate.ateapi.v1.ReportActorSnapshotStateResponse + 135, // 295: substrate.ateapi.v1.WorkerService.ReportActorCrashed:output_type -> substrate.ateapi.v1.ReportActorCrashedResponse + 246, // [246:296] is the sub-list for method output_type + 196, // [196:246] is the sub-list for method input_type + 194, // [194:196] is the sub-list for extension type_name + 192, // [192:194] is the sub-list for extension extendee + 0, // [0:192] is the sub-list for field type_name } func init() { file_api_ateapi_v1_ateapi_proto_init() } @@ -11136,7 +11238,7 @@ func file_api_ateapi_v1_ateapi_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_ateapi_v1_ateapi_proto_rawDesc), len(file_api_ateapi_v1_ateapi_proto_rawDesc)), NumEnums: 10, - NumMessages: 143, + NumMessages: 144, NumExtensions: 2, NumServices: 2, }, diff --git a/api/ateapi/v1/ateapi.proto b/api/ateapi/v1/ateapi.proto index 51ed7d1e32..277644ea65 100644 --- a/api/ateapi/v1/ateapi.proto +++ b/api/ateapi/v1/ateapi.proto @@ -621,7 +621,7 @@ message EgressPolicy { // +k8s:optional // +k8s:maxItems=256 // +k8s:listType=atomic - // +k8s:customValidation # no two rules tie on a name and port + // +k8s:customValidation # no two rules tie on a name and port, and an https rule allows each token_url repeated EgressRule rules = 2; } @@ -636,7 +636,7 @@ message EgressPolicyTemplate { // +k8s:required // +k8s:maxItems=256 // +k8s:listType=atomic - // +k8s:customValidation # no two rules tie on a name and port + // +k8s:customValidation # no two rules tie on a name and port, and an https rule allows each token_url repeated EgressRule rules = 1; } @@ -839,7 +839,7 @@ message CredentialHeader { // Source-agnostic reference interpreted by a registered credential provider: // `ate-secret:////` // - // Exactly one of credential_uri and actor_jwt must be set. + // Exactly one of credential_uri, actor_jwt, and token_exchange must be set. // // +k8s:optional // +k8s:unionMember @@ -852,6 +852,43 @@ message CredentialHeader { // +k8s:optional // +k8s:unionMember ActorJwtSource actor_jwt = 4; + + // An access token obtained by exchanging an actor JWT at an OAuth 2.0 + // token exchange endpoint (RFC 8693). + // + // +k8s:optional + // +k8s:unionMember + TokenExchangeSource token_exchange = 5; +} + +// TokenExchangeSource has the gateway mint a JWT for the Actor that sent the +// request, bound to audience, and exchange it at token_url (RFC 8693). The +// returned access token becomes the header value. +message TokenExchangeSource { + // HTTPS URL of the token endpoint, without userinfo, query, or fragment. + // An https rule in the same policy must allow its host and port. + // + // +k8s:required + // +k8s:maxLength=2048 + // +k8s:customValidation # format + string token_url = 1; + + // Becomes the actor JWT's only aud and the request's audience parameter. + // + // +k8s:required + // +k8s:minLength=1 + // +k8s:maxLength=512 + string audience = 2; + + // The request's scope parameter, space-joined. + // + // +k8s:optional + // +k8s:maxItems=16 + // +k8s:listType=set + // +k8s:eachVal=+k8s:minLength=1 + // +k8s:eachVal=+k8s:maxLength=256 + // +k8s:customValidation # RFC 6749 scope-token characters + repeated string scopes = 3; } // ActorJwtSource has the gateway mint a JWT for the Actor that sent the diff --git a/cmd/ateapi/internal/apivalidation/actor_template_test.go b/cmd/ateapi/internal/apivalidation/actor_template_test.go index 0c32ef3440..ed19efc77e 100644 --- a/cmd/ateapi/internal/apivalidation/actor_template_test.go +++ b/cmd/ateapi/internal/apivalidation/actor_template_test.go @@ -68,6 +68,16 @@ func TestValidateCreateActorTemplateRequest(t *testing.T) { }} })}, field.ErrorList{field.Invalid(field.NewPath("actor_template", "golden_egress_policy", "rules").Index(1).Child("tls_passthrough", "hostnames").Index(0), nil, "")}, + }, { + "golden egress token exchange at an endpoint no rule allows", + &ateapi.CreateActorTemplateRequest{ActorTemplate: validActorTemplate(func(tmpl *ateapi.ActorTemplate) { + tmpl.GoldenEgressPolicy = &ateapi.EgressPolicyTemplate{Rules: []*ateapi.EgressRule{{Https: &ateapi.HttpsRule{ + Hostnames: []string{"api.example.com"}, + Ports: &ateapi.Ports{Numbers: []int32{443}}, + Effects: &ateapi.HttpRuleEffects{ReplaceHeaders: []*ateapi.CredentialHeader{tokenExchangeHeader("https://sts.example.com/token")}}, + }}}} + })}, + field.ErrorList{field.Invalid(field.NewPath("actor_template", "golden_egress_policy", "rules").Index(0).Child("https", "effects", "replace_headers").Index(0).Child("token_exchange", "token_url"), nil, "")}, }, { "too many golden egress rules", &ateapi.CreateActorTemplateRequest{ActorTemplate: validActorTemplate(func(tmpl *ateapi.ActorTemplate) { diff --git a/cmd/ateapi/internal/apivalidation/egress_policy.go b/cmd/ateapi/internal/apivalidation/egress_policy.go index 4e8c8883fb..31508f0b0c 100644 --- a/cmd/ateapi/internal/apivalidation/egress_policy.go +++ b/cmd/ateapi/internal/apivalidation/egress_policy.go @@ -92,7 +92,8 @@ func ValidateCustom_DeleteActorEgressPolicyRequest_Name(_ context.Context, _ ope // ValidateCustom_EgressPolicy_Rules rejects two rules that tie on a pattern // and a port, whatever their protocols: the gateway would have no way to pick // one. Defaults are applied before validation, so an http rule left on 80 -// and an https rule left on 443 never tie. +// and an https rule left on 443 never tie. It also rejects a token exchange +// whose token_url no https rule allows. func ValidateCustom_EgressPolicy_Rules(_ context.Context, _ operation.Operation, p *field.Path, rules, _ []*ateapi.EgressRule) field.ErrorList { type key struct { pattern string @@ -123,6 +124,33 @@ func ValidateCustom_EgressPolicy_Rules(_ context.Context, _ operation.Operation, } } } + return append(errs, validateTokenURLsAllowed(p, rules)...) +} + +func validateTokenURLsAllowed(p *field.Path, rules []*ateapi.EgressRule) field.ErrorList { + var errs field.ErrorList + var policy *egresspolicy.Policy + for i, rule := range rules { + member, effects := "http", rule.GetHttp().GetEffects() + if rule.GetHttps() != nil { + member, effects = "https", rule.GetHttps().GetEffects() + } + for j, inj := range effects.GetReplaceHeaders() { + raw := inj.GetTokenExchange().GetTokenUrl() + dest, ok := egresspolicy.TokenURLDestination(raw) + if !ok { + continue // reported by the token_url format check + } + if policy == nil { + // Invalid patterns are reported by the hostname checks. + policy, _ = egresspolicy.Compile(&ateapi.EgressPolicy{Rules: rules}) + } + if !policy.EvaluateRequest(dest, true).Allowed { + path := p.Index(i).Child(member, "effects", "replace_headers").Index(j).Child("token_exchange", "token_url") + errs = append(errs, field.Invalid(path, raw, "host and port must be allowed by an https rule in this policy")) + } + } + } return errs } @@ -254,6 +282,36 @@ func ValidateCustom_CredentialHeader_CredentialUri(_ context.Context, _ operatio return nil } +func ValidateCustom_TokenExchangeSource_TokenUrl(_ context.Context, _ operation.Operation, p *field.Path, raw, _ *string) field.ErrorList { + if _, ok := egresspolicy.TokenURLDestination(*raw); !ok { + return field.ErrorList{ + field.Invalid(p, *raw, "must be an https URL with a DNS hostname and no userinfo, query, or fragment"), + } + } + return nil +} + +func ValidateCustom_TokenExchangeSource_Scopes(_ context.Context, _ operation.Operation, p *field.Path, scopes, _ []string) field.ErrorList { + var errs field.ErrorList + for i, scope := range scopes { + if !validScopeToken(scope) { + errs = append(errs, field.Invalid(p.Index(i), scope, `must be printable ASCII without spaces, '"', or '\'`)) + } + } + return errs +} + +// validScopeToken reports whether scope holds only RFC 6749 scope-token +// characters, which exclude the space that joins scopes in a request. +func validScopeToken(scope string) bool { + for _, c := range []byte(scope) { + if c < 0x21 || c > 0x7e || c == '"' || c == '\\' { + return false + } + } + return true +} + func validCredentialURI(raw string) bool { u, err := url.Parse(raw) if err != nil || u.Scheme != "ate-secret" || u.Host == "" || u.Host != u.Hostname() || u.User != nil || u.RawQuery != "" || u.Fragment != "" || len(validation.IsDNS1123Subdomain(u.Host)) != 0 { diff --git a/cmd/ateapi/internal/apivalidation/egress_policy_test.go b/cmd/ateapi/internal/apivalidation/egress_policy_test.go index 07457b4cc2..e34cf17fbc 100644 --- a/cmd/ateapi/internal/apivalidation/egress_policy_test.go +++ b/cmd/ateapi/internal/apivalidation/egress_policy_test.go @@ -55,6 +55,16 @@ func actorJWTHeader(audiences ...string) *ateapi.CredentialHeader { } } +// tokenExchangeHeader is an Authorization header replaced with a token +// exchanged at tokenURL. +func tokenExchangeHeader(tokenURL string, scopes ...string) *ateapi.CredentialHeader { + return &ateapi.CredentialHeader{ + Header: "Authorization", + Prefix: "Bearer ", + TokenExchange: &ateapi.TokenExchangeSource{TokenUrl: tokenURL, Audience: "https://sts.example.com", Scopes: scopes}, + } +} + func TestValidateCreateActorEgressPolicyRequest(t *testing.T) { validReq := func() *ateapi.CreateActorEgressPolicyRequest { return &ateapi.CreateActorEgressPolicyRequest{ @@ -977,6 +987,143 @@ func TestValidateEgressPolicyRules(t *testing.T) { want: field.ErrorList{ field.Invalid(staticHeader.Child("actor_jwt", "expiration_seconds"), 3601, "").WithOrigin("maximum"), }, + }, { + name: "token exchange at an endpoint an https rule allows", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token", "read", "https://www.googleapis.com/auth/cloud-platform") + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + }, { + name: "token exchange at an endpoint a wildcard https rule allows", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token") + p.Rules = append(p.Rules, httpsRule(nil, "*.example.com")) + }, + }, { + name: "token exchange at an endpoint no rule allows", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token") + }, + want: field.ErrorList{ + field.Invalid(staticHeader.Child("token_exchange", "token_url"), nil, ""), + }, + }, { + name: "token exchange at an endpoint only an http rule allows", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token") + p.Rules = append(p.Rules, trivialHTTPRule("sts.example.com")) + }, + want: field.ErrorList{ + field.Invalid(staticHeader.Child("token_exchange", "token_url"), nil, ""), + }, + }, { + name: "token exchange on an https rule at an endpoint no rule allows", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0] = &ateapi.EgressRule{Https: &ateapi.HttpsRule{ + Hostnames: []string{"api.example.com"}, + Effects: &ateapi.HttpRuleEffects{ReplaceHeaders: []*ateapi.CredentialHeader{tokenExchangeHeader("https://sts.example.com/token")}}, + }} + }, + want: field.ErrorList{ + field.Invalid(rule.Child("https", "effects", "replace_headers").Index(0).Child("token_exchange", "token_url"), nil, ""), + }, + }, { + name: "token exchange at an endpoint port an https rule allows", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com:8443/token") + p.Rules = append(p.Rules, httpsRule(portNumbers(8443), "sts.example.com")) + }, + }, { + name: "token exchange at an endpoint port the https rule does not allow", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com:8443/token") + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.Invalid(staticHeader.Child("token_exchange", "token_url"), nil, ""), + }, + }, { + name: "token exchange over http", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("http://sts.example.com/token") + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.Invalid(staticHeader.Child("token_exchange", "token_url"), nil, ""), + }, + }, { + name: "token exchange with a long token URL", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/" + strings.Repeat("a", 2025)) + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.TooLong(staticHeader.Child("token_exchange", "token_url"), "", 2048).WithOrigin("maxLength"), + }, + }, { + name: "token exchange without a token URL", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("") + }, + want: field.ErrorList{ + field.Required(staticHeader.Child("token_exchange", "token_url"), ""), + }, + }, { + name: "token exchange without an audience", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token") + p.Rules[0].Http.Effects.ReplaceHeaders[0].TokenExchange.Audience = "" + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.Required(staticHeader.Child("token_exchange", "audience"), ""), + }, + }, { + name: "token exchange with a long audience", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token") + p.Rules[0].Http.Effects.ReplaceHeaders[0].TokenExchange.Audience = strings.Repeat("a", 513) + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.TooLong(staticHeader.Child("token_exchange", "audience"), "", 512).WithOrigin("maxLength"), + }, + }, { + name: "token exchange with a scope containing a space", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token", "read write") + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.Invalid(staticHeader.Child("token_exchange", "scopes").Index(0), nil, ""), + }, + }, { + name: "token exchange with an empty scope", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token", "") + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.TooShort(staticHeader.Child("token_exchange", "scopes").Index(0), "", 1).WithOrigin("minLength"), + }, + }, { + name: "token exchange with a repeated scope", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token", "read", "read") + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.Duplicate(staticHeader.Child("token_exchange", "scopes").Index(1), "read"), + }, + }, { + name: "credential URI and token exchange in one header", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0].TokenExchange = tokenExchangeHeader("https://sts.example.com/token").TokenExchange + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.Invalid(staticHeader, nil, "one of").WithOrigin("union"), + }, }, { name: "empty effects", mutate: func(p *ateapi.EgressPolicy) { @@ -1030,6 +1177,19 @@ func TestCredentialURIValidation(t *testing.T) { } } +func TestScopeTokenValidation(t *testing.T) { + for _, scope := range []string{"read", "https://www.googleapis.com/auth/cloud-platform", "!#[]~"} { + if !validScopeToken(scope) { + t.Errorf("validScopeToken(%q) = false", scope) + } + } + for _, scope := range []string{"a b", `a"b`, `a\b`, "a\tb", "a\x7fb", "é"} { + if validScopeToken(scope) { + t.Errorf("validScopeToken(%q) = true", scope) + } + } +} + func TestHeaderValueValidation(t *testing.T) { for _, value := range []string{"Bearer token", "value\tvalue", "\u0080\u0081"} { if !validHeaderValue(value) { diff --git a/cmd/ateapi/internal/apivalidation/zz_generated.validation.go b/cmd/ateapi/internal/apivalidation/zz_generated.validation.go index 4719d8d4f4..39be04401e 100644 --- a/cmd/ateapi/internal/apivalidation/zz_generated.validation.go +++ b/cmd/ateapi/internal/apivalidation/zz_generated.validation.go @@ -2574,7 +2574,7 @@ func Validate_CreateWorkerRequest( return errs } -var unionMembershipFor_go_ate_dev_substrate_api_ateapi_v1_CredentialHeader_ = validate.NewUnionMembership(validate.NewUnionMember("credential_uri"), validate.NewUnionMember("actor_jwt")) +var unionMembershipFor_go_ate_dev_substrate_api_ateapi_v1_CredentialHeader_ = validate.NewUnionMembership(validate.NewUnionMember("credential_uri"), validate.NewUnionMember("actor_jwt"), validate.NewUnionMember("token_exchange")) // Validate_CredentialHeader validates an instance of CredentialHeader according // to declarative validation rules in the API schema. @@ -2595,6 +2595,12 @@ func Validate_CredentialHeader( return false } return obj.ActorJwt != nil + }, + func(obj *v1.CredentialHeader) bool { + if obj == nil { + return false + } + return obj.TokenExchange != nil }); len(e) != 0 { errs = append(errs, e...) } @@ -2726,6 +2732,36 @@ func Validate_CredentialHeader( errs = append(errs, fn(fldPath.Child("actor_jwt"), obj.ActorJwt, oldVal, oldObj != nil)...) } + { // field v1.CredentialHeader.TokenExchange + fn := func( + fldPath *field.Path, + obj, oldObj *v1.TokenExchangeSource, + oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update { + if ateDeepEqual(obj, oldObj) { + return nil + } + } + // call field-attached validations + earlyReturn := false + if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_TokenExchangeSource(ctx, op, fldPath, obj, oldObj)...) + return + } + oldVal := safe.Field(oldObj, + func(oldObj *v1.CredentialHeader) *v1.TokenExchangeSource { + return oldObj.TokenExchange + }) + errs = append(errs, fn(fldPath.Child("token_exchange"), obj.TokenExchange, oldVal, oldObj != nil)...) + } + return errs } @@ -9157,6 +9193,138 @@ func Validate_TlsPassthroughRule( return errs } +// Validate_TokenExchangeSource validates an instance of TokenExchangeSource according +// to declarative validation rules in the API schema. +func Validate_TokenExchangeSource( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *v1.TokenExchangeSource) (errs field.ErrorList) { + + { // field v1.TokenExchangeSource.TokenUrl + fn := func( + fldPath *field.Path, + obj, oldObj *string, + oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update { + if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) { + return nil + } + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // custom validation + if e := ValidateCustom_TokenExchangeSource_TokenUrl(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.MaxLength(ctx, op, fldPath, obj, oldObj, 2048); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *v1.TokenExchangeSource) *string { + return &oldObj.TokenUrl + }) + errs = append(errs, fn(fldPath.Child("token_url"), &obj.TokenUrl, oldVal, oldObj != nil)...) + } + + { // field v1.TokenExchangeSource.Audience + fn := func( + fldPath *field.Path, + obj, oldObj *string, + oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update { + if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) { + return nil + } + } + // call field-attached validations + earlyReturn := false + if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + if e := validate.MaxLength(ctx, op, fldPath, obj, oldObj, 512); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.MinLength(ctx, op, fldPath, obj, oldObj, 1); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *v1.TokenExchangeSource) *string { + return &oldObj.Audience + }) + errs = append(errs, fn(fldPath.Child("audience"), &obj.Audience, oldVal, oldObj != nil)...) + } + + { // field v1.TokenExchangeSource.Scopes + fn := func( + fldPath *field.Path, + obj, oldObj []string, + oldValueCorrelated bool) (errs field.ErrorList) { + // don't revalidate unchanged data + if oldValueCorrelated && op.Type == operation.Update { + if ateDeepEqual(obj, oldObj) { + return nil + } + } + // call field-attached validations + earlyReturn := false + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 16).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // custom validation + if e := ValidateCustom_TokenExchangeSource_Scopes(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, nil, + func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { + return validate.MinLength(ctx, op, fldPath, obj, oldObj, 1) + }); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.EachValSliceVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, nil, + func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { + return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 256) + }); len(e) != 0 { + errs = append(errs, e...) + } + // lists with set semantics require unique values + if e := validate.ValSliceUnique(ctx, op, fldPath, obj, oldObj, validate.DirectEqual); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *v1.TokenExchangeSource) []string { + return oldObj.Scopes + }) + errs = append(errs, fn(fldPath.Child("scopes"), obj.Scopes, oldVal, oldObj != nil)...) + } + + return errs +} + // Validate_TrustBundleDataSource validates an instance of TrustBundleDataSource according // to declarative validation rules in the API schema. func Validate_TrustBundleDataSource( diff --git a/cmd/atenet/internal/router/egress/credentials.go b/cmd/atenet/internal/router/egress/credentials.go index c52a696600..8ff947864d 100644 --- a/cmd/atenet/internal/router/egress/credentials.go +++ b/cmd/atenet/internal/router/egress/credentials.go @@ -104,12 +104,17 @@ func (h *Handler) applyEffects(ctx context.Context, ref resources.ActorRef, dest var secret []byte var err error - if src := inj.GetActorJwt(); src != nil { - secret, err = h.actorJWT(ctx, ref, dest, src) + switch { + case inj.GetActorJwt() != nil: + secret, err = h.actorJWT(ctx, ref, dest, inj.GetActorJwt()) if err != nil { return nil, fmt.Errorf("minting an actor JWT for header %s: %w", inj.GetHeader(), err) } - } else { + case inj.GetTokenExchange() != nil: + slog.ErrorContext(ctx, "egress denied: token exchange is not supported by this gateway", + slog.Any("actor", ref), slog.String("host", dest.Hostname), slog.String("header", inj.GetHeader())) + return nil, extproc.NewReqError(envoy_type.StatusCode_InternalServerError, deniedBody) + default: secret, err = h.providerCredential(ctx, ref, dest, inj.GetCredentialUri()) if err != nil { return nil, fmt.Errorf("fetching credential %s for header %s: %w", inj.GetCredentialUri(), inj.GetHeader(), err) diff --git a/cmd/atenet/internal/router/egress/credentials_test.go b/cmd/atenet/internal/router/egress/credentials_test.go index fabe82c203..de00b47555 100644 --- a/cmd/atenet/internal/router/egress/credentials_test.go +++ b/cmd/atenet/internal/router/egress/credentials_test.go @@ -463,3 +463,24 @@ func TestActorJWTInjectionDenials(t *testing.T) { }) } } + +// A token exchange entry denies even with a provider configured, so its empty +// credential_uri is never fetched. +func TestTokenExchangeInjectionDenied(t *testing.T) { + for _, leg := range requestLegs { + t.Run(leg, func(t *testing.T) { + provider := &fakeProvider{resp: bearerTokenResponse("s3cr3t")} + h := injectionHandlerFor(replaceHeadersPolicy(leg, &ateapi.CredentialHeader{ + Header: "authorization", + Prefix: "Bearer ", + TokenExchange: &ateapi.TokenExchangeSource{TokenUrl: "https://sts.example.com/token", Audience: "https://api.example.com"}, + }), provider, "") + _, err := h.HandleRequestHeaders(context.Background(), + withAuthorization(innerMetadata(leg, "GET", "api.example.com", nil))) + wantStatus(t, err, envoy_type.StatusCode_InternalServerError) + if provider.got != nil { + t.Errorf("provider was asked for %v, want no fetch", provider.got) + } + }) + } +} diff --git a/internal/egresspolicy/egresspolicy.go b/internal/egresspolicy/egresspolicy.go index 23de4a0e9e..ff8381631e 100644 --- a/internal/egresspolicy/egresspolicy.go +++ b/internal/egresspolicy/egresspolicy.go @@ -27,6 +27,7 @@ import ( "fmt" "net" "net/netip" + "net/url" "slices" "strconv" "strings" @@ -407,6 +408,29 @@ func NormalizeHostname(raw string) (string, bool) { return name, true } +// TokenURLDestination is the hostname and port a token exchange's token_url +// names, the port defaulting to 443. The URL must be https with a DNS +// hostname and no userinfo, query, or fragment. +func TokenURLDestination(raw string) (Destination, bool) { + u, err := url.Parse(raw) + if err != nil || u.Scheme != "https" || u.User != nil || u.RawQuery != "" || u.Fragment != "" { + return Destination{}, false + } + name, ok := NormalizeHostname(u.Hostname()) + if !ok { + return Destination{}, false + } + port := defaultHTTPSPort + if p := u.Port(); p != "" { + n, err := strconv.ParseUint(p, 10, 16) + if err != nil || n == 0 { + return Destination{}, false + } + port = uint16(n) + } + return Destination{Hostname: name, Port: port}, true +} + // isHostname reports whether name is a lowercase DNS-1123 subdomain whose last // label is not all digits. RFC 1123 section 2.1 requires that, and it is what // keeps a dotted-decimal address from passing as a name, including spellings diff --git a/internal/egresspolicy/egresspolicy_test.go b/internal/egresspolicy/egresspolicy_test.go index cd27c02b56..369bc469fb 100644 --- a/internal/egresspolicy/egresspolicy_test.go +++ b/internal/egresspolicy/egresspolicy_test.go @@ -214,6 +214,36 @@ func TestNormalizeHostname(t *testing.T) { } } +func TestTokenURLDestination(t *testing.T) { + tests := []struct { + raw string + want Destination + wantOK bool + }{ + {raw: "https://sts.googleapis.com/v1/token", want: Destination{Hostname: "sts.googleapis.com", Port: 443}, wantOK: true}, + {raw: "https://STS.Example.com./token", want: Destination{Hostname: "sts.example.com", Port: 443}, wantOK: true}, + {raw: "https://sts.example.com:8443/token", want: Destination{Hostname: "sts.example.com", Port: 8443}, wantOK: true}, + {raw: "https://sts.example.com", want: Destination{Hostname: "sts.example.com", Port: 443}, wantOK: true}, + {raw: "http://sts.example.com/token"}, + {raw: "https://user:pass@sts.example.com/token"}, + {raw: "https://sts.example.com/token?audience=x"}, + {raw: "https://sts.example.com/token#frag"}, + {raw: "https://192.0.2.1/token"}, + {raw: "https://[2001:db8::1]/token"}, + {raw: "https://sts.example.com:0/token"}, + {raw: "https://sts.example.com:99999/token"}, + {raw: "https:///token"}, + {raw: "sts.example.com/token"}, + {raw: ""}, + } + for _, tc := range tests { + got, ok := TokenURLDestination(tc.raw) + if ok != tc.wantOK || got != tc.want { + t.Errorf("TokenURLDestination(%q) = (%+v, %v), want (%+v, %v)", tc.raw, got, ok, tc.want, tc.wantOK) + } + } +} + func TestCompileReportsAndDropsInvalidEntries(t *testing.T) { compiled, errs := Compile(policy( httpRule("good.example.com", "BAD.example.com"), From 53ef0018a9996783655c0208614c1f61eb908530 Mon Sep 17 00:00:00 2001 From: Max Thompson Date: Tue, 27 Oct 2026 11:36:50 -0700 Subject: [PATCH 02/10] atenet: add an RFC 8693 token exchange client to the egress gateway Egress policies will let the gateway exchange an actor JWT for an access token at an RFC 8693 endpoint. Add the client for that. It posts the token exchange form, caps the response at 1 MiB, follows no redirects, ignores proxy environment variables, and maps endpoint errors to gRPC codes that the gateway can turn into a 403, 500, or 503. Add --token-exchange-ca-file for endpoints with private certificates. The router builds the client at startup so a bad file fails startup. Nothing calls the client yet. Signed-off-by: Max Thompson --- cmd/atenet/internal/router/cmd.go | 1 + cmd/atenet/internal/router/config.go | 3 + .../router/egress/tokenexchange/client.go | 206 +++++++++++++ .../egress/tokenexchange/client_test.go | 290 ++++++++++++++++++ cmd/atenet/internal/router/router.go | 7 + 5 files changed, 507 insertions(+) create mode 100644 cmd/atenet/internal/router/egress/tokenexchange/client.go create mode 100644 cmd/atenet/internal/router/egress/tokenexchange/client_test.go diff --git a/cmd/atenet/internal/router/cmd.go b/cmd/atenet/internal/router/cmd.go index ac169ecc55..3a7b9a2408 100644 --- a/cmd/atenet/internal/router/cmd.go +++ b/cmd/atenet/internal/router/cmd.go @@ -92,6 +92,7 @@ func bindRouterFlags(fs *pflag.FlagSet, cfg *routerConfig) { fs.StringVar(&cfg.CredentialProvider.ClientCert, "credential-provider-client-cert", "", "Credential bundle presented to the credential provider as the client certificate; required unless --credential-provider-insecure is set") fs.StringArrayVar(&cfg.CredentialProvider.ServerNames, "credential-provider-server-name", nil, "SAN/SNI expected on a credential provider's serving certificate. Repeat once per --credential-provider-address, in the same order, or omit") fs.BoolVar(&cfg.CredentialProvider.Insecure, "credential-provider-insecure", false, "Dial the credential provider WITHOUT TLS. Development only: secrets cross the network in the clear. Without this, a missing --credential-provider-ca-file or --credential-provider-client-cert fails startup instead of silently downgrading") + fs.StringVar(&cfg.TokenExchangeCAFile, "token-exchange-ca-file", "", "PEM certificates the egress gateway trusts for token exchange endpoints, in addition to the system roots. A missing file or one with no certificates fails startup") // Envoy learns the collector over xDS rather than from its own environment, // so the router has to carry the address for it. Defaulting to diff --git a/cmd/atenet/internal/router/config.go b/cmd/atenet/internal/router/config.go index 73a32b8dfb..e1f667f219 100644 --- a/cmd/atenet/internal/router/config.go +++ b/cmd/atenet/internal/router/config.go @@ -184,6 +184,9 @@ type routerConfig struct { // CredentialProvider with no Addresses leaves the injector disabled, so a // request that needs an injection is denied. See egress.Handler. CredentialProvider credentialProviderConfig + // TokenExchangeCAFile holds PEM certificates the egress gateway trusts for + // token exchange endpoints, in addition to the system roots. + TokenExchangeCAFile string LogLevel string MetricsAddr string diff --git a/cmd/atenet/internal/router/egress/tokenexchange/client.go b/cmd/atenet/internal/router/egress/tokenexchange/client.go new file mode 100644 index 0000000000..266a5d2f6c --- /dev/null +++ b/cmd/atenet/internal/router/egress/tokenexchange/client.go @@ -0,0 +1,206 @@ +// Copyright 2026 The Agent Substrate Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package tokenexchange exchanges actor JWTs for access tokens at OAuth 2.0 +// token exchange endpoints (RFC 8693). +package tokenexchange + +import ( + "context" + "crypto/tls" + "crypto/x509" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "os" + "strings" + "time" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + grantTypeTokenExchange = "urn:ietf:params:oauth:grant-type:token-exchange" + tokenTypeJWT = "urn:ietf:params:oauth:token-type:jwt" + tokenTypeAccessToken = "urn:ietf:params:oauth:token-type:access_token" + + maxResponseBytes = 1 << 20 + // maxDetailBytes bounds the endpoint-supplied error fields an error carries. + maxDetailBytes = 200 +) + +// deniedErrors are the OAuth error codes that mean the endpoint refused this +// actor's exchange. +var deniedErrors = map[string]bool{ + "invalid_grant": true, + "invalid_target": true, + "unauthorized_client": true, + "invalid_scope": true, + "access_denied": true, +} + +// Token is an access token issued by a token endpoint. +type Token struct { + AccessToken string + // ExpiresAt is zero when the endpoint did not return expires_in. + ExpiresAt time.Time +} + +// Client sends token exchange requests. +type Client struct { + httpClient *http.Client +} + +// New returns a Client that trusts the system roots plus the PEM certificates +// in caFile, when set, which must hold at least one. The client ignores proxy +// environment variables and does not follow redirects. +func New(caFile string) (*Client, error) { + transport := http.DefaultTransport.(*http.Transport).Clone() + transport.Proxy = nil + if caFile != "" { + certs, err := os.ReadFile(caFile) + if err != nil { + return nil, fmt.Errorf("reading token exchange CA file: %w", err) + } + roots, err := x509.SystemCertPool() + if err != nil { + return nil, fmt.Errorf("loading system roots: %w", err) + } + if !roots.AppendCertsFromPEM(certs) { + return nil, fmt.Errorf("token exchange CA file %q contains no certificates", caFile) + } + transport.TLSClientConfig = &tls.Config{RootCAs: roots} + } + return &Client{httpClient: &http.Client{ + Transport: transport, + CheckRedirect: func(*http.Request, []*http.Request) error { + return http.ErrUseLastResponse + }, + }}, nil +} + +// Exchange trades subjectToken, a JWT, for an access token at tokenURL. ctx +// bounds the whole exchange. Errors carry a gRPC status code: +// PermissionDenied when the endpoint refuses the exchange, FailedPrecondition +// when the request or endpoint is misconfigured, and Unavailable or +// DeadlineExceeded when a retry may succeed. +func (c *Client) Exchange(ctx context.Context, tokenURL, subjectToken, audience string, scopes []string) (Token, error) { + form := url.Values{ + "grant_type": {grantTypeTokenExchange}, + "subject_token": {subjectToken}, + "subject_token_type": {tokenTypeJWT}, + "audience": {audience}, + "requested_token_type": {tokenTypeAccessToken}, + } + if len(scopes) > 0 { + form.Set("scope", strings.Join(scopes, " ")) + } + req, err := http.NewRequestWithContext(ctx, http.MethodPost, tokenURL, strings.NewReader(form.Encode())) + if err != nil { + return Token{}, status.Errorf(codes.FailedPrecondition, "building token exchange request: %v", err) + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + resp, err := c.httpClient.Do(req) + if err != nil { + code := codes.Unavailable + if errors.Is(err, context.DeadlineExceeded) { + code = codes.DeadlineExceeded + } + return Token{}, status.Errorf(code, "token exchange request: %v", err) + } + defer resp.Body.Close() + body, err := io.ReadAll(io.LimitReader(resp.Body, maxResponseBytes+1)) + if err != nil { + return Token{}, status.Errorf(codes.Unavailable, "reading token exchange response: %v", err) + } + if len(body) > maxResponseBytes { + return Token{}, status.Errorf(codes.Unavailable, "token exchange response exceeds %d bytes", maxResponseBytes) + } + + if resp.StatusCode == http.StatusOK { + return parseToken(body) + } + var oauthErr oauthError + // A body that is not an OAuth error response leaves both fields empty. + _ = json.Unmarshal(body, &oauthErr) + code := codes.FailedPrecondition + switch { + case resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode >= 500: + code = codes.Unavailable + case (resp.StatusCode == http.StatusBadRequest || resp.StatusCode == http.StatusUnauthorized) && deniedErrors[oauthErr.Error]: + code = codes.PermissionDenied + } + return Token{}, status.Errorf(code, "token endpoint returned %d%s", resp.StatusCode, oauthErr.detail()) +} + +// parseToken reads a successful token exchange response. +func parseToken(body []byte) (Token, error) { + var r struct { + AccessToken string `json:"access_token"` + IssuedTokenType string `json:"issued_token_type"` + ExpiresIn *int64 `json:"expires_in"` + } + if err := json.Unmarshal(body, &r); err != nil { + return Token{}, status.Errorf(codes.Unavailable, "parsing token exchange response: %v", err) + } + if r.IssuedTokenType != "" && r.IssuedTokenType != tokenTypeAccessToken { + return Token{}, status.Errorf(codes.Unavailable, "token endpoint issued token type %q, want %q", clip(r.IssuedTokenType), tokenTypeAccessToken) + } + if !isAccessToken(r.AccessToken) { + return Token{}, status.Error(codes.Unavailable, "token endpoint returned an empty access_token or one with characters outside VSCHAR") + } + tok := Token{AccessToken: r.AccessToken} + if r.ExpiresIn != nil { + tok.ExpiresAt = time.Now().Add(time.Duration(*r.ExpiresIn) * time.Second) + } + return tok, nil +} + +// isAccessToken reports whether s is 1*VSCHAR, the access-token syntax of +// RFC 6749 Appendix A.12. +func isAccessToken(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] < 0x20 || s[i] > 0x7e { + return false + } + } + return s != "" +} + +// oauthError is an OAuth 2.0 error response (RFC 6749 section 5.2). +type oauthError struct { + Error string `json:"error"` + Description string `json:"error_description"` +} + +// detail formats e for an error message, clipped and quoted, or "" when the +// response carried neither field. +func (e oauthError) detail() string { + if e.Error == "" && e.Description == "" { + return "" + } + return fmt.Sprintf(": error %q, error_description %q", clip(e.Error), clip(e.Description)) +} + +func clip(s string) string { + if len(s) > maxDetailBytes { + return s[:maxDetailBytes] + } + return s +} diff --git a/cmd/atenet/internal/router/egress/tokenexchange/client_test.go b/cmd/atenet/internal/router/egress/tokenexchange/client_test.go new file mode 100644 index 0000000000..8579a732bd --- /dev/null +++ b/cmd/atenet/internal/router/egress/tokenexchange/client_test.go @@ -0,0 +1,290 @@ +// Copyright 2026 The Agent Substrate Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tokenexchange + +import ( + "context" + "encoding/pem" + "fmt" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "strings" + "sync/atomic" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +const ( + subjectToken = "subject.jwt.token" + audience = "//iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/ate/providers/actors" +) + +// newTestServer starts a TLS server running handler at /token and returns it +// with a Client that trusts its certificate. +func newTestServer(t *testing.T, handler http.HandlerFunc) (*httptest.Server, *Client) { + t.Helper() + mux := http.NewServeMux() + mux.HandleFunc("/token", handler) + srv := httptest.NewTLSServer(mux) + t.Cleanup(srv.Close) + c, err := New(writeCAFile(t, srv)) + if err != nil { + t.Fatalf("New: %v", err) + } + return srv, c +} + +func writeCAFile(t *testing.T, srv *httptest.Server) string { + t.Helper() + path := filepath.Join(t.TempDir(), "ca.crt") + certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: srv.Certificate().Raw}) + if err := os.WriteFile(path, certPEM, 0o600); err != nil { + t.Fatal(err) + } + return path +} + +func respond(status int, body string) http.HandlerFunc { + return func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + fmt.Fprint(w, body) + } +} + +func TestExchangeSendsTokenExchangeForm(t *testing.T) { + for _, tt := range []struct { + name string + scopes []string + want url.Values + }{{ + name: "scopes are space-joined", + scopes: []string{"https://www.googleapis.com/auth/cloud-platform", "read"}, + want: url.Values{ + "grant_type": {"urn:ietf:params:oauth:grant-type:token-exchange"}, + "subject_token": {subjectToken}, + "subject_token_type": {"urn:ietf:params:oauth:token-type:jwt"}, + "audience": {audience}, + "scope": {"https://www.googleapis.com/auth/cloud-platform read"}, + "requested_token_type": {"urn:ietf:params:oauth:token-type:access_token"}, + }, + }, { + name: "no scope without scopes", + want: url.Values{ + "grant_type": {"urn:ietf:params:oauth:grant-type:token-exchange"}, + "subject_token": {subjectToken}, + "subject_token_type": {"urn:ietf:params:oauth:token-type:jwt"}, + "audience": {audience}, + "requested_token_type": {"urn:ietf:params:oauth:token-type:access_token"}, + }, + }} { + t.Run(tt.name, func(t *testing.T) { + var got url.Values + srv, c := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + t.Errorf("method = %s, want POST", r.Method) + } + if ct := r.Header.Get("Content-Type"); ct != "application/x-www-form-urlencoded" { + t.Errorf("Content-Type = %q, want application/x-www-form-urlencoded", ct) + } + if err := r.ParseForm(); err != nil { + t.Errorf("ParseForm: %v", err) + } + got = r.PostForm + respond(http.StatusOK, `{"access_token":"at","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":600}`)(w, r) + }) + if _, err := c.Exchange(t.Context(), srv.URL+"/token", subjectToken, audience, tt.scopes); err != nil { + t.Fatalf("Exchange: %v", err) + } + if diff := cmp.Diff(tt.want, got); diff != "" { + t.Errorf("form (-want +got):\n%s", diff) + } + }) + } +} + +func TestExchangeReturnsToken(t *testing.T) { + for _, tt := range []struct { + name string + body string + wantExpiresIn time.Duration + }{{ + name: "with expires_in", + body: `{"access_token":"ya29.token","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":600}`, + wantExpiresIn: 600 * time.Second, + }, { + name: "without expires_in or issued_token_type", + body: `{"access_token":"ya29.token","token_type":"Bearer"}`, + }} { + t.Run(tt.name, func(t *testing.T) { + srv, c := newTestServer(t, respond(http.StatusOK, tt.body)) + before := time.Now() + tok, err := c.Exchange(t.Context(), srv.URL+"/token", subjectToken, audience, nil) + after := time.Now() + if err != nil { + t.Fatalf("Exchange: %v", err) + } + if tok.AccessToken != "ya29.token" { + t.Errorf("AccessToken = %q, want ya29.token", tok.AccessToken) + } + if tt.wantExpiresIn == 0 { + if !tok.ExpiresAt.IsZero() { + t.Errorf("ExpiresAt = %v, want zero", tok.ExpiresAt) + } + return + } + if tok.ExpiresAt.Before(before.Add(tt.wantExpiresIn)) || tok.ExpiresAt.After(after.Add(tt.wantExpiresIn)) { + t.Errorf("ExpiresAt = %v, want %v after the exchange", tok.ExpiresAt, tt.wantExpiresIn) + } + }) + } +} + +func TestExchangeErrors(t *testing.T) { + oauthError := func(code string) string { + return fmt.Sprintf(`{"error":%q,"error_description":"details"}`, code) + } + for _, tt := range []struct { + name string + status int + body string + wantCode codes.Code + }{ + {"invalid_grant", http.StatusBadRequest, oauthError("invalid_grant"), codes.PermissionDenied}, + {"invalid_target", http.StatusBadRequest, oauthError("invalid_target"), codes.PermissionDenied}, + {"unauthorized_client", http.StatusUnauthorized, oauthError("unauthorized_client"), codes.PermissionDenied}, + {"invalid_scope", http.StatusBadRequest, oauthError("invalid_scope"), codes.PermissionDenied}, + {"access_denied", http.StatusBadRequest, oauthError("access_denied"), codes.PermissionDenied}, + {"invalid_request", http.StatusBadRequest, oauthError("invalid_request"), codes.FailedPrecondition}, + {"invalid_client", http.StatusUnauthorized, oauthError("invalid_client"), codes.FailedPrecondition}, + {"unsupported_grant_type", http.StatusBadRequest, oauthError("unsupported_grant_type"), codes.FailedPrecondition}, + {"400 without an error code", http.StatusBadRequest, "bad request", codes.FailedPrecondition}, + {"404", http.StatusNotFound, "not found", codes.FailedPrecondition}, + {"429", http.StatusTooManyRequests, "slow down", codes.Unavailable}, + {"500", http.StatusInternalServerError, "", codes.Unavailable}, + {"503", http.StatusServiceUnavailable, oauthError("temporarily_unavailable"), codes.Unavailable}, + {"200 with a body that is not JSON", http.StatusOK, "", codes.Unavailable}, + {"200 without access_token", http.StatusOK, `{"token_type":"Bearer","expires_in":600}`, codes.Unavailable}, + {"200 with another issued_token_type", http.StatusOK, `{"access_token":"at","issued_token_type":"urn:ietf:params:oauth:token-type:jwt"}`, codes.Unavailable}, + {"200 with a control character in access_token", http.StatusOK, `{"access_token":"at\r\nX-Injected: 1"}`, codes.Unavailable}, + {"200 with non-ASCII in access_token", http.StatusOK, `{"access_token":"atÿ"}`, codes.Unavailable}, + {"200 over 1 MiB", http.StatusOK, `{"access_token":"at"}` + strings.Repeat(" ", maxResponseBytes), codes.Unavailable}, + } { + t.Run(tt.name, func(t *testing.T) { + srv, c := newTestServer(t, respond(tt.status, tt.body)) + _, err := c.Exchange(t.Context(), srv.URL+"/token", subjectToken, audience, nil) + if got := status.Code(err); got != tt.wantCode { + t.Fatalf("Exchange error = %v (code %v), want code %v", err, got, tt.wantCode) + } + if strings.Contains(err.Error(), subjectToken) { + t.Errorf("error %q contains the subject token", err) + } + }) + } +} + +func TestExchangeDoesNotFollowRedirects(t *testing.T) { + var followed atomic.Bool + srv, c := newTestServer(t, func(w http.ResponseWriter, r *http.Request) { + if r.URL.Query().Has("redirected") { + followed.Store(true) + respond(http.StatusOK, `{"access_token":"at"}`)(w, r) + return + } + http.Redirect(w, r, "/token?redirected", http.StatusTemporaryRedirect) + }) + _, err := c.Exchange(t.Context(), srv.URL+"/token", subjectToken, audience, nil) + if got := status.Code(err); got != codes.FailedPrecondition { + t.Errorf("Exchange error = %v (code %v), want code %v", err, got, codes.FailedPrecondition) + } + if followed.Load() { + t.Error("the client followed the redirect") + } +} + +func TestExchangeClipsErrorDescription(t *testing.T) { + description := strings.Repeat("d", maxDetailBytes+50) + srv, c := newTestServer(t, respond(http.StatusBadRequest, fmt.Sprintf(`{"error":"invalid_request","error_description":%q}`, description))) + _, err := c.Exchange(t.Context(), srv.URL+"/token", subjectToken, audience, nil) + if err == nil { + t.Fatal("Exchange succeeded, want an error") + } + if !strings.Contains(err.Error(), `"`+description[:maxDetailBytes]+`"`) { + t.Errorf("error %q does not carry the description clipped to %d bytes", err, maxDetailBytes) + } +} + +func TestExchangeHonorsContextDeadline(t *testing.T) { + release := make(chan struct{}) + defer close(release) + srv, c := newTestServer(t, func(http.ResponseWriter, *http.Request) { + <-release + }) + ctx, cancel := context.WithTimeout(t.Context(), 50*time.Millisecond) + defer cancel() + _, err := c.Exchange(ctx, srv.URL+"/token", subjectToken, audience, nil) + if got := status.Code(err); got != codes.DeadlineExceeded { + t.Errorf("Exchange error = %v (code %v), want code %v", err, got, codes.DeadlineExceeded) + } +} + +func TestExchangeUnreachableEndpoint(t *testing.T) { + srv, c := newTestServer(t, respond(http.StatusOK, `{"access_token":"at"}`)) + srv.Close() + _, err := c.Exchange(t.Context(), srv.URL+"/token", subjectToken, audience, nil) + if got := status.Code(err); got != codes.Unavailable { + t.Errorf("Exchange error = %v (code %v), want code %v", err, got, codes.Unavailable) + } +} + +func TestNewTrustsCAFile(t *testing.T) { + srv, trusting := newTestServer(t, respond(http.StatusOK, `{"access_token":"at"}`)) + if _, err := trusting.Exchange(t.Context(), srv.URL+"/token", subjectToken, audience, nil); err != nil { + t.Errorf("Exchange with the server's CA: %v", err) + } + + systemOnly, err := New("") + if err != nil { + t.Fatalf("New: %v", err) + } + _, err = systemOnly.Exchange(t.Context(), srv.URL+"/token", subjectToken, audience, nil) + if got := status.Code(err); got != codes.Unavailable { + t.Errorf("Exchange without the server's CA: error = %v (code %v), want code %v", err, got, codes.Unavailable) + } +} + +func TestNewRejectsUnusableCAFile(t *testing.T) { + noCerts := filepath.Join(t.TempDir(), "empty.pem") + if err := os.WriteFile(noCerts, []byte("not a certificate\n"), 0o600); err != nil { + t.Fatal(err) + } + for name, path := range map[string]string{ + "missing file": filepath.Join(t.TempDir(), "missing.pem"), + "file without a cert": noCerts, + } { + t.Run(name, func(t *testing.T) { + if _, err := New(path); err == nil { + t.Errorf("New(%q) succeeded, want an error", path) + } + }) + } +} diff --git a/cmd/atenet/internal/router/router.go b/cmd/atenet/internal/router/router.go index c559ea4d49..0f717047f9 100644 --- a/cmd/atenet/internal/router/router.go +++ b/cmd/atenet/internal/router/router.go @@ -39,6 +39,7 @@ import ( "go.ate.dev/substrate/api/ateapi/v1" "go.ate.dev/substrate/api/credprovider/v1" "go.ate.dev/substrate/cmd/atenet/internal/router/egress" + "go.ate.dev/substrate/cmd/atenet/internal/router/egress/tokenexchange" "go.ate.dev/substrate/cmd/atenet/internal/router/extproc" "go.ate.dev/substrate/cmd/atenet/internal/router/ingress" "go.ate.dev/substrate/internal/ateapiauth" @@ -255,6 +256,12 @@ func (s *RouterServer) Run(ctx context.Context) error { slog.String("provider_name", providerName)) } + // TODO(#1661): pass the client to egress.New once the handler injects + // exchanged tokens. + if _, err := tokenexchange.New(s.cfg.TokenExchangeCAFile); err != nil { + return fmt.Errorf("loading --token-exchange-ca-file: %w", err) + } + egressHandler := egress.New(s.apiClient, loadActorIdentityRoots, s.cfg.EgressPolicyCacheTTL, providers) handlers[egressHandler.Direction()] = egressHandler } From 39d0a0e0bf93bdf24815e0535a57ba1b33c94177 Mon Sep 17 00:00:00 2001 From: Max Thompson Date: Tue, 27 Oct 2026 13:25:11 -0700 Subject: [PATCH 03/10] atenet: cache RFC 8693 token exchanges in a broker Add tokenexchange.Broker, which mints a 300-second actor JWT bound to the exchange's audience, exchanges it, and caches the access token per actor, token URL, audience, and scopes. A token is reused until a third of its lifetime or 30 seconds remains, whichever comes first, and for 60 seconds when the endpoint returns no expires_in. Concurrent requests for one token share a single exchange. The exchange outlives a caller that gives up, so a slow endpoint still fills the cache for the next request, and it has its own 10-second deadline so an endpoint that never answers cannot hold the exchange forever. Mint failures wrap ErrMint so the gateway can map them like other actor JWT mint failures. Signed-off-by: Max Thompson --- .../router/egress/tokenexchange/broker.go | 147 +++++++++ .../egress/tokenexchange/broker_test.go | 300 ++++++++++++++++++ 2 files changed, 447 insertions(+) create mode 100644 cmd/atenet/internal/router/egress/tokenexchange/broker.go create mode 100644 cmd/atenet/internal/router/egress/tokenexchange/broker_test.go diff --git a/cmd/atenet/internal/router/egress/tokenexchange/broker.go b/cmd/atenet/internal/router/egress/tokenexchange/broker.go new file mode 100644 index 0000000000..b821f6ceb7 --- /dev/null +++ b/cmd/atenet/internal/router/egress/tokenexchange/broker.go @@ -0,0 +1,147 @@ +// Copyright 2026 The Agent Substrate Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tokenexchange + +import ( + "context" + "errors" + "fmt" + "slices" + "strings" + "time" + + "golang.org/x/sync/singleflight" + "google.golang.org/grpc/status" + "k8s.io/apimachinery/pkg/util/cache" + + "go.ate.dev/substrate/api/ateapi/v1" + "go.ate.dev/substrate/internal/resources" +) + +const ( + // subjectTokenSeconds is the subject JWT's lifetime, the least + // MintActorJwt accepts. Only the exchange uses it. + subjectTokenSeconds = 300 + // exchangeTimeout bounds an exchange that has outlived its caller. Without + // it, an endpoint that never answers would hold the flight for its key + // forever. + exchangeTimeout = 10 * time.Second + // minReuseMargin is the least lifetime left at which a token stops being + // reused. + minReuseMargin = 30 * time.Second + // unknownExpiryReuse is how long a token without expires_in is reused. + unknownExpiryReuse = 60 * time.Second +) + +// ErrMint wraps a failure to mint the subject JWT, so callers can tell it from +// a failed exchange. +var ErrMint = errors.New("minting the subject token") + +// subjectMinter mints actor JWTs; *actorjwt.Minter implements it. +type subjectMinter interface { + Token(ctx context.Context, ref resources.ActorRef, src *ateapi.ActorJwtSource) (string, error) +} + +// Broker exchanges actor JWTs for access tokens and reuses each access token +// until a third of its lifetime, or 30 seconds, remains, whichever comes +// first. Tokens are cached by actor name, so an actor recreated under the +// same name can get its predecessor's token until then. +type Broker struct { + minter subjectMinter + client *Client + // tokens maps a tokenKey to an access token until it stops being reused. + tokens *cache.Expiring + // flight collapses concurrent exchanges of one token into a single call. + flight singleflight.Group +} + +// NewBroker returns a Broker that mints subject JWTs with minter and exchanges +// them with client. +func NewBroker(minter subjectMinter, client *Client) *Broker { + return &Broker{minter: minter, client: client, tokens: cache.NewExpiring()} +} + +// Token returns an access token for the actor ref names, exchanged as src +// describes. Errors carry a gRPC status code as Client.Exchange's do, and a +// failed mint also wraps ErrMint. +func (b *Broker) Token(ctx context.Context, ref resources.ActorRef, src *ateapi.TokenExchangeSource) (string, error) { + scopes := slices.Sorted(slices.Values(src.GetScopes())) + key := tokenKey(ref, src, scopes) + if token, ok := b.cached(key); ok { + return token, nil + } + + // The exchange outlives the caller, so a slow endpoint still fills the + // cache for the next request. + ch := b.flight.DoChan(key, func() (any, error) { + ctx, cancel := context.WithTimeout(context.WithoutCancel(ctx), exchangeTimeout) + defer cancel() + return b.exchange(ctx, key, ref, src, scopes) + }) + select { + case <-ctx.Done(): + return "", status.FromContextError(ctx.Err()).Err() + case res := <-ch: + if res.Err != nil { + return "", res.Err + } + return res.Val.(string), nil + } +} + +// exchange mints a subject JWT, exchanges it, and caches the access token +// under key, unless a flight that just finished has already stored one. +func (b *Broker) exchange(ctx context.Context, key string, ref resources.ActorRef, src *ateapi.TokenExchangeSource, scopes []string) (string, error) { + if token, ok := b.cached(key); ok { + return token, nil + } + subject, err := b.minter.Token(ctx, ref, &ateapi.ActorJwtSource{ + Audiences: []string{src.GetAudience()}, + ExpirationSeconds: subjectTokenSeconds, + }) + if err != nil { + return "", fmt.Errorf("%w: %w", ErrMint, err) + } + token, err := b.client.Exchange(ctx, src.GetTokenUrl(), subject, src.GetAudience(), scopes) + if err != nil { + return "", fmt.Errorf("exchanging a token for %s at %s: %w", ref, src.GetTokenUrl(), err) + } + if reuse := reuseFor(token); reuse > 0 { + b.tokens.Set(key, token.AccessToken, reuse) + } + return token.AccessToken, nil +} + +// reuseFor is how long token is served from the cache. +func reuseFor(token Token) time.Duration { + if token.ExpiresAt.IsZero() { + return unknownExpiryReuse + } + lifetime := time.Until(token.ExpiresAt) + return lifetime - max(lifetime/3, minReuseMargin) +} + +func (b *Broker) cached(key string) (string, bool) { + v, ok := b.tokens.Get(key) + s, _ := v.(string) + return s, ok +} + +// tokenKey identifies the token an exchange for ref returns: everything the +// exchange request carries except the subject JWT, which is itself cached per +// actor. It is a string because singleflight keys are strings. +func tokenKey(ref resources.ActorRef, src *ateapi.TokenExchangeSource, scopes []string) string { + return fmt.Sprintf("%q %q %q %q %q", ref.Atespace, ref.Name, src.GetTokenUrl(), src.GetAudience(), strings.Join(scopes, " ")) +} diff --git a/cmd/atenet/internal/router/egress/tokenexchange/broker_test.go b/cmd/atenet/internal/router/egress/tokenexchange/broker_test.go new file mode 100644 index 0000000000..e1bfaca931 --- /dev/null +++ b/cmd/atenet/internal/router/egress/tokenexchange/broker_test.go @@ -0,0 +1,300 @@ +// Copyright 2026 The Agent Substrate Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tokenexchange + +import ( + "context" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "sync" + "sync/atomic" + "testing" + "testing/synctest" + "time" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" + + "go.ate.dev/substrate/api/ateapi/v1" + "go.ate.dev/substrate/internal/resources" +) + +var testActor = resources.ActorRef{Atespace: "default", Name: "my-actor"} + +func testSource() *ateapi.TokenExchangeSource { + return &ateapi.TokenExchangeSource{ + TokenUrl: "https://sts.example.com/token", + Audience: "sts.example.com", + Scopes: []string{"write", "read"}, + } +} + +// fakeMinter's nth call returns "subject-", or err when set. +type fakeMinter struct { + calls atomic.Int32 + last atomic.Pointer[ateapi.ActorJwtSource] + err error +} + +func (m *fakeMinter) Token(_ context.Context, _ resources.ActorRef, src *ateapi.ActorJwtSource) (string, error) { + n := m.calls.Add(1) + m.last.Store(src) + if m.err != nil { + return "", m.err + } + return fmt.Sprintf("subject-%d", n), nil +} + +// fakeEndpoint answers the nth exchange with access token "at-" and +// expiresIn, a JSON member such as `,"expires_in":900` or "" for none. A +// status other than 0 or 200 answers with an OAuth error instead. gate, when +// non-nil, blocks each exchange until it is closed or its request ends. +type fakeEndpoint struct { + calls atomic.Int32 + lastForm atomic.Pointer[url.Values] + expiresIn string + status int + gate chan struct{} +} + +func (e *fakeEndpoint) RoundTrip(req *http.Request) (*http.Response, error) { + n := e.calls.Add(1) + body, err := io.ReadAll(req.Body) + if err != nil { + return nil, err + } + form, err := url.ParseQuery(string(body)) + if err != nil { + return nil, err + } + e.lastForm.Store(&form) + if e.gate != nil { + select { + case <-e.gate: + case <-req.Context().Done(): + return nil, req.Context().Err() + } + } + code, answer := http.StatusOK, fmt.Sprintf(`{"access_token":"at-%d"%s}`, n, e.expiresIn) + if e.status != 0 && e.status != http.StatusOK { + code, answer = e.status, `{"error":"temporarily_unavailable"}` + } + return &http.Response{StatusCode: code, Body: io.NopCloser(strings.NewReader(answer)), Header: http.Header{}, Request: req}, nil +} + +func newTestBroker(m *fakeMinter, e *fakeEndpoint) *Broker { + return NewBroker(m, &Client{httpClient: &http.Client{Transport: e}}) +} + +func mustToken(t *testing.T, b *Broker, src *ateapi.TokenExchangeSource) string { + t.Helper() + token, err := b.Token(t.Context(), testActor, src) + if err != nil { + t.Fatalf("Token: %v", err) + } + return token +} + +func TestTokenReuse(t *testing.T) { + for _, tt := range []struct { + name string + expiresIn string + reuse time.Duration + }{ + {"until a third of the lifetime remains", `,"expires_in":900`, 600 * time.Second}, + {"until 30 seconds remain", `,"expires_in":60`, 30 * time.Second}, + {"for 60 seconds without expires_in", "", 60 * time.Second}, + } { + t.Run(tt.name, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + b := newTestBroker(&fakeMinter{}, &fakeEndpoint{expiresIn: tt.expiresIn}) + if got := mustToken(t, b, testSource()); got != "at-1" { + t.Fatalf("first token = %q, want at-1", got) + } + time.Sleep(tt.reuse - time.Second) + if got := mustToken(t, b, testSource()); got != "at-1" { + t.Errorf("token %v later = %q, want the cached at-1", tt.reuse-time.Second, got) + } + time.Sleep(2 * time.Second) + if got := mustToken(t, b, testSource()); got != "at-2" { + t.Errorf("token %v later = %q, want a new at-2", tt.reuse+time.Second, got) + } + }) + }) + } +} + +func TestDoNotCacheShortLivedTokens(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + endpoint := &fakeEndpoint{expiresIn: `,"expires_in":30`} + b := newTestBroker(&fakeMinter{}, endpoint) + mustToken(t, b, testSource()) + if got := mustToken(t, b, testSource()); got != "at-2" { + t.Errorf("second token = %q, want a new at-2", got) + } + }) +} + +func TestExchangeSendsMintedSubjectToken(t *testing.T) { + minter, endpoint := &fakeMinter{}, &fakeEndpoint{} + mustToken(t, newTestBroker(minter, endpoint), testSource()) + + want := &ateapi.ActorJwtSource{Audiences: []string{"sts.example.com"}, ExpirationSeconds: 300} + if got := minter.last.Load(); !proto.Equal(got, want) { + t.Errorf("minted %v, want %v", got, want) + } + form := *endpoint.lastForm.Load() + for field, want := range map[string]string{ + "subject_token": "subject-1", + "audience": "sts.example.com", + "scope": "read write", + } { + if got := form.Get(field); got != want { + t.Errorf("form %s = %q, want %q", field, got, want) + } + } +} + +func TestCollapseConcurrentExchanges(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + endpoint := &fakeEndpoint{gate: make(chan struct{})} + b := newTestBroker(&fakeMinter{}, endpoint) + tokens := make([]string, 10) + var wg sync.WaitGroup + for i := range tokens { + wg.Go(func() { tokens[i] = mustToken(t, b, testSource()) }) + } + synctest.Wait() + close(endpoint.gate) + wg.Wait() + if n := endpoint.calls.Load(); n != 1 { + t.Errorf("%d exchanges, want 1", n) + } + for i, got := range tokens { + if got != "at-1" { + t.Errorf("caller %d got %q, want at-1", i, got) + } + } + }) +} + +// A caller that gives up gets DeadlineExceeded, and the exchange it started +// still fills the cache. +func TestExchangeOutlivesCaller(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + endpoint := &fakeEndpoint{gate: make(chan struct{})} + b := newTestBroker(&fakeMinter{}, endpoint) + ctx, cancel := context.WithTimeout(t.Context(), 4*time.Second) + defer cancel() + if _, err := b.Token(ctx, testActor, testSource()); status.Code(err) != codes.DeadlineExceeded { + t.Fatalf("Token error = %v, want code DeadlineExceeded", err) + } + close(endpoint.gate) + synctest.Wait() + if got := mustToken(t, b, testSource()); got != "at-1" { + t.Errorf("token = %q, want at-1 from the abandoned exchange", got) + } + if n := endpoint.calls.Load(); n != 1 { + t.Errorf("%d exchanges, want 1", n) + } + }) +} + +// An endpoint that never answers releases its key after exchangeTimeout, so a +// later request starts a new exchange instead of joining the stuck one. +func TestHungExchangeTimesOut(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + endpoint := &fakeEndpoint{gate: make(chan struct{})} + b := newTestBroker(&fakeMinter{}, endpoint) + ctx, cancel := context.WithTimeout(t.Context(), 4*time.Second) + defer cancel() + if _, err := b.Token(ctx, testActor, testSource()); err == nil { + t.Fatal("Token succeeded, want an error") + } + time.Sleep(exchangeTimeout) + close(endpoint.gate) + if got := mustToken(t, b, testSource()); got != "at-2" { + t.Errorf("token = %q, want at-2 from a new exchange", got) + } + }) +} + +func TestKeyOnActorAndEverythingSent(t *testing.T) { + for _, tt := range []struct { + name string + actor resources.ActorRef + mutate func(*ateapi.TokenExchangeSource) + wantSame bool + }{ + {name: "another actor", actor: resources.ActorRef{Atespace: "default", Name: "other"}}, + {name: "another token URL", mutate: func(s *ateapi.TokenExchangeSource) { s.TokenUrl = "https://sts.example.com/v2/token" }}, + {name: "another audience", mutate: func(s *ateapi.TokenExchangeSource) { s.Audience = "other.example.com" }}, + {name: "other scopes", mutate: func(s *ateapi.TokenExchangeSource) { s.Scopes = []string{"read"} }}, + {name: "reordered scopes", mutate: func(s *ateapi.TokenExchangeSource) { s.Scopes = []string{"read", "write"} }, wantSame: true}, + } { + t.Run(tt.name, func(t *testing.T) { + endpoint := &fakeEndpoint{expiresIn: `,"expires_in":900`} + b := newTestBroker(&fakeMinter{}, endpoint) + mustToken(t, b, testSource()) + actor, src := testActor, testSource() + if tt.actor != (resources.ActorRef{}) { + actor = tt.actor + } + if tt.mutate != nil { + tt.mutate(src) + } + if _, err := b.Token(t.Context(), actor, src); err != nil { + t.Fatalf("Token: %v", err) + } + want := int32(2) + if tt.wantSame { + want = 1 + } + if n := endpoint.calls.Load(); n != want { + t.Errorf("%d exchanges, want %d", n, want) + } + }) + } +} + +func TestMintErrorWrapsErrMint(t *testing.T) { + endpoint := &fakeEndpoint{} + b := newTestBroker(&fakeMinter{err: status.Error(codes.NotFound, "actor not found")}, endpoint) + _, err := b.Token(t.Context(), testActor, testSource()) + if !errors.Is(err, ErrMint) || status.Code(err) != codes.NotFound { + t.Errorf("Token error = %v, want ErrMint with code NotFound", err) + } + if n := endpoint.calls.Load(); n != 0 { + t.Errorf("%d exchanges after a failed mint, want 0", n) + } +} + +func TestDoNotCacheErrors(t *testing.T) { + endpoint := &fakeEndpoint{status: http.StatusServiceUnavailable} + b := newTestBroker(&fakeMinter{}, endpoint) + _, err := b.Token(t.Context(), testActor, testSource()) + if status.Code(err) != codes.Unavailable || errors.Is(err, ErrMint) { + t.Fatalf("Token error = %v, want code Unavailable without ErrMint", err) + } + endpoint.status = http.StatusOK + if got := mustToken(t, b, testSource()); got != "at-2" { + t.Errorf("token after a failed exchange = %q, want at-2", got) + } +} From 20bb01b7764e09b13296988056886689e3a29ded Mon Sep 17 00:00:00 2001 From: Max Thompson Date: Tue, 27 Oct 2026 14:19:01 -0700 Subject: [PATCH 04/10] egress policy: mark token exchange fields as not secret TestSecretLikeFieldsAreLabeled flags fields whose names look like credentials. token_exchange holds an endpoint, an audience, and scopes, and token_url can carry no userinfo or query, so neither holds a secret. The token URL check now skips headers that use another source, and the token_url doc names the DNS hostname rule. The http-rule test case now fails on the protocol alone, and the scopes limits have test cases. Signed-off-by: Max Thompson --- api/ateapi/v1/ateapi.pb.go | 5 ++-- api/ateapi/v1/ateapi.proto | 5 ++-- .../internal/apivalidation/egress_policy.go | 3 +++ .../apivalidation/egress_policy_test.go | 24 ++++++++++++++++++- internal/protoredact/protoredact_test.go | 2 ++ 5 files changed, 34 insertions(+), 5 deletions(-) diff --git a/api/ateapi/v1/ateapi.pb.go b/api/ateapi/v1/ateapi.pb.go index a343b410a4..6effe6891d 100644 --- a/api/ateapi/v1/ateapi.pb.go +++ b/api/ateapi/v1/ateapi.pb.go @@ -2052,8 +2052,9 @@ func (x *CredentialHeader) GetTokenExchange() *TokenExchangeSource { // returned access token becomes the header value. type TokenExchangeSource struct { state protoimpl.MessageState `protogen:"open.v1"` - // HTTPS URL of the token endpoint, without userinfo, query, or fragment. - // An https rule in the same policy must allow its host and port. + // HTTPS URL of the token endpoint, with a DNS hostname and without userinfo, + // query, or fragment. An https rule in the same policy must allow its host + // and port. // // +k8s:required // +k8s:maxLength=2048 diff --git a/api/ateapi/v1/ateapi.proto b/api/ateapi/v1/ateapi.proto index 277644ea65..ca6d7c24c2 100644 --- a/api/ateapi/v1/ateapi.proto +++ b/api/ateapi/v1/ateapi.proto @@ -865,8 +865,9 @@ message CredentialHeader { // request, bound to audience, and exchange it at token_url (RFC 8693). The // returned access token becomes the header value. message TokenExchangeSource { - // HTTPS URL of the token endpoint, without userinfo, query, or fragment. - // An https rule in the same policy must allow its host and port. + // HTTPS URL of the token endpoint, with a DNS hostname and without userinfo, + // query, or fragment. An https rule in the same policy must allow its host + // and port. // // +k8s:required // +k8s:maxLength=2048 diff --git a/cmd/ateapi/internal/apivalidation/egress_policy.go b/cmd/ateapi/internal/apivalidation/egress_policy.go index 31508f0b0c..71abd0f2e4 100644 --- a/cmd/ateapi/internal/apivalidation/egress_policy.go +++ b/cmd/ateapi/internal/apivalidation/egress_policy.go @@ -136,6 +136,9 @@ func validateTokenURLsAllowed(p *field.Path, rules []*ateapi.EgressRule) field.E member, effects = "https", rule.GetHttps().GetEffects() } for j, inj := range effects.GetReplaceHeaders() { + if inj.GetTokenExchange() == nil { + continue + } raw := inj.GetTokenExchange().GetTokenUrl() dest, ok := egresspolicy.TokenURLDestination(raw) if !ok { diff --git a/cmd/ateapi/internal/apivalidation/egress_policy_test.go b/cmd/ateapi/internal/apivalidation/egress_policy_test.go index e34cf17fbc..5cdfc6fd06 100644 --- a/cmd/ateapi/internal/apivalidation/egress_policy_test.go +++ b/cmd/ateapi/internal/apivalidation/egress_policy_test.go @@ -1011,7 +1011,7 @@ func TestValidateEgressPolicyRules(t *testing.T) { name: "token exchange at an endpoint only an http rule allows", mutate: func(p *ateapi.EgressPolicy) { p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token") - p.Rules = append(p.Rules, trivialHTTPRule("sts.example.com")) + p.Rules = append(p.Rules, &ateapi.EgressRule{Http: &ateapi.HttpRule{Hostnames: []string{"sts.example.com"}, Ports: portNumbers(443)}}) }, want: field.ErrorList{ field.Invalid(staticHeader.Child("token_exchange", "token_url"), nil, ""), @@ -1106,6 +1106,28 @@ func TestValidateEgressPolicyRules(t *testing.T) { want: field.ErrorList{ field.TooShort(staticHeader.Child("token_exchange", "scopes").Index(0), "", 1).WithOrigin("minLength"), }, + }, { + name: "token exchange with a long scope", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token", strings.Repeat("a", 257)) + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.TooLong(staticHeader.Child("token_exchange", "scopes").Index(0), "", 256).WithOrigin("maxLength"), + }, + }, { + name: "token exchange with too many scopes", + mutate: func(p *ateapi.EgressPolicy) { + var scopes []string + for i := range 17 { + scopes = append(scopes, fmt.Sprintf("scope-%d", i)) + } + p.Rules[0].Http.Effects.ReplaceHeaders[0] = tokenExchangeHeader("https://sts.example.com/token", scopes...) + p.Rules = append(p.Rules, httpsRule(nil, "sts.example.com")) + }, + want: field.ErrorList{ + field.TooMany(staticHeader.Child("token_exchange", "scopes"), 17, 16).WithOrigin("maxItems"), + }, }, { name: "token exchange with a repeated scope", mutate: func(p *ateapi.EgressPolicy) { diff --git a/internal/protoredact/protoredact_test.go b/internal/protoredact/protoredact_test.go index 3f61efe3c4..2e61557717 100644 --- a/internal/protoredact/protoredact_test.go +++ b/internal/protoredact/protoredact_test.go @@ -443,6 +443,8 @@ func TestSecretLikeFieldsAreLabeled(t *testing.T) { "next_page_token": "opaque list cursor", "substrate.ateapi.v1.CredentialHeader.credential_uri": "ate-secret:// reference to a credential, not the value", "substrate.ateapi.v1.CredentialHeader.actor_jwt": "ActorJwtSource: audiences and lifetime for minting, not a token", + "substrate.ateapi.v1.CredentialHeader.token_exchange": "TokenExchangeSource: endpoint, audience, and scopes for an exchange, not a token", + "substrate.ateapi.v1.TokenExchangeSource.token_url": "endpoint URL without userinfo or query, so it holds no credential", } forEachField(func(fd protoreflect.FieldDescriptor) { if !secretLikeName.MatchString(string(fd.Name())) || isDebugRedact(fd) { From 5a45bb10ac67666f034c0067e381972a450c934e Mon Sep 17 00:00:00 2001 From: Max Thompson Date: Tue, 27 Oct 2026 14:20:54 -0700 Subject: [PATCH 05/10] atenet: clip token exchange parse errors A malformed token response's JSON error can quote the endpoint's own text, such as an expires_in number a megabyte long. Clip it like the other endpoint-supplied error fields. Exchange's doc now names each error code by outcome. New tests cover DEL and a space in access_token and a truncated response body, and the deadline test uses an expired context instead of waiting. Signed-off-by: Max Thompson --- .../router/egress/tokenexchange/client.go | 12 +++--- .../egress/tokenexchange/client_test.go | 42 +++++++++++++++---- 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/cmd/atenet/internal/router/egress/tokenexchange/client.go b/cmd/atenet/internal/router/egress/tokenexchange/client.go index 266a5d2f6c..07e6610b2d 100644 --- a/cmd/atenet/internal/router/egress/tokenexchange/client.go +++ b/cmd/atenet/internal/router/egress/tokenexchange/client.go @@ -94,11 +94,11 @@ func New(caFile string) (*Client, error) { }}, nil } -// Exchange trades subjectToken, a JWT, for an access token at tokenURL. ctx -// bounds the whole exchange. Errors carry a gRPC status code: -// PermissionDenied when the endpoint refuses the exchange, FailedPrecondition -// when the request or endpoint is misconfigured, and Unavailable or -// DeadlineExceeded when a retry may succeed. +// Exchange trades subjectToken, a JWT, for an access token at tokenURL within +// ctx. Errors carry a gRPC code: PermissionDenied for an OAuth denial, +// FailedPrecondition for another 4xx or a 3xx, DeadlineExceeded when ctx +// expires, and Unavailable for 429, 5xx, a failed connection, or an unusable +// 200 response. func (c *Client) Exchange(ctx context.Context, tokenURL, subjectToken, audience string, scopes []string) (Token, error) { form := url.Values{ "grant_type": {grantTypeTokenExchange}, @@ -157,7 +157,7 @@ func parseToken(body []byte) (Token, error) { ExpiresIn *int64 `json:"expires_in"` } if err := json.Unmarshal(body, &r); err != nil { - return Token{}, status.Errorf(codes.Unavailable, "parsing token exchange response: %v", err) + return Token{}, status.Errorf(codes.Unavailable, "parsing token exchange response: %s", clip(err.Error())) } if r.IssuedTokenType != "" && r.IssuedTokenType != tokenTypeAccessToken { return Token{}, status.Errorf(codes.Unavailable, "token endpoint issued token type %q, want %q", clip(r.IssuedTokenType), tokenTypeAccessToken) diff --git a/cmd/atenet/internal/router/egress/tokenexchange/client_test.go b/cmd/atenet/internal/router/egress/tokenexchange/client_test.go index 8579a732bd..d9b11d2464 100644 --- a/cmd/atenet/internal/router/egress/tokenexchange/client_test.go +++ b/cmd/atenet/internal/router/egress/tokenexchange/client_test.go @@ -126,6 +126,7 @@ func TestExchangeReturnsToken(t *testing.T) { for _, tt := range []struct { name string body string + wantToken string // ya29.token unless set wantExpiresIn time.Duration }{{ name: "with expires_in", @@ -134,6 +135,10 @@ func TestExchangeReturnsToken(t *testing.T) { }, { name: "without expires_in or issued_token_type", body: `{"access_token":"ya29.token","token_type":"Bearer"}`, + }, { + name: "with a space in access_token", + body: `{"access_token":"ya29 token"}`, + wantToken: "ya29 token", }} { t.Run(tt.name, func(t *testing.T) { srv, c := newTestServer(t, respond(http.StatusOK, tt.body)) @@ -143,8 +148,12 @@ func TestExchangeReturnsToken(t *testing.T) { if err != nil { t.Fatalf("Exchange: %v", err) } - if tok.AccessToken != "ya29.token" { - t.Errorf("AccessToken = %q, want ya29.token", tok.AccessToken) + want := tt.wantToken + if want == "" { + want = "ya29.token" + } + if tok.AccessToken != want { + t.Errorf("AccessToken = %q, want %q", tok.AccessToken, want) } if tt.wantExpiresIn == 0 { if !tok.ExpiresAt.IsZero() { @@ -187,6 +196,7 @@ func TestExchangeErrors(t *testing.T) { {"200 with another issued_token_type", http.StatusOK, `{"access_token":"at","issued_token_type":"urn:ietf:params:oauth:token-type:jwt"}`, codes.Unavailable}, {"200 with a control character in access_token", http.StatusOK, `{"access_token":"at\r\nX-Injected: 1"}`, codes.Unavailable}, {"200 with non-ASCII in access_token", http.StatusOK, `{"access_token":"atÿ"}`, codes.Unavailable}, + {"200 with DEL in access_token", http.StatusOK, `{"access_token":"at\u007f"}`, codes.Unavailable}, {"200 over 1 MiB", http.StatusOK, `{"access_token":"at"}` + strings.Repeat(" ", maxResponseBytes), codes.Unavailable}, } { t.Run(tt.name, func(t *testing.T) { @@ -233,13 +243,29 @@ func TestExchangeClipsErrorDescription(t *testing.T) { } } -func TestExchangeHonorsContextDeadline(t *testing.T) { - release := make(chan struct{}) - defer close(release) - srv, c := newTestServer(t, func(http.ResponseWriter, *http.Request) { - <-release +func TestExchangeClipsParseError(t *testing.T) { + srv, c := newTestServer(t, respond(http.StatusOK, `{"access_token":"at","expires_in":`+strings.Repeat("9", 4096)+`}`)) + _, err := c.Exchange(t.Context(), srv.URL+"/token", subjectToken, audience, nil) + if status.Code(err) != codes.Unavailable || len(err.Error()) > 2*maxDetailBytes { + t.Errorf("Exchange error = %.300s... (%d bytes), want code Unavailable within %d bytes", err, len(err.Error()), 2*maxDetailBytes) + } +} + +func TestExchangeTruncatedResponse(t *testing.T) { + srv, c := newTestServer(t, func(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Length", "100") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{"access_token"`) }) - ctx, cancel := context.WithTimeout(t.Context(), 50*time.Millisecond) + _, err := c.Exchange(t.Context(), srv.URL+"/token", subjectToken, audience, nil) + if got := status.Code(err); got != codes.Unavailable { + t.Errorf("Exchange error = %v (code %v), want code %v", err, got, codes.Unavailable) + } +} + +func TestExchangeHonorsContextDeadline(t *testing.T) { + srv, c := newTestServer(t, respond(http.StatusOK, `{"access_token":"at"}`)) + ctx, cancel := context.WithDeadline(t.Context(), time.Now()) defer cancel() _, err := c.Exchange(ctx, srv.URL+"/token", subjectToken, audience, nil) if got := status.Code(err); got != codes.DeadlineExceeded { From 83cb7d6faf4c9cdcbdd25505b954e9caaef4ad74 Mon Sep 17 00:00:00 2001 From: Max Thompson Date: Tue, 27 Oct 2026 14:25:04 -0700 Subject: [PATCH 06/10] egress policy: allow at most 16 token exchanges per policy Each token_exchange entry costs a pass over every rule when ateapi checks that an https rule allows its token URL. A policy with thousands of entries could cost seconds of CPU. Reject more than 16, the same bound as one rule's replace_headers, before running the check. Signed-off-by: Max Thompson --- api/ateapi/v1/ateapi.pb.go | 3 +- api/ateapi/v1/ateapi.proto | 3 +- .../internal/apivalidation/egress_policy.go | 45 ++++++++++++------- .../apivalidation/egress_policy_test.go | 25 +++++++++++ 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/api/ateapi/v1/ateapi.pb.go b/api/ateapi/v1/ateapi.pb.go index 6effe6891d..d8ce773f68 100644 --- a/api/ateapi/v1/ateapi.pb.go +++ b/api/ateapi/v1/ateapi.pb.go @@ -1973,7 +1973,8 @@ type CredentialHeader struct { // +k8s:unionMember ActorJwt *ActorJwtSource `protobuf:"bytes,4,opt,name=actor_jwt,json=actorJwt,proto3" json:"actor_jwt,omitempty"` // An access token obtained by exchanging an actor JWT at an OAuth 2.0 - // token exchange endpoint (RFC 8693). + // token exchange endpoint (RFC 8693). A policy can have at most 16 + // token_exchange entries. // // +k8s:optional // +k8s:unionMember diff --git a/api/ateapi/v1/ateapi.proto b/api/ateapi/v1/ateapi.proto index ca6d7c24c2..d654c0c5f5 100644 --- a/api/ateapi/v1/ateapi.proto +++ b/api/ateapi/v1/ateapi.proto @@ -854,7 +854,8 @@ message CredentialHeader { ActorJwtSource actor_jwt = 4; // An access token obtained by exchanging an actor JWT at an OAuth 2.0 - // token exchange endpoint (RFC 8693). + // token exchange endpoint (RFC 8693). A policy can have at most 16 + // token_exchange entries. // // +k8s:optional // +k8s:unionMember diff --git a/cmd/ateapi/internal/apivalidation/egress_policy.go b/cmd/ateapi/internal/apivalidation/egress_policy.go index 71abd0f2e4..4960ad361d 100644 --- a/cmd/ateapi/internal/apivalidation/egress_policy.go +++ b/cmd/ateapi/internal/apivalidation/egress_policy.go @@ -127,33 +127,46 @@ func ValidateCustom_EgressPolicy_Rules(_ context.Context, _ operation.Operation, return append(errs, validateTokenURLsAllowed(p, rules)...) } +// maxTokenExchanges bounds the token_exchange entries in one policy, since +// each costs a pass over every rule here. +const maxTokenExchanges = 16 + func validateTokenURLsAllowed(p *field.Path, rules []*ateapi.EgressRule) field.ErrorList { - var errs field.ErrorList - var policy *egresspolicy.Policy + type tokenURL struct { + path *field.Path + raw string + } + var urls []tokenURL for i, rule := range rules { member, effects := "http", rule.GetHttp().GetEffects() if rule.GetHttps() != nil { member, effects = "https", rule.GetHttps().GetEffects() } for j, inj := range effects.GetReplaceHeaders() { - if inj.GetTokenExchange() == nil { - continue - } - raw := inj.GetTokenExchange().GetTokenUrl() - dest, ok := egresspolicy.TokenURLDestination(raw) - if !ok { - continue // reported by the token_url format check - } - if policy == nil { - // Invalid patterns are reported by the hostname checks. - policy, _ = egresspolicy.Compile(&ateapi.EgressPolicy{Rules: rules}) - } - if !policy.EvaluateRequest(dest, true).Allowed { + if src := inj.GetTokenExchange(); src != nil { path := p.Index(i).Child(member, "effects", "replace_headers").Index(j).Child("token_exchange", "token_url") - errs = append(errs, field.Invalid(path, raw, "host and port must be allowed by an https rule in this policy")) + urls = append(urls, tokenURL{path, src.GetTokenUrl()}) } } } + if len(urls) > maxTokenExchanges { + return field.ErrorList{field.Invalid(p, len(urls), fmt.Sprintf("at most %d replace_headers entries may set token_exchange", maxTokenExchanges))} + } + if len(urls) == 0 { + return nil + } + // Invalid patterns are reported by the hostname checks. + policy, _ := egresspolicy.Compile(&ateapi.EgressPolicy{Rules: rules}) + var errs field.ErrorList + for _, u := range urls { + dest, ok := egresspolicy.TokenURLDestination(u.raw) + if !ok { + continue // reported by the token_url format check + } + if !policy.EvaluateRequest(dest, true).Allowed { + errs = append(errs, field.Invalid(u.path, u.raw, "host and port must be allowed by an https rule in this policy")) + } + } return errs } diff --git a/cmd/ateapi/internal/apivalidation/egress_policy_test.go b/cmd/ateapi/internal/apivalidation/egress_policy_test.go index 5cdfc6fd06..f50de769e1 100644 --- a/cmd/ateapi/internal/apivalidation/egress_policy_test.go +++ b/cmd/ateapi/internal/apivalidation/egress_policy_test.go @@ -1137,6 +1137,31 @@ func TestValidateEgressPolicyRules(t *testing.T) { want: field.ErrorList{ field.Duplicate(staticHeader.Child("token_exchange", "scopes").Index(1), "read"), }, + }, { + name: "16 token exchanges", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules = []*ateapi.EgressRule{httpsRule(nil, "sts.example.com")} + for i := range 16 { + p.Rules = append(p.Rules, &ateapi.EgressRule{Https: &ateapi.HttpsRule{ + Hostnames: []string{fmt.Sprintf("api-%d.example.com", i)}, + Effects: &ateapi.HttpRuleEffects{ReplaceHeaders: []*ateapi.CredentialHeader{tokenExchangeHeader("https://sts.example.com/token")}}, + }}) + } + }, + }, { + name: "too many token exchanges", + mutate: func(p *ateapi.EgressPolicy) { + p.Rules = []*ateapi.EgressRule{httpsRule(nil, "sts.example.com")} + for i := range 17 { + p.Rules = append(p.Rules, &ateapi.EgressRule{Https: &ateapi.HttpsRule{ + Hostnames: []string{fmt.Sprintf("api-%d.example.com", i)}, + Effects: &ateapi.HttpRuleEffects{ReplaceHeaders: []*ateapi.CredentialHeader{tokenExchangeHeader("https://sts.example.com/token")}}, + }}) + } + }, + want: field.ErrorList{ + field.Invalid(rules, nil, ""), + }, }, { name: "credential URI and token exchange in one header", mutate: func(p *ateapi.EgressPolicy) { From 5e506f7770d5a25b237a6485dcda4b0fb0f52f5f Mon Sep 17 00:00:00 2001 From: Max Thompson Date: Tue, 27 Oct 2026 13:32:23 -0700 Subject: [PATCH 07/10] atenet: inject RFC 8693 exchanged tokens on egress A replace_headers entry with token_exchange now gets the access token the broker exchanges for the actor, where the gateway used to deny it with a 500. The gateway first checks that an https rule in the actor's policy allows the token URL's host and port, as ateapi does at admission, and denies with a 500 if none does. An endpoint that refuses the actor denies the request with a 403, a transient endpoint failure with a 503, and a misconfigured endpoint with a 500. A failed mint of the subject JWT maps the same way as for actor JWT injection. Signed-off-by: Max Thompson --- .../internal/router/egress/credentials.go | 53 ++++- .../router/egress/credentials_test.go | 204 ++++++++++++++++-- cmd/atenet/internal/router/egress/egress.go | 12 +- .../internal/router/egress/egress_test.go | 8 +- cmd/atenet/internal/router/egress/request.go | 2 +- .../internal/router/egress/request_test.go | 8 +- .../router/extproc/egress_websocket_test.go | 2 +- cmd/atenet/internal/router/router.go | 7 +- 8 files changed, 253 insertions(+), 43 deletions(-) diff --git a/cmd/atenet/internal/router/egress/credentials.go b/cmd/atenet/internal/router/egress/credentials.go index 8ff947864d..64c40bae8f 100644 --- a/cmd/atenet/internal/router/egress/credentials.go +++ b/cmd/atenet/internal/router/egress/credentials.go @@ -16,6 +16,7 @@ package egress import ( "context" + "errors" "fmt" "log/slog" "maps" @@ -30,6 +31,7 @@ import ( "go.ate.dev/substrate/api/ateapi/v1" "go.ate.dev/substrate/api/credprovider/v1" + "go.ate.dev/substrate/cmd/atenet/internal/router/egress/tokenexchange" "go.ate.dev/substrate/cmd/atenet/internal/router/extproc" "go.ate.dev/substrate/internal/egresspolicy" "go.ate.dev/substrate/internal/resources" @@ -72,6 +74,24 @@ func mapActorJWTError(err error) error { } } +// mapTokenExchangeError converts a token exchange failure into a client-facing +// ext_proc denial. A failed subject JWT mint maps as mapActorJWTError does. +// Otherwise an endpoint refusing the actor denies as 403, a transient failure +// as a retryable 503, and anything else as 500. +func mapTokenExchangeError(err error) error { + if errors.Is(err, tokenexchange.ErrMint) { + return mapActorJWTError(err) + } + switch status.Code(err) { + case codes.PermissionDenied: + return extproc.WrapReqError(envoy_type.StatusCode_Forbidden, err, deniedBody) + case codes.Unavailable, codes.DeadlineExceeded: + return extproc.WrapReqError(envoy_type.StatusCode_ServiceUnavailable, err, deniedBody) + default: + return extproc.WrapReqError(envoy_type.StatusCode_InternalServerError, err, deniedBody) + } +} + // applyEffects resolves a matched rule's credential injections and returns the // header mutations to add to the request, or an error that denies it. Only a // header the request carries is replaced (headers is keyed by lowercased @@ -79,9 +99,10 @@ func mapActorJWTError(err error) error { // fetched. // // Any failure to produce a credential the request needs denies it, including -// having no provider configured for a credential_uri entry. Actor JWTs need no -// provider. -func (h *Handler) applyEffects(ctx context.Context, ref resources.ActorRef, dest egresspolicy.Destination, headers map[string]string, effects *ateapi.HttpRuleEffects) ([]*corev3.HeaderValueOption, error) { +// having no provider configured for a credential_uri entry. Actor JWTs and +// token exchanges need no provider; policy is the actor's compiled policy, +// which must allow each token exchange's endpoint. +func (h *Handler) applyEffects(ctx context.Context, ref resources.ActorRef, dest egresspolicy.Destination, headers map[string]string, policy *egresspolicy.Policy, effects *ateapi.HttpRuleEffects) ([]*corev3.HeaderValueOption, error) { var injections []*ateapi.CredentialHeader for _, inj := range effects.GetReplaceHeaders() { if _, ok := headers[strings.ToLower(inj.GetHeader())]; ok { @@ -111,9 +132,10 @@ func (h *Handler) applyEffects(ctx context.Context, ref resources.ActorRef, dest return nil, fmt.Errorf("minting an actor JWT for header %s: %w", inj.GetHeader(), err) } case inj.GetTokenExchange() != nil: - slog.ErrorContext(ctx, "egress denied: token exchange is not supported by this gateway", - slog.Any("actor", ref), slog.String("host", dest.Hostname), slog.String("header", inj.GetHeader())) - return nil, extproc.NewReqError(envoy_type.StatusCode_InternalServerError, deniedBody) + secret, err = h.exchangedToken(ctx, ref, dest, policy, inj.GetTokenExchange()) + if err != nil { + return nil, fmt.Errorf("exchanging a token for header %s: %w", inj.GetHeader(), err) + } default: secret, err = h.providerCredential(ctx, ref, dest, inj.GetCredentialUri()) if err != nil { @@ -143,6 +165,25 @@ func (h *Handler) actorJWT(ctx context.Context, ref resources.ActorRef, dest egr return []byte(jwt), nil } +// exchangedToken exchanges an actor JWT for the access token src asks for. +// Every error it returns is already a client-facing denial. +func (h *Handler) exchangedToken(ctx context.Context, ref resources.ActorRef, dest egresspolicy.Destination, policy *egresspolicy.Policy, src *ateapi.TokenExchangeSource) ([]byte, error) { + endpoint, ok := egresspolicy.TokenURLDestination(src.GetTokenUrl()) + if !ok || !policy.EvaluateRequest(endpoint, true).Allowed { + slog.ErrorContext(ctx, "egress denied: no https rule allows the token exchange endpoint", + slog.Any("actor", ref), slog.String("host", dest.Hostname), slog.String("tokenURL", src.GetTokenUrl())) + return nil, extproc.NewReqError(envoy_type.StatusCode_InternalServerError, deniedBody) + } + token, err := h.tokens.Token(ctx, ref, src) + if err != nil { + slog.ErrorContext(ctx, "egress denied: token exchange failed", + slog.Any("actor", ref), slog.String("host", dest.Hostname), slog.String("tokenURL", src.GetTokenUrl()), + slog.String("audience", src.GetAudience()), slog.Any("err", err)) + return nil, mapTokenExchangeError(err) + } + return []byte(token), nil +} + // providerCredential fetches the secret uri names from the credential // provider. Every error it returns is already a client-facing denial. func (h *Handler) providerCredential(ctx context.Context, ref resources.ActorRef, dest egresspolicy.Destination, uri string) ([]byte, error) { diff --git a/cmd/atenet/internal/router/egress/credentials_test.go b/cmd/atenet/internal/router/egress/credentials_test.go index de00b47555..3cfbbf630a 100644 --- a/cmd/atenet/internal/router/egress/credentials_test.go +++ b/cmd/atenet/internal/router/egress/credentials_test.go @@ -17,8 +17,23 @@ package egress import ( "cmp" "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "encoding/pem" "errors" + "io" "maps" + "math/big" + "net/http" + "net/http/httptest" + "net/url" + "os" + "path/filepath" + "strconv" + "sync/atomic" "testing" "testing/synctest" "time" @@ -32,6 +47,7 @@ import ( "go.ate.dev/substrate/api/ateapi/v1" "go.ate.dev/substrate/api/credprovider/v1" + "go.ate.dev/substrate/cmd/atenet/internal/router/egress/tokenexchange" "go.ate.dev/substrate/cmd/atenet/internal/router/extproc" ) @@ -62,7 +78,7 @@ func injectionHandlerFor(policy *ateapi.EgressPolicy, provider credprovider.Cred if provider != nil { providers = map[string]credprovider.CredentialProviderClient{providerName: provider} } - return New(&egressMockClient{actor: runningActor(), policy: policy}, nil, 0, providers) + return New(&egressMockClient{actor: runningActor(), policy: policy}, nil, 0, providers, nil) } // withAuthorization adds the placeholder authorization header the sample @@ -129,7 +145,7 @@ func TestInjectionPicksProviderByName(t *testing.T) { k8s := &fakeProvider{resp: bearerTokenResponse("from-k8s")} vault := &fakeProvider{resp: bearerTokenResponse("from-vault")} h := New(&egressMockClient{actor: runningActor(), policy: policy}, nil, 0, - map[string]credprovider.CredentialProviderClient{"k8s": k8s, "vault": vault}) + map[string]credprovider.CredentialProviderClient{"k8s": k8s, "vault": vault}, nil) md := withAuthorization(innerMetadata(extproc.EgressTLSMITMFilterChainName, "GET", "api.example.com", nil)) md.Headers["x-api-key"] = "placeholder" @@ -161,7 +177,7 @@ func TestInjectionCatchAllProvider(t *testing.T) { catchAll := &fakeProvider{resp: bearerTokenResponse("s3cr3t")} h := New(&egressMockClient{actor: runningActor(), policy: policy}, nil, 0, - map[string]credprovider.CredentialProviderClient{"": catchAll}) + map[string]credprovider.CredentialProviderClient{"": catchAll}, nil) if _, err := h.HandleRequestHeaders(context.Background(), md()); err != nil { t.Fatalf("HandleRequestHeaders with a lone \"\" provider: %v", err) } @@ -172,7 +188,7 @@ func TestInjectionCatchAllProvider(t *testing.T) { catchAll = &fakeProvider{resp: bearerTokenResponse("s3cr3t")} k8s := &fakeProvider{resp: bearerTokenResponse("s3cr3t")} h = New(&egressMockClient{actor: runningActor(), policy: policy}, nil, 0, - map[string]credprovider.CredentialProviderClient{"": catchAll, injectionProviderName: k8s}) + map[string]credprovider.CredentialProviderClient{"": catchAll, injectionProviderName: k8s}, nil) if _, err := h.HandleRequestHeaders(context.Background(), md()); err != nil { t.Fatalf("HandleRequestHeaders with a \"\" provider next to another: %v", err) } @@ -380,7 +396,7 @@ func TestActorJWTInjection(t *testing.T) { actorJWTHeader("authorization"), &ateapi.CredentialHeader{Header: "x-api-key", CredentialUri: "ate-secret://k8s/default/token"}, )} - h := New(client, nil, 0, nil) + h := New(client, nil, 0, nil, nil) res, err := h.HandleRequestHeaders(context.Background(), withAuthorization(innerMetadata(leg, "GET", "api.example.com", nil))) @@ -415,7 +431,7 @@ func TestActorJWTInjection(t *testing.T) { func TestActorJWTInjectionSkippedWithoutHeader(t *testing.T) { client := &egressMockClient{actor: runningActor(), policy: replaceHeadersPolicy(extproc.EgressTLSMITMFilterChainName, actorJWTHeader("authorization"))} - h := New(client, nil, 0, nil) + h := New(client, nil, 0, nil, nil) res, err := h.HandleRequestHeaders(context.Background(), innerMetadata(extproc.EgressTLSMITMFilterChainName, "GET", "api.example.com", nil)) if err != nil { @@ -453,7 +469,7 @@ func TestActorJWTInjectionDenials(t *testing.T) { policy: replaceHeadersPolicy(leg, actorJWTHeader(header)), mintErr: tc.mintErr, } - h := New(mock, nil, 0, nil) + h := New(mock, nil, 0, nil, nil) md := innerMetadata(leg, "GET", "api.example.com", nil) md.Headers[header] = "placeholder" _, err := h.HandleRequestHeaders(context.Background(), md) @@ -464,22 +480,170 @@ func TestActorJWTInjectionDenials(t *testing.T) { } } -// A token exchange entry denies even with a provider configured, so its empty -// credential_uri is never fetched. -func TestTokenExchangeInjectionDenied(t *testing.T) { +// tokenEndpoint starts a TLS token endpoint at localhost that answers every +// exchange with status and body. It returns the endpoint's token URL, a client +// that trusts it, and the number of exchanges it has served. +func tokenEndpoint(t *testing.T, status int, body string) (string, *tokenexchange.Client, *atomic.Int32) { + t.Helper() + var calls atomic.Int32 + srv := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + calls.Add(1) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + _, _ = io.WriteString(w, body) + })) + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatal(err) + } + template := &x509.Certificate{ + SerialNumber: big.NewInt(1), + DNSNames: []string{"localhost"}, + NotBefore: time.Now().Add(-time.Hour), + NotAfter: time.Now().Add(time.Hour), + IsCA: true, + BasicConstraintsValid: true, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + } + der, err := x509.CreateCertificate(rand.Reader, template, template, &key.PublicKey, key) + if err != nil { + t.Fatal(err) + } + srv.TLS = &tls.Config{Certificates: []tls.Certificate{{Certificate: [][]byte{der}, PrivateKey: key}}} + srv.StartTLS() + t.Cleanup(srv.Close) + + caFile := filepath.Join(t.TempDir(), "ca.pem") + if err := os.WriteFile(caFile, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}), 0o600); err != nil { + t.Fatal(err) + } + client, err := tokenexchange.New(caFile) + if err != nil { + t.Fatal(err) + } + u, err := url.Parse(srv.URL) + if err != nil { + t.Fatal(err) + } + return "https://localhost:" + u.Port() + "/token", client, &calls +} + +func tokenExchangeHeader(header, tokenURL string) *ateapi.CredentialHeader { + return &ateapi.CredentialHeader{ + Header: header, + Prefix: "Bearer ", + TokenExchange: &ateapi.TokenExchangeSource{TokenUrl: tokenURL, Audience: "sts.example.com"}, + } +} + +// tokenExchangePolicy is replaceHeadersPolicy plus an https rule that allows +// tokenURL's host and port. +func tokenExchangePolicy(t *testing.T, leg, tokenURL string, entries ...*ateapi.CredentialHeader) *ateapi.EgressPolicy { + t.Helper() + u, err := url.Parse(tokenURL) + if err != nil { + t.Fatal(err) + } + port, err := strconv.Atoi(u.Port()) + if err != nil { + t.Fatal(err) + } + policy := replaceHeadersPolicy(leg, entries...) + policy.Rules = append(policy.Rules, &ateapi.EgressRule{Https: &ateapi.HttpsRule{ + Hostnames: []string{u.Hostname()}, + Ports: &ateapi.Ports{Numbers: []int32{int32(port)}}, + }}) + return policy +} + +func TestTokenExchangeInjection(t *testing.T) { for _, leg := range requestLegs { t.Run(leg, func(t *testing.T) { - provider := &fakeProvider{resp: bearerTokenResponse("s3cr3t")} - h := injectionHandlerFor(replaceHeadersPolicy(leg, &ateapi.CredentialHeader{ - Header: "authorization", - Prefix: "Bearer ", - TokenExchange: &ateapi.TokenExchangeSource{TokenUrl: "https://sts.example.com/token", Audience: "https://api.example.com"}, - }), provider, "") - _, err := h.HandleRequestHeaders(context.Background(), + tokenURL, tokenClient, _ := tokenEndpoint(t, http.StatusOK, `{"access_token":"exchanged","expires_in":900}`) + client := &egressMockClient{actor: runningActor(), policy: tokenExchangePolicy(t, leg, tokenURL, tokenExchangeHeader("authorization", tokenURL))} + h := New(client, nil, 0, nil, tokenClient) + + res, err := h.HandleRequestHeaders(context.Background(), withAuthorization(innerMetadata(leg, "GET", "api.example.com", nil))) - wantStatus(t, err, envoy_type.StatusCode_InternalServerError) - if provider.got != nil { - t.Errorf("provider was asked for %v, want no fetch", provider.got) + if err != nil { + t.Fatalf("HandleRequestHeaders: %v", err) + } + setHeaders := res.Response.GetResponse().GetHeaderMutation().GetSetHeaders() + if len(setHeaders) != 1 { + t.Fatalf("got %d header mutations, want 1", len(setHeaders)) + } + if got := string(setHeaders[0].GetHeader().GetRawValue()); got != "Bearer exchanged" { + t.Errorf("header value = %q, want %q", got, "Bearer exchanged") + } + if got := setHeaders[0].GetAppendAction(); got != corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD { + t.Errorf("append action = %v, want OVERWRITE_IF_EXISTS_OR_ADD", got) + } + want := &ateapi.MintActorJwtRequest{ + Actor: &ateapi.ObjectRef{Atespace: testEgressAtespace, Name: testEgressActor}, + Audiences: []string{"sts.example.com"}, + ExpirationSeconds: 300, + } + if got := client.lastMint.Load(); !proto.Equal(got, want) { + t.Errorf("MintActorJwt request = %v, want %v", got, want) + } + }) + } +} + +func TestTokenExchangeSkippedWithoutHeader(t *testing.T) { + tokenURL, tokenClient, exchanges := tokenEndpoint(t, http.StatusOK, `{"access_token":"exchanged"}`) + client := &egressMockClient{actor: runningActor(), policy: tokenExchangePolicy(t, extproc.EgressTLSMITMFilterChainName, tokenURL, tokenExchangeHeader("authorization", tokenURL))} + h := New(client, nil, 0, nil, tokenClient) + res, err := h.HandleRequestHeaders(context.Background(), + innerMetadata(extproc.EgressTLSMITMFilterChainName, "GET", "api.example.com", nil)) + if err != nil { + t.Fatalf("HandleRequestHeaders: %v", err) + } + if got := res.Response.GetResponse().GetHeaderMutation().GetSetHeaders(); len(got) != 0 { + t.Errorf("got %d injected headers, want 0", len(got)) + } + if n := exchanges.Load(); n != 0 { + t.Errorf("%d exchanges, want 0", n) + } +} + +func TestTokenExchangeDenials(t *testing.T) { + const issued = `{"access_token":"exchanged"}` + tests := []struct { + name string + status int + body string + mintErr error + notAllowed bool // no rule allows the token URL + want envoy_type.StatusCode + }{ + {name: "token URL no rule allows", status: http.StatusOK, body: issued, notAllowed: true, want: envoy_type.StatusCode_InternalServerError}, + {name: "endpoint refuses the actor", status: http.StatusBadRequest, body: `{"error":"invalid_grant"}`, want: envoy_type.StatusCode_Forbidden}, + {name: "endpoint unavailable", status: http.StatusServiceUnavailable, want: envoy_type.StatusCode_ServiceUnavailable}, + {name: "endpoint rejects the request", status: http.StatusBadRequest, body: `{"error":"invalid_request"}`, want: envoy_type.StatusCode_InternalServerError}, + {name: "actor deleted", status: http.StatusOK, body: issued, mintErr: status.Error(codes.NotFound, "actor not found"), want: envoy_type.StatusCode_Forbidden}, + {name: "ateapi unavailable", status: http.StatusOK, body: issued, mintErr: status.Error(codes.Unavailable, "ateapi is down"), want: envoy_type.StatusCode_ServiceUnavailable}, + {name: "gateway may not mint", status: http.StatusOK, body: issued, mintErr: status.Error(codes.PermissionDenied, "denied"), want: envoy_type.StatusCode_InternalServerError}, + } + for _, leg := range requestLegs { + t.Run(leg, func(t *testing.T) { + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tokenURL, tokenClient, exchanges := tokenEndpoint(t, tc.status, tc.body) + entry := tokenExchangeHeader("authorization", tokenURL) + policy := tokenExchangePolicy(t, leg, tokenURL, entry) + if tc.notAllowed { + policy = replaceHeadersPolicy(leg, entry) + } + h := New(&egressMockClient{actor: runningActor(), policy: policy, mintErr: tc.mintErr}, nil, 0, nil, tokenClient) + _, err := h.HandleRequestHeaders(context.Background(), + withAuthorization(innerMetadata(leg, "GET", "api.example.com", nil))) + wantStatus(t, err, tc.want) + if tc.notAllowed && exchanges.Load() != 0 { + t.Errorf("%d exchanges at a token URL no rule allows, want 0", exchanges.Load()) + } + }) } }) } diff --git a/cmd/atenet/internal/router/egress/egress.go b/cmd/atenet/internal/router/egress/egress.go index fa5e802d3a..86c8c38f1f 100644 --- a/cmd/atenet/internal/router/egress/egress.go +++ b/cmd/atenet/internal/router/egress/egress.go @@ -44,6 +44,7 @@ import ( "go.ate.dev/substrate/api/ateapi/v1" "go.ate.dev/substrate/api/credprovider/v1" "go.ate.dev/substrate/cmd/atenet/internal/router/egress/actorjwt" + "go.ate.dev/substrate/cmd/atenet/internal/router/egress/tokenexchange" "go.ate.dev/substrate/cmd/atenet/internal/router/extproc" "go.ate.dev/substrate/internal/egresspolicy" "go.ate.dev/substrate/internal/resources" @@ -85,6 +86,9 @@ type Handler struct { policies *policyCache // actorJWTs mints the actor JWTs that credential injections ask for. actorJWTs *actorjwt.Minter + // tokens exchanges actor JWTs for the access tokens token_exchange + // injections ask for. + tokens *tokenexchange.Broker // providers is a map of provider name (from the URI) to the credential // provider. // @@ -102,18 +106,20 @@ type Handler struct { // providers resolve an allowed rule's credential_uri injections on the // TLS-terminated MITM leg; none leaves them off, so a request that needs one is // denied. Actor JWT injections need no provider. See Handler.providers for the -// map definition. -func New(apiClient ateapi.ControlClient, loadActorIdentityRoots func() (*x509.CertPool, error), policyCacheTTL time.Duration, providers map[string]credprovider.CredentialProviderClient) *Handler { +// map definition. tokenClient sends the exchanges of token_exchange injections. +func New(apiClient ateapi.ControlClient, loadActorIdentityRoots func() (*x509.CertPool, error), policyCacheTTL time.Duration, providers map[string]credprovider.CredentialProviderClient, tokenClient *tokenexchange.Client) *Handler { if loadActorIdentityRoots == nil { loadActorIdentityRoots = func() (*x509.CertPool, error) { return nil, errors.New("no actor-identity CA configured") } } + actorJWTs := actorjwt.New(apiClient) return &Handler{ apiClient: apiClient, loadActorIdentityRoots: loadActorIdentityRoots, policies: newPolicyCache(apiClient, policyCacheTTL), - actorJWTs: actorjwt.New(apiClient), + actorJWTs: actorJWTs, + tokens: tokenexchange.NewBroker(actorJWTs, tokenClient), providers: providers, } } diff --git a/cmd/atenet/internal/router/egress/egress_test.go b/cmd/atenet/internal/router/egress/egress_test.go index 8be08b8d97..f52682accd 100644 --- a/cmd/atenet/internal/router/egress/egress_test.go +++ b/cmd/atenet/internal/router/egress/egress_test.go @@ -167,7 +167,7 @@ func xfccHeaderDER(chain ...[]byte) string { // egressHandler builds a Handler with an allow-everything policy, so the // identity tests are about identity alone. func egressHandler(loadRoots func() (*x509.CertPool, error), actor *ateapi.Actor, err error) *Handler { - return New(&egressMockClient{actor: actor, err: err, policy: allowAllPolicy()}, loadRoots, DefaultPolicyCacheTTL, nil) + return New(&egressMockClient{actor: actor, err: err, policy: allowAllPolicy()}, loadRoots, DefaultPolicyCacheTTL, nil, nil) } // egressMockClient is the slice of ateapi the egress handler talks to. @@ -409,7 +409,7 @@ func TestConnectLegOpensForAnyRules(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - h := New(&egressMockClient{actor: runningActor(), policy: tc.policy}, ca.loadRoots, 0, nil) + h := New(&egressMockClient{actor: runningActor(), policy: tc.policy}, ca.loadRoots, 0, nil, nil) md := egressMetadata(xfccHeader(leaf)) md.Host = "93.184.216.34:443" if tc.dialed != "" { @@ -468,7 +468,7 @@ func TestConnectLegWithoutRequestLegs(t *testing.T) { leaf := ca.issueActorCert(t, "spiffe://substrate-actor.local/worker-for-actor/foo/bar", actorCertOptions{}) certificate := string(pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: leaf.Raw})) - h := New(&egressMockClient{actor: runningActor(), policy: httpPolicy("api.example.com")}, ca.loadRoots, 0, nil) + h := New(&egressMockClient{actor: runningActor(), policy: httpPolicy("api.example.com")}, ca.loadRoots, 0, nil, nil) res, err := h.HandleRequestHeaders(context.Background(), agentgatewayEgressMetadata(certificate)) if err != nil { t.Fatalf("HandleRequestHeaders() error = %v, want the tunnel to open", err) @@ -476,7 +476,7 @@ func TestConnectLegWithoutRequestLegs(t *testing.T) { if got := dialedPortOf(res); got != "80" { t.Errorf("dialed port = %q, want %q", got, "80") } - h = New(&egressMockClient{actor: runningActor()}, ca.loadRoots, 0, nil) + h = New(&egressMockClient{actor: runningActor()}, ca.loadRoots, 0, nil, nil) _, err = h.HandleRequestHeaders(context.Background(), agentgatewayEgressMetadata(certificate)) wantStatus(t, err, envoy_type.StatusCode_Forbidden) } diff --git a/cmd/atenet/internal/router/egress/request.go b/cmd/atenet/internal/router/egress/request.go index 308f8accea..d51968f6d7 100644 --- a/cmd/atenet/internal/router/egress/request.go +++ b/cmd/atenet/internal/router/egress/request.go @@ -103,7 +103,7 @@ func (h *Handler) handleRequest(ctx context.Context, md *extproc.RequestMetadata slog.WarnContext(ctx, "egress denied: no rule allows the destination", attrs(decision)...) return extproc.Result{}, extproc.NewReqError(envoy_type.StatusCode_Forbidden, deniedBody) } - injected, err := h.applyEffects(ctx, ref, dest, md.Headers, decision.Effects) + injected, err := h.applyEffects(ctx, ref, dest, md.Headers, policy, decision.Effects) if err != nil { return extproc.Result{}, err } diff --git a/cmd/atenet/internal/router/egress/request_test.go b/cmd/atenet/internal/router/egress/request_test.go index 49ceb6d60b..9f5b9ccf0a 100644 --- a/cmd/atenet/internal/router/egress/request_test.go +++ b/cmd/atenet/internal/router/egress/request_test.go @@ -66,7 +66,7 @@ func cleartextInjectionPolicy(pattern string) *ateapi.EgressPolicy { // policyHandler builds a Handler for an actor whose policy is policy (nil // means none) with the cache disabled, so each callout sees the mock as is. func policyHandler(policy *ateapi.EgressPolicy) *Handler { - return New(&egressMockClient{actor: runningActor(), policy: policy}, nil, 0, nil) + return New(&egressMockClient{actor: runningActor(), policy: policy}, nil, 0, nil, nil) } // testSNI is the server name the test actor's TLS connection presented, on @@ -420,7 +420,7 @@ func TestRequestLegPolicyLookup(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - h := New(tc.client, nil, 0, nil) + h := New(tc.client, nil, 0, nil, nil) _, err := h.HandleRequestHeaders(context.Background(), requestMetadata("api.example.com")) wantStatus(t, err, tc.want) }) @@ -446,7 +446,7 @@ func TestConnectLegRequiresAPolicy(t *testing.T) { } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - h := New(tc.client, ca.loadRoots, DefaultPolicyCacheTTL, nil) + h := New(tc.client, ca.loadRoots, DefaultPolicyCacheTTL, nil, nil) res, err := h.HandleRequestHeaders(context.Background(), egressMetadata(xfccHeader(leaf))) if tc.want == 0 { wantAllowed(t, res, err) @@ -503,7 +503,7 @@ func TestDenialBodyIsUniform(t *testing.T) { // A caller that gives up mid-fetch is neither a denial nor an outage. func TestCanceledCallerIsNotAPolicyFailure(t *testing.T) { client := &egressMockClient{policy: allowAllPolicy(), policyGate: make(chan struct{})} - h := New(client, nil, 0, nil) + h := New(client, nil, 0, nil, nil) ctx, cancel := context.WithCancel(context.Background()) done := make(chan error, 1) go func() { diff --git a/cmd/atenet/internal/router/extproc/egress_websocket_test.go b/cmd/atenet/internal/router/extproc/egress_websocket_test.go index 738d87c74b..8397989571 100644 --- a/cmd/atenet/internal/router/extproc/egress_websocket_test.go +++ b/cmd/atenet/internal/router/extproc/egress_websocket_test.go @@ -74,7 +74,7 @@ func TestProcessReturnsWebSocketDenialAsImmediateResponse(t *testing.T) { } stream := &websocketProcessStream{ctx: context.Background(), requests: []*extprocv3.ProcessingRequest{request}} server := extproc.NewServer(0, nil, extproc.Handlers{ - extproc.DirectionEgress: egress.New(nil, nil, 0, nil), + extproc.DirectionEgress: egress.New(nil, nil, 0, nil, nil), }) if err := server.Process(stream); err != nil { diff --git a/cmd/atenet/internal/router/router.go b/cmd/atenet/internal/router/router.go index 0f717047f9..2a4666a00b 100644 --- a/cmd/atenet/internal/router/router.go +++ b/cmd/atenet/internal/router/router.go @@ -256,13 +256,12 @@ func (s *RouterServer) Run(ctx context.Context) error { slog.String("provider_name", providerName)) } - // TODO(#1661): pass the client to egress.New once the handler injects - // exchanged tokens. - if _, err := tokenexchange.New(s.cfg.TokenExchangeCAFile); err != nil { + tokenClient, err := tokenexchange.New(s.cfg.TokenExchangeCAFile) + if err != nil { return fmt.Errorf("loading --token-exchange-ca-file: %w", err) } - egressHandler := egress.New(s.apiClient, loadActorIdentityRoots, s.cfg.EgressPolicyCacheTTL, providers) + egressHandler := egress.New(s.apiClient, loadActorIdentityRoots, s.cfg.EgressPolicyCacheTTL, providers, tokenClient) handlers[egressHandler.Direction()] = egressHandler } From 6fcd8b80efade0ab8afdf2068d68c7ba951700d8 Mon Sep 17 00:00:00 2001 From: Max Thompson Date: Tue, 27 Oct 2026 13:33:57 -0700 Subject: [PATCH 08/10] ate-setup: add --token-exchange-ca-configmap for the egress gateway The egress gateway's --token-exchange-ca-file adds PEM certificates it trusts for token exchange endpoints with private certificates. ate-setup server-side applies the egress Deployment on every install, so a manual patch of the ext-proc args would not survive the next one. When the setting names a ConfigMap, the installer mounts its ca.crt into the ext-proc container and sets the flag. The admin owns the ConfigMap: deploy ate-system and deploy atenet fail before changing the cluster if it or its ca.crt key is missing. The setting requires the Envoy dataplane. Signed-off-by: Max Thompson --- cmd/ate-setup/commands.md | 1 + cmd/ate-setup/internal/config/config.go | 4 + cmd/ate-setup/internal/config/config_test.go | 3 + cmd/ate-setup/internal/config/errors_test.go | 5 + cmd/ate-setup/internal/config/load.go | 12 +++ cmd/ate-setup/internal/config/options_test.go | 2 + cmd/ate-setup/internal/config/setting.go | 7 ++ cmd/ate-setup/internal/steps/deploy.go | 6 ++ cmd/ate-setup/internal/steps/overlay.go | 63 ++++++++++++ cmd/ate-setup/internal/steps/overlay_test.go | 99 +++++++++++++++++++ docs/guides/operator-install.md | 1 + manifests/ate-install/atenet-egress.yaml | 6 ++ 12 files changed, 209 insertions(+) diff --git a/cmd/ate-setup/commands.md b/cmd/ate-setup/commands.md index 0b91ed1390..a1479ef4fa 100644 --- a/cmd/ate-setup/commands.md +++ b/cmd/ate-setup/commands.md @@ -30,6 +30,7 @@ every setting with its environment variable. | `--egress-gateway-extra-spiffe-ids IDS` | Comma-separated SPIFFE IDs that hold ate-api-server's `egress_gateway` role next to atenet-egress, such as a credential provider that mints actor JWTs. ate-setup passes atenet-egress's ID and then each listed ID as repeated `--egress-gateway-spiffe-id` flags; unset passes none, leaving ate-api-server's default. `ATE_EGRESS_GATEWAY_EXTRA_SPIFFE_IDS` when the flag is absent | | `--experimental-additional-egress-extproc-service NS/SVC:PORT` | External processor authorization filter | | `--credential-provider JSON` | Required by `deploy ate-system` and `deploy atenet`. A JSON object: `{"name":"k8s.io"}` deploys and uses the bundled Kubernetes Secrets provider, with a NetworkPolicy that admits only the egress gateway; `{"enabled":false}` turns egress credential injection off; `{"name":"","address":":"}` uses a provider you deploy yourself. A JSON array of provider objects selects several, each serving the `ate-secret://` URIs its name is the host of. See [`docs/guides/egress-credential-injection.md`](../../docs/guides/egress-credential-injection.md). `ATE_CREDENTIAL_PROVIDER` when the flag is absent | +| `--token-exchange-ca-configmap NAME` | ConfigMap in the install namespace whose `ca.crt` key holds PEM certificates the egress gateway trusts for token exchange endpoints, in addition to the system roots. The installer mounts it but does not create it: `deploy ate-system` and `deploy atenet` fail if it or its `ca.crt` key is missing. The gateway reads it at startup, so restart `atenet-egress` after changing it. Requires `--atenet-dataplane=envoy`. `ATE_TOKEN_EXCHANGE_CA_CONFIGMAP` when the flag is absent | | `--ateapi-log-level LEVEL` | ate-api-server log level: `debug`, `info`, `warn`, or `error`. Written to the `ate-api-config` ConfigMap on every deploy when set; unset keeps the installed level (`info` on a new install). Changing the level updates the env-hash annotation and rolls `ate-api-server`. `ATE_API_LOG_LEVEL` when the flag is absent | | `--otlp-endpoint URL` | Send control plane telemetry to `URL` instead of the cluster default (see [`benchmarking/telemetry/README.md`](../../benchmarking/telemetry/README.md)). `ATE_OTLP_ENDPOINT` when the flag is absent | | `--preview GATES` | Preview gates passed as `--preview` to every substrate component: comma-separated gate names, or `ALL` for all. `ATE_PREVIEW` or `config.preview` when the flag is absent. An explicit `--preview=''` turns them all off, even when `ATE_PREVIEW` is set | diff --git a/cmd/ate-setup/internal/config/config.go b/cmd/ate-setup/internal/config/config.go index 30bcd18aad..64c8e989d3 100644 --- a/cmd/ate-setup/internal/config/config.go +++ b/cmd/ate-setup/internal/config/config.go @@ -242,6 +242,10 @@ type Config struct { // CredentialProviders for its schema. CredentialProviderJSON string + // TokenExchangeCAConfigMap names the admin-owned ConfigMap whose ca.crt + // the egress gateway trusts for token exchange endpoints. + TokenExchangeCAConfigMap string + // AnthropicAPIKey is required only by the claude-code-multiplex demo. AnthropicAPIKey string diff --git a/cmd/ate-setup/internal/config/config_test.go b/cmd/ate-setup/internal/config/config_test.go index 18e95e41be..19a3df5728 100644 --- a/cmd/ate-setup/internal/config/config_test.go +++ b/cmd/ate-setup/internal/config/config_test.go @@ -74,6 +74,7 @@ func loadEnv(t *testing.T) { "ATE_INSTALL_ROLLOUT_TIMEOUT", "ATE_OTLP_ENDPOINT", "ATE_PREVIEW", + "ATE_TOKEN_EXCHANGE_CA_CONFIGMAP", "BENCHMARK_ACTOR_MEMORY", "BUCKET_NAME", "CLUSTER_LOCATION", @@ -1042,6 +1043,8 @@ func TestLoadRejectsInvalidValues(t *testing.T) { {"cluster size", Options{ClusterSize: "size5"}}, {"extproc invalid format", Options{AdditionalEgressExtprocService: "extproc:50051"}}, {"extproc agentgateway", Options{Router: RouterAgentgateway, AdditionalEgressExtprocService: "ate-system/extproc:50051"}}, + {"token exchange CA name", Options{TokenExchangeCAConfigMap: "Corp_CA"}}, + {"token exchange CA agentgateway", Options{Router: RouterAgentgateway, TokenExchangeCAConfigMap: "corp-ca"}}, {"provider not JSON", Options{CredentialProvider: "k8s.io"}}, {"provider as a JSON string", Options{CredentialProvider: `"off"`}}, {"provider null", Options{CredentialProvider: `null`}}, diff --git a/cmd/ate-setup/internal/config/errors_test.go b/cmd/ate-setup/internal/config/errors_test.go index 87169d6be8..10e9f68728 100644 --- a/cmd/ate-setup/internal/config/errors_test.go +++ b/cmd/ate-setup/internal/config/errors_test.go @@ -209,6 +209,11 @@ func TestValidationErrorsNameTheSupplyingChannel(t *testing.T) { env: map[string]string{"ATE_ADDITIONAL_EGRESS_EXTPROC_SERVICE": "ns/svc:99999"}, wantIn: []string{"atenet.egress.additionalExtprocService", "1-65535", "ATE_ADDITIONAL_EGRESS_EXTPROC_SERVICE"}, }, + { + name: "token exchange CA is not a ConfigMap name", + env: map[string]string{"ATE_TOKEN_EXCHANGE_CA_CONFIGMAP": "Corp_CA"}, + wantIn: []string{"atenet.egress.tokenExchangeCAConfigMap", "a ConfigMap name", `"Corp_CA"`, "ATE_TOKEN_EXCHANGE_CA_CONFIGMAP"}, + }, { name: "image tag without a repository", env: map[string]string{"ATE_IMAGE_TAG": "v1"}, diff --git a/cmd/ate-setup/internal/config/load.go b/cmd/ate-setup/internal/config/load.go index c9410fab3a..635f53ca1b 100644 --- a/cmd/ate-setup/internal/config/load.go +++ b/cmd/ate-setup/internal/config/load.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/pflag" "github.com/spiffe/go-spiffe/v2/spiffeid" + "k8s.io/apimachinery/pkg/util/validation" "go.ate.dev/substrate/cmd/ate-setup/internal/images" "go.ate.dev/substrate/internal/actoridjwt" @@ -273,6 +274,7 @@ func buildConfig(root string, env map[string]string, r *Resolved) (*Config, erro IngressAllowedSPIFFEIDs: splitList(r.String("ingressAllowedSPIFFEIDs")), AdditionalEgressExtprocService: r.String("atenet.egress.additionalExtprocService"), CredentialProviderJSON: r.String("atenet.egress.credentialProvider"), + TokenExchangeCAConfigMap: r.String("atenet.egress.tokenExchangeCAConfigMap"), AnthropicAPIKey: r.String("demo.anthropicAPIKey"), OtlpEndpoint: r.String("otlpEndpoint"), APILogLevel: r.String("ateapi.logLevel"), @@ -449,6 +451,16 @@ func validateResolved(cfg *Config, r *Resolved) error { Why: "an additional ext_proc filter requires dataplane " + RouterEnvoy} } } + if cfg.TokenExchangeCAConfigMap != "" { + ca, _ := r.Value("atenet.egress.tokenExchangeCAConfigMap") + if len(validation.IsDNS1123Subdomain(cfg.TokenExchangeCAConfigMap)) != 0 { + return &InvalidError{Value: ca, Want: "a ConfigMap name"} + } + if cfg.Router != RouterEnvoy { + return &ConflictError{A: ca, B: dataplane, + Why: "token exchange requires dataplane " + RouterEnvoy} + } + } // A gate the components do not know makes each of them refuse to start, so // catch it here rather than as a failed rollout. if err := preview.Verify(cfg.PreviewGates...); err != nil { diff --git a/cmd/ate-setup/internal/config/options_test.go b/cmd/ate-setup/internal/config/options_test.go index a6d03c514d..4c4f43808f 100644 --- a/cmd/ate-setup/internal/config/options_test.go +++ b/cmd/ate-setup/internal/config/options_test.go @@ -43,6 +43,7 @@ type Options struct { E2ETuning bool AdditionalEgressExtprocService string CredentialProvider string + TokenExchangeCAConfigMap string OtlpEndpoint string // Image source selection. @@ -91,6 +92,7 @@ func optionsFlagSet(opts Options) (*pflag.FlagSet, error) { {"cluster-size", opts.ClusterSize}, {"experimental-additional-egress-extproc-service", opts.AdditionalEgressExtprocService}, {"credential-provider", opts.CredentialProvider}, + {"token-exchange-ca-configmap", opts.TokenExchangeCAConfigMap}, {"otlp-endpoint", opts.OtlpEndpoint}, {"image-repo", opts.ImageRepo}, {"image-tag", opts.ImageTag}, diff --git a/cmd/ate-setup/internal/config/setting.go b/cmd/ate-setup/internal/config/setting.go index cb3dfd15c8..cebc69dcc5 100644 --- a/cmd/ate-setup/internal/config/setting.go +++ b/cmd/ate-setup/internal/config/setting.go @@ -169,6 +169,13 @@ var Registry = []Setting{ Usage: "Run an additional ext_proc authorization filter served by NS/SVC:PORT " + "(requires --atenet-dataplane=envoy)", }, + { + Key: "atenet.egress.tokenExchangeCAConfigMap", Env: "ATE_TOKEN_EXCHANGE_CA_CONFIGMAP", + Flag: "token-exchange-ca-configmap", Kind: KindString, + Usage: "ConfigMap in the install namespace whose ca.crt key holds PEM certificates the egress " + + "gateway trusts for token exchange endpoints, besides the system roots. The installer does " + + "not create it (requires --atenet-dataplane=envoy)", + }, { // No default: an unset level keeps the one an existing install has, diff --git a/cmd/ate-setup/internal/steps/deploy.go b/cmd/ate-setup/internal/steps/deploy.go index b1f9c4fe7a..62668a7487 100644 --- a/cmd/ate-setup/internal/steps/deploy.go +++ b/cmd/ate-setup/internal/steps/deploy.go @@ -97,6 +97,9 @@ func (e *Env) DeployAteSystem(ctx context.Context, opts DeployOptions) error { if err := e.requireNoWorkerPods(ctx); err != nil { return err } + if err := e.requireTokenExchangeCA(ctx); err != nil { + return err + } var microvmPrep *microVMPrepared if microvm { microvmPrep, err = e.prepareMicroVMDeps(ctx) @@ -556,6 +559,9 @@ func (e *Env) DeployAtenet(ctx context.Context) error { if err != nil { return err } + if err := e.requireTokenExchangeCA(ctx); err != nil { + return err + } if err := e.EnsureCRDs(ctx); err != nil { return err } diff --git a/cmd/ate-setup/internal/steps/overlay.go b/cmd/ate-setup/internal/steps/overlay.go index 2fbaf8ca66..ac8c32c334 100644 --- a/cmd/ate-setup/internal/steps/overlay.go +++ b/cmd/ate-setup/internal/steps/overlay.go @@ -275,6 +275,10 @@ func (e *Env) renderAtenetEgressManifest(ctx context.Context, providers []config if err != nil { return nil, err } + raw, err = patchAtenetEgressTokenExchangeCA(raw, e.Cfg.TokenExchangeCAConfigMap) + if err != nil { + return nil, err + } raw = e.patchEnvoyDataplaneImage(raw, imageReference) if e.Cfg.EnvoyConcurrency > 0 { raw, err = patchAtenetEgressEnvoyConcurrency(raw, e.Cfg.EnvoyConcurrency) @@ -389,6 +393,65 @@ func patchAgentgatewayEgressInject(raw []byte, providers []config.CredentialProv return replaceManifestMarker(raw, "#ATE_AGENTGATEWAY_CREDENTIAL_PROVIDERS", block) } +const ( + tokenExchangeCADir = "/run/token-exchange-ca" + tokenExchangeCAKey = "ca.crt" +) + +// patchAtenetEgressTokenExchangeCA replaces the token exchange CA markers in +// the egress sidecar with --token-exchange-ca-file and a mount of configMap's +// ca.crt, or removes them when configMap is empty. +func patchAtenetEgressTokenExchangeCA(raw []byte, configMap string) ([]byte, error) { + var flag, mount, volume string + if configMap != "" { + flag = fmt.Sprintf("- --token-exchange-ca-file=%s/%s", tokenExchangeCADir, tokenExchangeCAKey) + mount = fmt.Sprintf(`- name: token-exchange-ca + mountPath: %s + readOnly: true`, tokenExchangeCADir) + volume = fmt.Sprintf(`- name: token-exchange-ca + configMap: + name: %q + items: + - key: %s + path: %s`, configMap, tokenExchangeCAKey, tokenExchangeCAKey) + } + for _, splice := range []struct{ marker, block string }{ + {"#ATE_TOKEN_EXCHANGE_CA_FLAG", flag}, + {"#ATE_TOKEN_EXCHANGE_CA_MOUNT", mount}, + {"#ATE_TOKEN_EXCHANGE_CA_VOLUME", volume}, + } { + var err error + if raw, err = replaceManifestMarker(raw, splice.marker, splice.block); err != nil { + return nil, err + } + } + return raw, nil +} + +// requireTokenExchangeCA fails when --token-exchange-ca-configmap names a +// ConfigMap the install namespace lacks, or one without a ca.crt key, which +// would leave the egress gateway unable to start. +func (e *Env) requireTokenExchangeCA(ctx context.Context) error { + name := e.Cfg.TokenExchangeCAConfigMap + if name == "" { + return nil + } + cm, err := e.Kube.GetConfigMap(ctx, e.Namespace(), name) + if err != nil { + return err + } + if cm == nil { + return fmt.Errorf("token exchange CA ConfigMap %s/%s does not exist; create it with the CA certificates under key %s", + e.Namespace(), name, tokenExchangeCAKey) + } + _, inData := cm.Data[tokenExchangeCAKey] + _, inBinary := cm.BinaryData[tokenExchangeCAKey] + if !inData && !inBinary { + return fmt.Errorf("token exchange CA ConfigMap %s/%s has no %s key", e.Namespace(), name, tokenExchangeCAKey) + } + return nil +} + func replaceManifestMarker(raw []byte, marker, block string) ([]byte, error) { var out []string replaced := 0 diff --git a/cmd/ate-setup/internal/steps/overlay_test.go b/cmd/ate-setup/internal/steps/overlay_test.go index ab735825d3..6bfc0f503f 100644 --- a/cmd/ate-setup/internal/steps/overlay_test.go +++ b/cmd/ate-setup/internal/steps/overlay_test.go @@ -1623,6 +1623,105 @@ func TestRenderAtenetEgressManifestEnvoyConcurrency(t *testing.T) { }) } +func TestRenderAtenetEgressManifestTokenExchangeCA(t *testing.T) { + const digest = "sha256:2222222222222222222222222222222222222222222222222222222222222222" + src := images.Source{Repo: "example.com/substrate", Tag: "v1.2.3"} + render := func(t *testing.T, configMap string) (corev1.Container, []corev1.Volume) { + t.Helper() + e := &Env{ + Cfg: &config.Config{Root: repoRoot(t), Router: config.RouterEnvoy, Images: src, TokenExchangeCAConfigMap: configMap}, + resolver: images.NewPrebuilt(src, func(context.Context, string) (string, error) { + return digest, nil + }), + } + // No credential provider: the CA does not depend on one. + out, err := e.renderAtenetEgressManifest(t.Context(), nil) + if err != nil { + t.Fatalf("renderAtenetEgressManifest() error = %v", err) + } + for _, doc := range strings.Split(string(out), "\n---\n") { + var dep appsv1.Deployment + if err := yaml.Unmarshal([]byte(doc), &dep); err != nil || dep.Kind != "Deployment" || dep.Name != "atenet-egress" { + continue + } + for _, c := range dep.Spec.Template.Spec.Containers { + if c.Name == "ext-proc" { + return c, dep.Spec.Template.Spec.Volumes + } + } + t.Fatal("atenet-egress Deployment has no ext-proc container") + } + t.Fatal("rendered manifest has no atenet-egress Deployment") + return corev1.Container{}, nil + } + const flag = "--token-exchange-ca-file=/run/token-exchange-ca/ca.crt" + + t.Run("unset", func(t *testing.T) { + extProc, volumes := render(t, "") + if slices.Contains(extProc.Args, flag) { + t.Errorf("ext-proc args contain %s with no ConfigMap set", flag) + } + if slices.ContainsFunc(extProc.VolumeMounts, func(m corev1.VolumeMount) bool { return m.Name == "token-exchange-ca" }) { + t.Error("ext-proc mounts token-exchange-ca with no ConfigMap set") + } + if slices.ContainsFunc(volumes, func(v corev1.Volume) bool { return v.Name == "token-exchange-ca" }) { + t.Error("atenet-egress has a token-exchange-ca volume with no ConfigMap set") + } + }) + + t.Run("set", func(t *testing.T) { + extProc, volumes := render(t, "corp-ca") + if !slices.Contains(extProc.Args, flag) { + t.Errorf("ext-proc args = %v, want %s", extProc.Args, flag) + } + wantMount := corev1.VolumeMount{Name: "token-exchange-ca", MountPath: "/run/token-exchange-ca", ReadOnly: true} + if !slices.ContainsFunc(extProc.VolumeMounts, func(m corev1.VolumeMount) bool { return reflect.DeepEqual(m, wantMount) }) { + t.Errorf("ext-proc volume mounts = %v, want %v", extProc.VolumeMounts, wantMount) + } + wantVolume := corev1.Volume{Name: "token-exchange-ca", VolumeSource: corev1.VolumeSource{ConfigMap: &corev1.ConfigMapVolumeSource{ + LocalObjectReference: corev1.LocalObjectReference{Name: "corp-ca"}, + Items: []corev1.KeyToPath{{Key: "ca.crt", Path: "ca.crt"}}, + }}} + if !slices.ContainsFunc(volumes, func(v corev1.Volume) bool { return reflect.DeepEqual(v, wantVolume) }) { + t.Errorf("atenet-egress volumes = %v, want one equal to %v", volumes, wantVolume) + } + }) + + if _, err := patchAtenetEgressTokenExchangeCA([]byte("kind: ConfigMap\n"), "corp-ca"); err == nil { + t.Error("patchAtenetEgressTokenExchangeCA accepted a manifest without the markers") + } +} + +func TestRequireTokenExchangeCA(t *testing.T) { + configMap := func(data, binaryData map[string]string) *corev1.ConfigMap { + cm := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Namespace: NamespaceAteSystem, Name: "corp-ca"}, Data: data} + for k, v := range binaryData { + cm.BinaryData = map[string][]byte{k: []byte(v)} + } + return cm + } + for _, tc := range []struct { + name string + configMap string + objects []runtime.Object + wantErr bool + }{ + {name: "unset"}, + {name: "present", configMap: "corp-ca", objects: []runtime.Object{configMap(map[string]string{"ca.crt": "pem"}, nil)}}, + {name: "present as binary data", configMap: "corp-ca", objects: []runtime.Object{configMap(nil, map[string]string{"ca.crt": "der"})}}, + {name: "missing", configMap: "corp-ca", wantErr: true}, + {name: "without ca.crt", configMap: "corp-ca", objects: []runtime.Object{configMap(map[string]string{"tls.crt": "pem"}, nil)}, wantErr: true}, + } { + t.Run(tc.name, func(t *testing.T) { + e := &Env{Cfg: &config.Config{Root: repoRoot(t), TokenExchangeCAConfigMap: tc.configMap}, Kube: fakeKube(t, tc.objects...)} + err := e.requireTokenExchangeCA(t.Context()) + if (err != nil) != tc.wantErr { + t.Fatalf("requireTokenExchangeCA() = %v, want error %v", err, tc.wantErr) + } + }) + } +} + // Without --fake-data-plane there is no fake-workersync rollout to wait for. func TestWaitFakeWorkersyncWithoutTheFlagIsANoOp(t *testing.T) { e := &Env{Cfg: &config.Config{Root: repoRoot(t)}, Kube: fakeKube(t)} diff --git a/docs/guides/operator-install.md b/docs/guides/operator-install.md index c62050a5c0..b61fc3c892 100644 --- a/docs/guides/operator-install.md +++ b/docs/guides/operator-install.md @@ -300,6 +300,7 @@ demos, the [benchmarks](benchmarking.md#settings), and the | `atenet.dataplane` | `ATE_ATENET_DATAPLANE` | `--atenet-dataplane` | `envoy` | (global) | | `atenet.egress.additionalExtprocService` | `ATE_ADDITIONAL_EGRESS_EXTPROC_SERVICE` | `--experimental-additional-egress-extproc-service` | — | (global) | | `atenet.egress.credentialProvider` | `ATE_CREDENTIAL_PROVIDER` | `--credential-provider` | — | (global) | +| `atenet.egress.tokenExchangeCAConfigMap` | `ATE_TOKEN_EXCHANGE_CA_CONFIGMAP` | `--token-exchange-ca-configmap` | — | (global) | | `clusterSize` | `ATE_INSTALL_CLUSTER_SIZE` | `--cluster-size` | `size0` | (global) | | `context` | `KUBECTL_CONTEXT` | `--context` | — | (global) | | `cordonSystemInfraComponents` | `ATE_INSTALL_CORDON_SYSTEM_INFRA_COMPONENTS` | `--cordon-system-infra-components` | `false` | (global) | diff --git a/manifests/ate-install/atenet-egress.yaml b/manifests/ate-install/atenet-egress.yaml index 5e8745027e..52aac8f0cf 100644 --- a/manifests/ate-install/atenet-egress.yaml +++ b/manifests/ate-install/atenet-egress.yaml @@ -1398,6 +1398,10 @@ spec: # {"enabled":false}. Without a provider, a request matching a rule that # requires an injection is denied (see egress.applyEffects). #ATE_EGRESS_INJECT_FLAGS -- Don't modify this line. + # The installer replaces this marker, and the two below, with + # --token-exchange-ca-file and the mount of the ConfigMap that + # --token-exchange-ca-configmap names, or removes them when it is unset. + #ATE_TOKEN_EXCHANGE_CA_FLAG -- Don't modify this line. securityContext: allowPrivilegeEscalation: false capabilities: @@ -1458,6 +1462,7 @@ spec: readOnly: true - name: drain-signal mountPath: /var/run/atenet + #ATE_TOKEN_EXCHANGE_CA_MOUNT -- Don't modify this line. volumes: - name: config configMap: @@ -1503,6 +1508,7 @@ spec: items: - key: pool path: mitm-pool.json + #ATE_TOKEN_EXCHANGE_CA_VOLUME -- Don't modify this line. --- apiVersion: v1 kind: Service From af2681565fecfacc00ae467639045c8577bfccc5 Mon Sep 17 00:00:00 2001 From: Max Thompson Date: Tue, 27 Oct 2026 14:17:36 -0700 Subject: [PATCH 09/10] e2e: test RFC 8693 token exchange through the egress gateway testserver gains POST /token, a fake RFC 8693 endpoint. It checks the grant and token types and that the audience is the subject JWT's only aud, and issues "exchanged.." without verifying the JWT. egresscredinject deploys it as a second origin, sts, whose https and http rules exchange actor JWTs at its own /token. A fetch of the sts origin must echo the token issued for this actor on both legs. Agentgateway, which does not support token exchange, must deny it with a 403. The suite also checks that ateapi rejects a policy whose token URL no https rule allows. hack/setup-e2e-egress-tls-kind.sh gives the Envoy gateway's ext-proc --token-exchange-ca-file with the service-DNS trust bundle it already mounts, so it trusts the sts origin's certificate. Signed-off-by: Max Thompson --- hack/setup-e2e-egress-tls-kind.sh | 17 ++++ internal/e2e/atenet_dataplane.go | 8 ++ internal/e2e/fixtures/testserver/http.go | 68 ++++++++++++- internal/e2e/fixtures/testserver/http_test.go | 51 ++++++++++ .../egresscredinject/egresscredinject_test.go | 96 +++++++++++++++++-- 5 files changed, 229 insertions(+), 11 deletions(-) diff --git a/hack/setup-e2e-egress-tls-kind.sh b/hack/setup-e2e-egress-tls-kind.sh index 9d2ce16539..f69c11e684 100755 --- a/hack/setup-e2e-egress-tls-kind.sh +++ b/hack/setup-e2e-egress-tls-kind.sh @@ -81,4 +81,21 @@ spec: emptyDir: {} EOF )" +# The Envoy gateway's ext-proc also exchanges tokens at local origins, so it +# trusts the same signer for token exchange. Its container already mounts the +# bundle. Re-runs leave the flag as it is. +if [[ "${gateway}" == envoy ]]; then + ca_flag=--token-exchange-ca-file=/run/servicedns.podcert.ate.dev/trust-bundle.pem + containers="$(kubectl "${kubectl_args[@]}" get deployment atenet-egress -o jsonpath='{range .spec.template.spec.containers[*]}{.name}{"\n"}{end}')" + index="$(grep -nx ext-proc <<<"${containers}" | cut -d: -f1)" + if [[ -z "${index}" ]]; then + echo "error: the Envoy egress is missing its ext-proc container" >&2 + exit 1 + fi + args="$(kubectl "${kubectl_args[@]}" get deployment atenet-egress -o jsonpath='{.spec.template.spec.containers[?(@.name=="ext-proc")].args}')" + if [[ "${args}" != *"${ca_flag}"* ]]; then + kubectl "${kubectl_args[@]}" patch deployment atenet-egress --type=json \ + --patch "[{\"op\":\"add\",\"path\":\"/spec/template/spec/containers/$((index - 1))/args/-\",\"value\":\"${ca_flag}\"}]" + fi +fi kubectl "${kubectl_args[@]}" rollout status deployment/atenet-egress --timeout=120s diff --git a/internal/e2e/atenet_dataplane.go b/internal/e2e/atenet_dataplane.go index a10c6cb3b1..c68c0a5bc2 100644 --- a/internal/e2e/atenet_dataplane.go +++ b/internal/e2e/atenet_dataplane.go @@ -35,6 +35,10 @@ type AtenetDataplane interface { IsRetryableParkingBudgetExhaustion(status int, body string) bool ParkingBudgetStatus() int IsEgressPolicyDenied(status int, body string) bool + // InjectsExchangedTokens reports whether the dataplane injects the + // credentials of token_exchange entries. One that does not denies the + // request with 403. + InjectsExchangedTokens() bool PlatformMetricPrefixes([]string) []string RouteDurationSeen(context.Context, string) (bool, error) EgressCollectorTelemetrySeen(string) bool @@ -86,6 +90,8 @@ func isEgressPolicyDenied(status int, body string) bool { (status == http.StatusBadGateway && strings.Contains(body, "request failed")) } +func (envoyAtenetDataplane) InjectsExchangedTokens() bool { return true } + func (envoyAtenetDataplane) PlatformMetricPrefixes(prefixes []string) []string { return prefixes } func (envoyAtenetDataplane) RouteDurationSeen(_ context.Context, collectorScrape string) (bool, error) { @@ -154,6 +160,8 @@ func (agentGatewayAtenetDataplane) IsEgressPolicyDenied(status int, body string) return isEgressPolicyDenied(status, body) } +func (agentGatewayAtenetDataplane) InjectsExchangedTokens() bool { return false } + func (agentGatewayAtenetDataplane) PlatformMetricPrefixes(prefixes []string) []string { filtered := make([]string, 0, len(prefixes)) for _, prefix := range prefixes { diff --git a/internal/e2e/fixtures/testserver/http.go b/internal/e2e/fixtures/testserver/http.go index 858045db77..067ccc6b38 100644 --- a/internal/e2e/fixtures/testserver/http.go +++ b/internal/e2e/fixtures/testserver/http.go @@ -15,6 +15,7 @@ package main import ( + "encoding/base64" "encoding/json" "fmt" "io" @@ -54,7 +55,7 @@ const defaultDialTimeout = 3 * time.Second // they arrived, which is how a suite sees what the gateway put on the wire. // The /fetch handler for e2e tests that need test requests with bodies. // /dial reports whether this pod can open a TCP connection to an address; see -// dial. +// dial. POST /token is a fake RFC 8693 token endpoint; see exchangeToken. func newHTTPHandler(body string) http.Handler { mux := http.NewServeMux() mux.HandleFunc("/healthz", func(w http.ResponseWriter, _ *http.Request) { @@ -81,9 +82,72 @@ func newHTTPHandler(body string) http.Handler { w.WriteHeader(http.StatusOK) }) mux.HandleFunc("/dial", dial) + mux.HandleFunc("POST /token", exchangeToken) return mux } +// The RFC 8693 values exchangeToken accepts. +const ( + grantTypeTokenExchange = "urn:ietf:params:oauth:grant-type:token-exchange" + tokenTypeJWT = "urn:ietf:params:oauth:token-type:jwt" + tokenTypeAccessToken = "urn:ietf:params:oauth:token-type:access_token" +) + +// exchangeToken answers an RFC 8693 token exchange whose audience is the +// subject JWT's only aud with the access token "exchanged..", so a +// suite can see which actor and scope the gateway exchanged for. It does not +// verify the JWT's signature. +func exchangeToken(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + oauthError(w, "invalid_request", err.Error()) + return + } + form := r.PostForm + if form.Get("grant_type") != grantTypeTokenExchange || form.Get("subject_token_type") != tokenTypeJWT || form.Get("requested_token_type") != tokenTypeAccessToken { + oauthError(w, "invalid_request", "want an RFC 8693 exchange of a JWT for an access token") + return + } + var claims struct { + Sub string `json:"sub"` + Aud []string `json:"aud"` + } + parts := strings.Split(form.Get("subject_token"), ".") + if len(parts) != 3 { + oauthError(w, "invalid_grant", "subject_token is not a JWT") + return + } + payload, err := base64.RawURLEncoding.DecodeString(parts[1]) + if err == nil { + err = json.Unmarshal(payload, &claims) + } + if err != nil { + oauthError(w, "invalid_grant", fmt.Sprintf("decoding the subject_token payload: %v", err)) + return + } + if len(claims.Aud) != 1 || claims.Aud[0] != form.Get("audience") { + oauthError(w, "invalid_grant", fmt.Sprintf("subject_token aud %q is not [%q]", claims.Aud, form.Get("audience"))) + return + } + w.Header().Set("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(map[string]any{ + "access_token": "exchanged." + claims.Sub + "." + form.Get("scope"), + "issued_token_type": tokenTypeAccessToken, + "token_type": "Bearer", + "expires_in": 600, + }); err != nil { + log.Printf("testserver http: encoding /token response: %v", err) + } +} + +// oauthError writes an RFC 6749 section 5.2 error response. +func oauthError(w http.ResponseWriter, code, description string) { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusBadRequest) + if err := json.NewEncoder(w).Encode(map[string]string{"error": code, "error_description": description}); err != nil { + log.Printf("testserver http: encoding /token error: %v", err) + } +} + // acceptingConnect answers every CONNECT with 200, as a forward proxy does // when it opens the tunnel, and hands anything else to next. Nothing is // relayed: the server stands in for a proxy only so that a client whose @@ -146,7 +210,7 @@ func newHTTPCmd() *cobra.Command { ) cmd := &cobra.Command{ Use: "http", - Short: "Serve an HTTP/1.1 origin answering /healthz, /echoheaders, POST /fetch and /dial, and with --accept-connect any CONNECT.", + Short: "Serve an HTTP/1.1 origin answering /healthz, /echoheaders, POST /fetch, POST /token and /dial, and with --accept-connect any CONNECT.", Args: cobra.NoArgs, RunE: func(_ *cobra.Command, _ []string) error { handler := newHTTPHandler(body) diff --git a/internal/e2e/fixtures/testserver/http_test.go b/internal/e2e/fixtures/testserver/http_test.go index 8be1d2d79d..6001672392 100644 --- a/internal/e2e/fixtures/testserver/http_test.go +++ b/internal/e2e/fixtures/testserver/http_test.go @@ -18,6 +18,7 @@ import ( "bufio" "crypto/tls" "crypto/x509" + "encoding/base64" "encoding/json" "fmt" "io" @@ -198,6 +199,56 @@ func TestHTTPHandlerFetch(t *testing.T) { }) } +func TestHTTPHandlerExchangesTokens(t *testing.T) { + server := startOriginServer(t, newHTTPHandler(""), "", "") + client := &http.Client{Timeout: 2 * time.Second} + jwt := func(sub string, aud ...string) string { + payload, err := json.Marshal(map[string]any{"sub": sub, "aud": aud}) + if err != nil { + t.Fatal(err) + } + return "e30." + base64.RawURLEncoding.EncodeToString(payload) + ".sig" + } + for _, tt := range []struct { + name string + mutate func(url.Values) + wantStatus int + wantBody string + }{ + {"issues a token naming the subject and scope", nil, http.StatusOK, `"access_token":"exchanged.actor/ns/a.read"`}, + {"rejects another audience", func(v url.Values) { v.Set("audience", "other.example") }, http.StatusBadRequest, `"error":"invalid_grant"`}, + {"rejects a JWT bound to more audiences", func(v url.Values) { v.Set("subject_token", jwt("actor/ns/a", "sts.example", "other.example")) }, http.StatusBadRequest, `"error":"invalid_grant"`}, + {"rejects a subject_token that is not a JWT", func(v url.Values) { v.Set("subject_token", "opaque") }, http.StatusBadRequest, `"error":"invalid_grant"`}, + {"rejects another grant type", func(v url.Values) { v.Set("grant_type", "client_credentials") }, http.StatusBadRequest, `"error":"invalid_request"`}, + } { + t.Run(tt.name, func(t *testing.T) { + form := url.Values{ + "grant_type": {grantTypeTokenExchange}, + "subject_token": {jwt("actor/ns/a", "sts.example")}, + "subject_token_type": {tokenTypeJWT}, + "requested_token_type": {tokenTypeAccessToken}, + "audience": {"sts.example"}, + "scope": {"read"}, + } + if tt.mutate != nil { + tt.mutate(form) + } + resp, err := client.PostForm(server.httpURL+"/token", form) + if err != nil { + t.Fatalf("POST /token: %v", err) + } + defer resp.Body.Close() + body, err := io.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + if resp.StatusCode != tt.wantStatus || !strings.Contains(string(body), tt.wantBody) { + t.Errorf("POST /token = %d %s, want %d with %s", resp.StatusCode, body, tt.wantStatus, tt.wantBody) + } + }) + } +} + func TestHTTPServesCleartextBesideTLS(t *testing.T) { cert := writeOriginCertificate(t) server := startOriginServer(t, newHTTPHandler("both schemes"), cert.certFile, cert.keyFile) diff --git a/internal/e2e/suites/egresscredinject/egresscredinject_test.go b/internal/e2e/suites/egresscredinject/egresscredinject_test.go index f7f51c47a2..c2ac33abe5 100644 --- a/internal/e2e/suites/egresscredinject/egresscredinject_test.go +++ b/internal/e2e/suites/egresscredinject/egresscredinject_test.go @@ -17,8 +17,9 @@ // gateway's MITM leg resolve the credential through the // k8s-credential-provider and replace the actor's placeholder header with it // before re-originating upstream. The same rule also injects an actor JWT -// minted by ateapi. See TestActorEgressCredentialInjection for the proof -// structure and how to run this locally. +// minted by ateapi, and another origin's rules inject a token the gateway +// gets by RFC 8693 token exchange. See TestActorEgressCredentialInjection for +// the proof structure and how to run this locally. package egresscredinject import ( @@ -55,6 +56,23 @@ var echoServer = e2e.ServiceDNSTLS(e2e.ServerPod{ Args: []string{"http"}, }) +// stsServer is a fake RFC 8693 token endpoint, testserver's POST /token, whose +// /echoheaders also shows what the gateway injected. Its rules exchange actor +// JWTs at its own /token, which its https rule allows. +var stsServer = e2e.ServiceDNSTLS(e2e.ServerPod{ + Name: "sts", + ImportPath: "go.ate.dev/substrate/internal/e2e/fixtures/testserver", + Args: []string{"http"}, +}) + +// The sts rules replace exchangedTokenHeader with a token exchanged for +// tokenExchangeAudience and tokenExchangeScope. +const ( + exchangedTokenHeader = "X-Exchanged-Token" + tokenExchangeAudience = "e2e-sts.ate.dev" + tokenExchangeScope = "read" +) + // The hosts of the fail-closed cases. const ( unfetchableHost = "unfetchable.egresscredinject.invalid" @@ -100,10 +118,15 @@ var withPlaceholder = []string{ // secret and for a namespace outside the atespace's authorization // (default-deny), 403 or 500 for a URI naming a provider this gateway // does not serve — over HTTPS and plain HTTP alike. +// - token exchange: a fetch of the sts origin echoes the access token its +// /token issued for this actor's JWT, over HTTPS and plain HTTP. A +// dataplane without token exchange denies it with 403. ateapi rejects a +// policy whose token URL no https rule allows. // -// The echo origin runs in the cluster, so its HTTPS side needs the gateway to -// trust the service-DNS signer its certificate comes from, which -// hack/setup-e2e-egress-tls-kind.sh adds after the install. The install must +// The echo and sts origins run in the cluster, so the gateway has to trust +// the service-DNS signer their certificates come from, for HTTPS and for +// token exchange. hack/setup-e2e-egress-tls-kind.sh adds both after the +// install. The install must // be made with the bundled provider, which deploys the k8s-credential-provider // and points the egress gateway at it. Locally: // @@ -120,13 +143,14 @@ func TestActorEgressCredentialInjection(t *testing.T) { echo := e2e.DeployServerPod(t, ctx, echoServer) // The name the service-DNS signer certifies. echoHost := echo.SvcName() + stsHost := e2e.DeployServerPod(t, ctx, stsServer).SvcName() e2e.ConfigureCredentialProvider(t) probeNamespace, _ = e2e.DeployProbe(t, "egresscredinject", e2e.WithTrustBundle()) const id = "probe-credinject" - createAndResumeActor(t, ctx, clients, id, echoHost) + createAndResumeActor(t, ctx, clients, id, echoHost, stsHost) waitForActorState(t, ctx, clients, id, ateapi.ActorState_ACTOR_STATE_RUNNING) actor, err := clients.SubstrateAPI.GetActor(ctx, &ateapi.GetActorRequest{ Actor: &ateapi.ObjectRef{Atespace: probeNamespace, Name: id}, @@ -174,6 +198,44 @@ func TestActorEgressCredentialInjection(t *testing.T) { }) } + for _, scheme := range []string{"https", "http"} { + t.Run("token exchange/"+scheme, func(t *testing.T) { + origin := scheme + "://" + stsHost + "/echoheaders" + withExchangePlaceholder := []string{"header=" + url.QueryEscape(exchangedTokenHeader+":"+placeholder)} + if !e2e.CurrentAtenetDataplane().InjectsExchangedTokens() { + if got := probeFetch(t, ctx, rc, id, origin, withExchangePlaceholder); got.Status != "403" { + t.Errorf("fetch of %s returned status %q (error %q), want 403 from a dataplane without token exchange", origin, got.Status, got.Error) + } + return + } + echoed := assertEchoedHeaders(t, "token exchange fetch", fetchEcho(t, ctx, rc, id, origin, withExchangePlaceholder)) + want := "Bearer exchanged.actor/" + probeNamespace + "/" + id + "." + tokenExchangeScope + if got := echoed[exchangedTokenHeader]; got != want { + t.Errorf("upstream received %s %q, want %q", exchangedTokenHeader, got, want) + } + }) + } + + t.Run("token URL no https rule allows", func(t *testing.T) { + rule := e2e.EgressAllowHTTPS(stsHost) + rule.Https.Effects = &ateapi.HttpRuleEffects{ReplaceHeaders: []*ateapi.CredentialHeader{ + tokenExchangeInjection("https://sts.egresscredinject.invalid/token"), + }} + // The actor does not exist, so a policy ateapi wrongly accepted + // would still not be stored. + ref := &ateapi.ObjectRef{Atespace: probeNamespace, Name: id + "-rejected"} + _, err := clients.SubstrateAPI.CreateActorEgressPolicy(ctx, &ateapi.CreateActorEgressPolicyRequest{ + Actor: ref, + EgressPolicy: &ateapi.EgressPolicy{ + Metadata: &ateapi.ResourceMetadata{Atespace: probeNamespace, Name: resources.SingletonName}, + Rules: []*ateapi.EgressRule{rule}, + }, + }) + if status.Code(err) != codes.InvalidArgument { + t.Errorf("CreateActorEgressPolicy with a token URL no rule allows: %v, want InvalidArgument", err) + } + }) + // Fail closed: each of these rules names a credential that cannot be // injected, and the denial must be the mapped status, not a request that // went out without the credential. No retries: the gateway answers these @@ -357,8 +419,9 @@ func probeFetch(t *testing.T, ctx context.Context, rc *e2e.RouterClient, id, ori // createAndResumeActor first deletes any actor left by an earlier run, since // actor records outlive the fixture namespace. echoHost is the echo origin's -// name, whose rules inject a credential the provider resolves. -func createAndResumeActor(t *testing.T, ctx context.Context, clients *e2e.Clients, id, echoHost string) { +// name, whose rules inject a credential the provider resolves, and stsHost is +// the sts origin's, whose rules inject an exchanged token. +func createAndResumeActor(t *testing.T, ctx context.Context, clients *e2e.Clients, id, echoHost, stsHost string) { t.Helper() ref := &ateapi.ObjectRef{Atespace: probeNamespace, Name: id} // NotFound is the normal case on a fresh run. Other errors don't stop the @@ -389,7 +452,10 @@ func createAndResumeActor(t *testing.T, ctx context.Context, clients *e2e.Client echoHTTPSRule.Https.Effects.ReplaceHeaders = append(echoHTTPSRule.Https.Effects.ReplaceHeaders, actorJWTInjection()) echoHTTPRule := e2e.EgressInjectHeaderHTTP("Authorization", "Bearer ", e2e.CredentialInjectionURI, echoHost, echoHost+".cluster.local") echoHTTPRule.Http.Effects.ReplaceHeaders = append(echoHTTPRule.Http.Effects.ReplaceHeaders, actorJWTInjection()) - rules := []*ateapi.EgressRule{echoHTTPSRule, echoHTTPRule} + exchange := &ateapi.HttpRuleEffects{ReplaceHeaders: []*ateapi.CredentialHeader{tokenExchangeInjection("https://" + stsHost + "/token")}} + stsHTTPSRule, stsHTTPRule := e2e.EgressAllowHTTPS(stsHost), e2e.EgressAllowHTTP(stsHost) + stsHTTPSRule.Https.Effects, stsHTTPRule.Http.Effects = exchange, exchange + rules := []*ateapi.EgressRule{echoHTTPSRule, echoHTTPRule, stsHTTPSRule, stsHTTPRule} for _, credential := range []struct { host string uri string @@ -420,6 +486,18 @@ func actorJWTInjection() *ateapi.CredentialHeader { } } +func tokenExchangeInjection(tokenURL string) *ateapi.CredentialHeader { + return &ateapi.CredentialHeader{ + Header: exchangedTokenHeader, + Prefix: "Bearer ", + TokenExchange: &ateapi.TokenExchangeSource{ + TokenUrl: tokenURL, + Audience: tokenExchangeAudience, + Scopes: []string{tokenExchangeScope}, + }, + } +} + func waitForActorState(t *testing.T, ctx context.Context, clients *e2e.Clients, actorName string, want ateapi.ActorState) { t.Helper() deadline := time.Now().Add(60 * time.Second) From f42d7908bd053126e859ba3e92d24521e3f51a00 Mon Sep 17 00:00:00 2001 From: Max Thompson Date: Tue, 27 Oct 2026 14:17:24 -0700 Subject: [PATCH 10/10] docs: document injecting RFC 8693 exchanged tokens Egress policies can now exchange an actor JWT for an access token at an RFC 8693 token endpoint and inject it. Document the tokenExchange fields and limits, the rule that an https rule in the same policy must allow the token URL, what the gateway sends and accepts, how long it reuses a token, and the outcomes in the gateway's table. Add the admin setting for endpoints with private certificates, a Google Cloud Workload Identity Federation recipe, and the 127-character google.subject limit that long atespace and actor names hit. Note exchanged tokens in the threat model's credential leak entry, and in the API guide's list of what calls MintActorJwt. Signed-off-by: Max Thompson --- docs/guides/egress-credential-injection.md | 155 +++++++++++++++++++-- docs/reference/api-guide.md | 2 +- docs/security/threat-model.md | 2 +- docs/threats.json | 2 +- 4 files changed, 148 insertions(+), 13 deletions(-) diff --git a/docs/guides/egress-credential-injection.md b/docs/guides/egress-credential-injection.md index b95a55faf9..a8894b4ca8 100644 --- a/docs/guides/egress-credential-injection.md +++ b/docs/guides/egress-credential-injection.md @@ -27,7 +27,9 @@ the secret, nor choose the value that leaves the cluster. The gateway can also inject an **actor JWT** in place of a stored secret: a token that ateapi mints for the actor making the request, which an upstream -that trusts Substrate's issuer can verify. See [Actor JWTs](#actor-jwts). +that trusts Substrate's issuer can verify. See [Actor JWTs](#actor-jwts). Or +it can exchange that JWT at an OAuth 2.0 token endpoint and inject the access +token it gets back. See [Token exchange](#token-exchange). ## When you need this @@ -35,7 +37,8 @@ that trusts Substrate's issuer can verify. See [Actor JWTs](#actor-jwts). Actors call APIs that need bearer tokens or API keys, and those secrets must stay out of the actor's filesystem, environment, and snapshots. Other APIs need to know which actor is calling and accept a signed token that says so; -for those, inject an actor JWT. +for those, inject an actor JWT. APIs that take their own OAuth access tokens, +such as Google Cloud's, can get one for the actor through token exchange. Injection is supported on HTTP and HTTPS requests allowed by an `http` or `https` rule with credential replacement effects. For HTTPS, the actor must @@ -50,12 +53,14 @@ flowchart LR provider["Credential provider"] store[("Secret store")] ateapi["ateapi"] + sts["Token endpoint"] upstream["Upstream API"] actor -->|"HTTPS request,
placeholder header"| gateway gateway -->|"credentialUri:
fetch credential
for this actor"| provider provider -->|"authorize, then read"| store gateway -->|"actorJwt:
mint a JWT
for this actor"| ateapi + gateway -->|"tokenExchange:
exchange that JWT"| sts gateway -->|"request with
the credential"| upstream ``` @@ -76,7 +81,8 @@ flowchart LR For an actor JWT, steps 2 and 3 go to ateapi instead of a credential provider: the gateway asks ateapi to mint a token for the actor, or reuses one it minted -earlier. +earlier. For a token exchange, the gateway then posts that JWT to the token +endpoint and injects the access token it returns. The reference k8s secret provider, `cmd/credential-provider/kubernetes-secrets`, resolves Kubernetes Secrets and is the one [For cluster admins](#for-cluster-admins) @@ -113,6 +119,9 @@ cluster admin grants its atespace access to the `ns1` namespace. * `prefix` is prepended verbatim to the credential — include the separator, e.g. `"Bearer "` with the trailing space. * `credentialUri` names the credential; see [Credential URIs](#credential-uris). +* An entry sets exactly one of `credentialUri`, `actorJwt` + ([Actor JWTs](#actor-jwts)), and `tokenExchange` + ([Token exchange](#token-exchange)). ### Credential URIs @@ -155,7 +164,6 @@ rules: against. * `expirationSeconds` is the token's lifetime, from 300 to 3600 seconds, with no default. -* An entry sets exactly one of `credentialUri` and `actorJwt`. The token's `sub` is `actor//`, and its `iss` is the issuer the install gave ateapi (`ACTOR_JWT_ISSUER`). It also names the actor's @@ -168,6 +176,101 @@ third of its lifetime is left, so the upstream always gets a token with at least that much time remaining: 300 seconds for the 900-second lifetime above. Concurrent requests that need the same token share one mint. +### Token exchange + +An entry can set `tokenExchange` instead. The gateway mints an actor JWT, +exchanges it at an OAuth 2.0 token exchange endpoint +([RFC 8693](https://www.rfc-editor.org/rfc/rfc8693)), and puts the prefix and +the returned access token into the header. Like `actorJwt`, it needs no +credential provider. + +```yaml +rules: +- https: + hostnames: ["sts.googleapis.com"] +- https: + hostnames: ["storage.googleapis.com"] + effects: + replaceHeaders: + - header: Authorization + prefix: "Bearer " + tokenExchange: + tokenUrl: https://sts.googleapis.com/v1/token + audience: //iam.googleapis.com/projects/123/locations/global/workloadIdentityPools/ate/providers/actors + scopes: ["https://www.googleapis.com/auth/cloud-platform"] +``` + +* `tokenUrl` is the endpoint, up to 2048 characters: an `https` URL with a + DNS hostname and no userinfo, query, or fragment. An `https` rule in the + same policy must allow its host and port, which is 443 unless the URL names + one. ateapi rejects a policy without one, and the gateway checks again on + each request, which is why the first rule above exists. +* `audience` becomes the actor JWT's only `aud` and the request's `audience` + parameter, up to 512 characters. +* `scopes` is optional: up to 16 distinct values, each up to 256 printable + ASCII characters without spaces, `"`, or `\`. They go in one + space-separated `scope` parameter. +* A policy can have at most 16 `tokenExchange` entries. + +The gateway posts an RFC 8693 request with +`grant_type=urn:ietf:params:oauth:grant-type:token-exchange` and +`requested_token_type=urn:ietf:params:oauth:token-type:access_token`. The +`subject_token` is an actor JWT with a 300-second lifetime, of type +`urn:ietf:params:oauth:token-type:jwt`. The gateway sends no client +credentials and no `actor_token`, so it works only with endpoints that accept +an unauthenticated exchange. Endpoints that require client authentication +aren't supported, and neither are AWS STS and Microsoft Entra, which use their +own protocols. The gateway follows no redirects and ignores proxy environment +variables. It accepts a response only with a non-empty `access_token` of +printable ASCII and, when `issued_token_type` is present, the access token +type. It verifies the endpoint against the system roots, plus any CA an admin +adds (see [Private token endpoints](#private-token-endpoints)). + +The gateway reuses an access token for the same actor, `tokenUrl`, `audience`, +and scopes until a third of its lifetime or 30 seconds remains, whichever +comes first, and for 60 seconds when the response has no `expires_in`. It +doesn't cache a token with 30 seconds or less to live. Concurrent requests +that need the same token share one exchange. A request waits at most four +seconds for it, but the exchange keeps going for up to ten, so a slow +endpoint still fills the cache for the next request. + +#### Google Cloud Workload Identity Federation + +Google Cloud's Security Token Service accepts an unauthenticated exchange. To +use the policy above: + +1. Create a workload identity pool and an OIDC provider whose issuer is the + actor JWT issuer, set by the `actorJWT.issuer` install setting (see + [operator-install.md](operator-install.md)). Google Cloud reads the signing + keys through the issuer's discovery document. If the issuer isn't publicly + reachable, as the default `https://idp..svc` isn't, upload the + JWK set from `/openid/v1/jwks` instead. An upload holds at most 8 + keys and replaces the previous one, so upload again after + `kubectl ate admin add-jwt-key` and before activating the new key (see the + [kubectl-ate README](../reference/kubectl-ate.md)). +2. Allow the audience + `//iam.googleapis.com/projects//locations/global/workloadIdentityPools//providers/` + as a custom audience of the provider, and use the same string as + `audience`. The gateway sends it as both the JWT's `aud` and the STS + `audience` parameter, which takes this form. +3. Map `google.subject=assertion.sub` and + `attribute.atespace=assertion.sub.split("/")[1]`. Google Cloud limits + `google.subject` to 127 characters, while the `sub`, + `actor//`, can reach 133: atespace and actor names can + each be 63. The mapping works only when the atespace and actor name total + 120 characters or less. Longer ones fail the exchange. +4. Grant roles to + `principal://iam.googleapis.com/projects//locations/global/workloadIdentityPools//subject/actor//` + for one actor, or to + `principalSet://iam.googleapis.com/projects//locations/global/workloadIdentityPools//attribute.atespace/` + for an atespace. Don't grant the whole pool. Any atespace can have the + gateway mint its actors a JWT for any audience, so the audience doesn't + keep other tenants out. + +STS requires a scope, such as `https://www.googleapis.com/auth/cloud-platform`. +It returns a federated token, which works with services that support direct +resource access. The gateway doesn't impersonate a service account. + ## What the gateway does | Situation | Outcome | @@ -183,6 +286,12 @@ Concurrent requests that need the same token share one mint. | `actorJwt` entry, the actor no longer exists | **403**, fail closed | | `actorJwt` entry, ateapi unreachable or timed out | **503**, fail closed but retryable | | `actorJwt` entry, any other mint failure | **500** (Envoy) or **403** (agentgateway), fail closed | +| Request decided by an `http` or `https` rule, carries the header of a `tokenExchange` entry, exchange succeeds | Header value replaced with the prefix and the access token, with or without a provider | +| `tokenExchange` entry whose `tokenUrl` no `https` rule in the policy allows | **500**, fail closed, no exchange | +| `tokenExchange` entry, the actor no longer exists, or the endpoint refuses the exchange (`invalid_grant`, `invalid_target`, `unauthorized_client`, `invalid_scope`, or `access_denied` with a 400 or 401) | **403**, fail closed | +| `tokenExchange` entry, ateapi or the endpoint unreachable or timed out, including a TLS failure; the endpoint answers 429 or 5xx, or a 200 without a usable access token | **503**, fail closed but retryable | +| `tokenExchange` entry, any other endpoint answer, including other 4xx errors and redirects, or any other mint failure | **500**, fail closed | +| `tokenExchange` entry on the agentgateway dataplane | **403**, fail closed; agentgateway doesn't exchange tokens | A request that does not carry the header skips injection. On an HTTP or HTTPS request that carries it, any failure to produce the credential, including @@ -277,8 +386,33 @@ kubectl apply -f manifests/egress-credential-injection/sample-secret.yaml `http` or `https` rules, as described under [The policy](#the-policy). An actor with `https` rules also needs the projected egress trust bundle to do TLS through the gateway at all (see -[egress-trust-bundle.md](egress-trust-bundle.md)). Actor JWT entries need no -provider; ateapi signs them with a key that every install creates. +[egress-trust-bundle.md](egress-trust-bundle.md)). Actor JWT and token +exchange entries need no provider; ateapi signs the JWTs with a key that every +install creates. + +### Private token endpoints + +The gateway verifies token exchange endpoints against the system roots. For +an endpoint with a private certificate, put its CA certificates under key +`ca.crt` in a ConfigMap in the install namespace and name it at install time: + +```bash +kubectl -n ate-system create configmap token-exchange-ca --from-file=ca.crt=corp-root-ca.pem +go run ./cmd/ate-setup deploy atenet --token-exchange-ca-configmap=token-exchange-ca +``` + +Pass the flag with the rest of your install settings. + +The installer mounts the ConfigMap into the gateway, which trusts these +certificates in addition to the system roots. The installer doesn't create +the ConfigMap, and the install fails if it or its `ca.crt` key is missing. +The setting needs the Envoy dataplane; see +[commands.md](../../cmd/ate-setup/commands.md). The gateway reads the +certificates at startup, so restart it after changing the ConfigMap: + +```bash +kubectl -n ate-system rollout restart deployment/atenet-egress +``` ### Verify @@ -308,10 +442,11 @@ and recreated, the channel can sit in connect backoff for up to a couple of minutes before re-resolving; injection fails closed with 503 (deliberately retryable) until it reconnects. -**Actor JWTs follow the actor's name.** The gateway caches tokens by actor -name. An actor that is deleted and recreated under the same name can get its -predecessor's token until that token's refresh point, up to two thirds of its -lifetime, and that token's UID claim names the old actor. +**Actor JWTs and exchanged tokens follow the actor's name.** The gateway +caches both by actor name. An actor that is deleted and recreated under the +same name can get its predecessor's token until that token's refresh point, +up to two thirds of its lifetime, or 60 seconds for an exchanged token without +`expires_in`. An actor JWT's UID claim then names the old actor. **Scope of the reference provider's RBAC.** The sample deployment grants read on Secrets cluster-wide so the policy may name any namespace; a production diff --git a/docs/reference/api-guide.md b/docs/reference/api-guide.md index e8baa8c878..21db462919 100644 --- a/docs/reference/api-guide.md +++ b/docs/reference/api-guide.md @@ -821,7 +821,7 @@ Query the physical resource pool. ate-api-server mints credentials that identify an actor to services outside Substrate. They name the logical actor, so they stay the same as the actor moves between workers. This is distinct from the `actorMetadata` data source described under [SystemInfo Volumes](#systeminfo-volumes), which only tells an actor its own identity fields (name, atespace, uid). -`MintActorJwt` is on `substrate.ateapi.v1.Control`. Only the egress gateway may call it; see [authorization.md](../guides/authorization.md). It calls this RPC when an egress policy injects an actor JWT into a request. +`MintActorJwt` is on `substrate.ateapi.v1.Control`. Only the egress gateway may call it; see [authorization.md](../guides/authorization.md). It calls this RPC when an egress policy injects an actor JWT into a request, or exchanges one for an access token. ### `MintActorJwt` diff --git a/docs/security/threat-model.md b/docs/security/threat-model.md index 99c714aab2..f80b75fe8c 100644 --- a/docs/security/threat-model.md +++ b/docs/security/threat-model.md @@ -156,7 +156,7 @@ Substrate is expected to sit behind the user's service, not on the internet, so | T-26 | Critical | Stale policies propagated out-of-band with actor scheduling result in incorrect access boundaries (including network policies, IAM permissions, etc.) | An actor's network and authorization policy is in effect before it starts running. Enforcement points may cache policy, as long as a policy change takes effect within a short time. An enforcement point that can't get current policy denies rather than serving stale policy indefinitely. | Program authz and network policy as part of actor create and resume, or fetch it on first use and deny until it arrives. Short cache TTLs. Invalidate caches on policy change where it's cheap. | Caching is required for performance, since enforcement points check policy on every connection or query. Operators may tune cache TTLs. Policy applies to new connections; a policy change doesn't cut connections that are already open. Credentials already issued are limited by their own lifetime (T-28), not by policy propagation. | | T-27 | Critical | Worker reuse enables malicious actor to escalate to other actors by persisting a threat across reuse, reading leftover state from a previous actor, or taking advantage of stale policy configuration. | All actor-specific worker state, including process state, filesystem, env vars, mounted config, network policy, and security policy, must be completely reset between actors that subsequently run on the same worker. | Synchronize local policy updates with actor lifecycle. Test cleanup across suspend/resume for each sandbox type, with honeypots on both sides. | | | T-28 | Critical | Malicious actor tricks Substrate identity broker into returning identity credentials for a different actor. | Add defense-in-depth to ensure that credentials cannot be mis-issued, and that mis-issued credentials are unusable in practice. | Check the actor's worker assignment before issuing credentials. Bind credentials to the worker and node, check those claims in authz, and invalidate old credentials on reschedule. | | -| T-29 | High | Agent leaks credentials exposed in sandbox, because LLMs are unreliable. Due to prompt injection or just agent silliness. | Credentials are not exposed in sandboxes by default. | No credentials in sandboxes by default. Inject credentials at a proxy. Delegate to drivers (like CSI) rather than sandboxed clients. Credentials that must enter the sandbox are wrapped and only usable through the proxy. | Exposing credentials can be opt-in when the process is not an AI agent or can't read them. | +| T-29 | High | Agent leaks credentials exposed in sandbox, because LLMs are unreliable. Due to prompt injection or just agent silliness. | Credentials are not exposed in sandboxes by default. | No credentials in sandboxes by default. Inject credentials at a proxy. Delegate to drivers (like CSI) rather than sandboxed clients. Credentials that must enter the sandbox are wrapped and only usable through the proxy. | Exposing credentials can be opt-in when the process is not an AI agent or can't read them. Token exchange (`tokenExchange`) keeps exchanged access tokens in the egress gateway process, like other injected credentials. The token URL must be a host the policy's `https` rules allow, and the gateway follows no redirects. | | T-30 | Medium | Malicious actor moves laterally to other nodes via suspend/resume, especially if self-suspend is allowed. | Actor isolation doesn't depend on which node an actor runs on. Admins can confine actors to specific node pools. | Let admins restrict which node pools an actor can resume on. | Actors move between workers by design, so nodes aren't a boundary between actors. This only matters combined with a sandbox, network, or filesystem escape. | | T-31 | High | DoS/resource exhaustion via malicious container image | Enforce limits when pulling and extracting container images | Limit uncompressed layer size and unpack time. | Example: a zip bomb. | | T-32 | Medium | Compromised process inside actor uses actor identity to write its own snapshot, enabling local privilege escalation (still in actor sandbox). | Issue separate credentials for accessing snapshots. | Snapshot access requires an extra claim, audience, or second identity proving atelet is acting for the actor. | Medium because it's still a local exploit inside the sandbox. Example: a nonroot process rewrites the snapshot so it can `su` to root after restore. | diff --git a/docs/threats.json b/docs/threats.json index afdccd6e9a..07946aed77 100644 --- a/docs/threats.json +++ b/docs/threats.json @@ -221,7 +221,7 @@ "threat": "Agent leaks credentials exposed in sandbox, because LLMs are unreliable. Due to prompt injection or just agent silliness.", "mitigating_invariants": "Credentials are not exposed in sandboxes by default.", "suggested_concrete_mitigations": "No credentials in sandboxes by default. Inject credentials at a proxy. Delegate to drivers (like CSI) rather than sandboxed clients. Credentials that must enter the sandbox are wrapped and only usable through the proxy.", - "notes": "Exposing credentials can be opt-in when the process is not an AI agent or can't read them." + "notes": "Exposing credentials can be opt-in when the process is not an AI agent or can't read them. Token exchange (`tokenExchange`) keeps exchanged access tokens in the egress gateway process, like other injected credentials. The token URL must be a host the policy's `https` rules allow, and the gateway follows no redirects." }, { "threat_id": "T-30",