Repository: akuity/kargo
Pull request #612 (2023-08-30 05:25:59)

feat: add PromoteSubscribers API/CLI
1. Add PromoteSubscribers API/CLI, which accepts a Stage and one of its historical freights. It then identifies all subscribers of that Stage and creates Promotion objects for each of them.

CLI usage:
```
$ kargo stage promote-subscribers kargo-guestbook staging --freight=c353927ca7af42b38c0cdcfa393b2c552740e547
Promotion Created: "prod-use1.01h92dysvxkx7nmhptkhgtsd28.c353927"
Promotion Created: "prod-usw1.01h92dyswv62mr2jx8rrsn4z40.c353927"
Promotion Created: "prod-euw1.01h92dysxe2y02mqzhxk9ehrq6.c353927"
```

2. Promotions objects are now consistently named with the following convention (previously, promotions names were different between auto-promote vs. API promote)
```
<stagename>.<ulid>.<7-character-freight-id>
```

A [ULID](https://github.com/ulid/spec) is basically a UUID, but uses less characters, and lexographically sorted so that when listing, the most recent will appear in the last. 

Example:
```
$ k get promos  -w
NAME                                           STAGE       STATE                                      PHASE       AGE
dev.01h92e6wcfws9x3pjeg0dsva0d.f08b2e7         dev         f08b2e72c9b2b7b263da6d55f9536e49b5ce972c               8m43s
prod-euw1.01h92dysxe2y02mqzhxk9ehrq6.c353927   prod-euw1   c353927ca7af42b38c0cdcfa393b2c552740e547   Completed   13m
prod-use1.01h92dysvxkx7nmhptkhgtsd28.c353927   prod-use1   c353927ca7af42b38c0cdcfa393b2c552740e547   Completed   13m
prod-usw1.01h92dyswv62mr2jx8rrsn4z40.c353927   prod-usw1   c353927ca7af42b38c0cdcfa393b2c552740e547   Completed   13m
```

3. Eliminated some repeated copy-and-paste code in API handlers with a helper

netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *docs-kargo-akuity-io* processing.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | aa2adde7c10d8eff494b278a07b60d177b85a77e |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/docs-kargo-akuity-io/deploys/64eed2e9663c1400083c212f |
jessesuen: I am missing unit tests for this, but please start on the review while I work on that.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity) Report
Patch coverage: **`38.09%`** and project coverage change: **`-0.55%`** :warning:
> Comparison is base [(`8aa3244`)](https://app.codecov.io/gh/akuity/kargo/commit/8aa3244f58885ca6a29d52a6db79e02bb616053f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity) 46.22% compared to head [(`aa2adde`)](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity) 45.68%.
> Report is 1 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #612      +/-   ##
==========================================
- Coverage   46.22%   45.68%   -0.55%     
==========================================
  Files          79       81       +2     
  Lines        5713     5742      +29     
==========================================
- Hits         2641     2623      -18     
- Misses       2938     2986      +48     
+ Partials      134      133       -1     
```


| [Files Changed](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity) | Coverage Δ | |
|---|---|---|
| [internal/api/delete\_stage\_v1alpha1.go](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL2RlbGV0ZV9zdGFnZV92MWFscGhhMS5nbw==) | `0.00% <0.00%> (ø)` | |
| [internal/api/get\_promotion\_policy\_v1alpha1.go](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL2dldF9wcm9tb3Rpb25fcG9saWN5X3YxYWxwaGExLmdv) | `0.00% <0.00%> (ø)` | |
| [internal/api/promote\_subscribers\_v1alpha1.go](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL3Byb21vdGVfc3Vic2NyaWJlcnNfdjFhbHBoYTEuZ28=) | `0.00% <0.00%> (ø)` | |
| [internal/api/promote\_stage\_v1alpha1.go](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL3Byb21vdGVfc3RhZ2VfdjFhbHBoYTEuZ28=) | `57.14% <75.00%> (-24.34%)` | :arrow_down: |
| [internal/api/helpers.go](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL2hlbHBlcnMuZ28=) | `92.30% <92.30%> (ø)` | |
| [internal/api/get\_stage\_v1alpha1.go](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL2dldF9zdGFnZV92MWFscGhhMS5nbw==) | `73.68% <100.00%> (-3.59%)` | :arrow_down: |
| [internal/api/refresh\_stage\_v1alpha1.go](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL3JlZnJlc2hfc3RhZ2VfdjFhbHBoYTEuZ28=) | `83.33% <100.00%> (-2.39%)` | :arrow_down: |
| [internal/api/validators.go](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL3ZhbGlkYXRvcnMuZ28=) | `82.14% <100.00%> (+27.59%)` | :arrow_up: |
| [internal/controller/stages/stages.go](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvY29udHJvbGxlci9zdGFnZXMvc3RhZ2VzLmdv) | `48.40% <100.00%> (-0.58%)` | :arrow_down: |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/akuity/kargo/pull/612?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity).


Patch:
diff --git a/api/service/v1alpha1/service.proto b/api/service/v1alpha1/service.proto
index 73758316..cb4e52a0 100644
--- a/api/service/v1alpha1/service.proto
+++ b/api/service/v1alpha1/service.proto
@@ -30,6 +30,7 @@ service KargoService {
   rpc UpdateStage(UpdateStageRequest) returns (UpdateStageResponse);
   rpc DeleteStage(DeleteStageRequest) returns (DeleteStageResponse);
   rpc PromoteStage(PromoteStageRequest) returns (PromoteStageResponse);
+  rpc PromoteSubscribers(PromoteSubscribersRequest) returns (PromoteSubscribersResponse);
   rpc RefreshStage(RefreshStageRequest) returns (RefreshStageResponse);
 
   /* Promotion APIs */
@@ -214,6 +215,16 @@ message PromoteStageResponse {
   github.com.akuity.kargo.pkg.api.v1alpha1.Promotion promotion = 1;
 }
 
+message PromoteSubscribersRequest {
+  string project = 1;
+  string stage = 2;
+  string freight = 3;
+}
+
+message PromoteSubscribersResponse {
+  repeated github.com.akuity.kargo.pkg.api.v1alpha1.Promotion promotions = 1;
+}
+
 message RefreshStageRequest {
   string project = 1;
   string name = 2;
diff --git a/go.mod b/go.mod
index d460a627..a4761490 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,7 @@ require (
 	github.com/google/uuid v1.3.1
 	github.com/kelseyhightower/envconfig v1.4.0
 	github.com/pkg/errors v0.9.1
+	github.com/oklog/ulid/v2 v2.1.0
 	github.com/samber/mo v1.8.0
 	github.com/sirupsen/logrus v1.9.0
 	github.com/spf13/cobra v1.6.1
diff --git a/go.sum b/go.sum
index 049c06e0..b135c37e 100644
--- a/go.sum
+++ b/go.sum
@@ -877,6 +877,8 @@ github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+
 github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
 github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
+github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
 github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
 github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852/go.mod h1:eqOVx5Vwu4gd2mmMZvVZsgIqNSaW3xxRThUJ0k/TPk4=
@@ -923,6 +925,7 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
 github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
+github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
 github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
diff --git a/internal/api/delete_stage_v1alpha1.go b/internal/api/delete_stage_v1alpha1.go
index aff1ea48..8472d282 100644
--- a/internal/api/delete_stage_v1alpha1.go
+++ b/internal/api/delete_stage_v1alpha1.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 
 	"connectrpc.com/connect"
-	"github.com/pkg/errors"
 	kubeerr "k8s.io/apimachinery/pkg/api/errors"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
@@ -17,11 +16,8 @@ func (s *server) DeleteStage(
 	ctx context.Context,
 	req *connect.Request[svcv1alpha1.DeleteStageRequest],
 ) (*connect.Response[svcv1alpha1.DeleteStageResponse], error) {
-	if req.Msg.GetProject() == "" {
-		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("project should not be empty"))
-	}
-	if req.Msg.GetName() == "" {
-		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("name should not be empty"))
+	if err := validateProjectAndStageNonEmpty(req.Msg.GetProject(), req.Msg.GetName()); err != nil {
+		return nil, err
 	}
 	if err := s.validateProject(ctx, req.Msg.GetProject()); err != nil {
 		return nil, err
diff --git a/internal/api/get_promotion_policy_v1alpha1.go b/internal/api/get_promotion_policy_v1alpha1.go
index 6316b68e..48a608d5 100644
--- a/internal/api/get_promotion_policy_v1alpha1.go
+++ b/internal/api/get_promotion_policy_v1alpha1.go
@@ -4,7 +4,6 @@ import (
 	"context"
 
 	"connectrpc.com/connect"
-	"github.com/pkg/errors"
 	kubeerr "k8s.io/apimachinery/pkg/api/errors"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
@@ -17,12 +16,10 @@ func (s *server) GetPromotionPolicy(
 	ctx context.Context,
 	req *connect.Request[svcv1alpha1.GetPromotionPolicyRequest],
 ) (*connect.Response[svcv1alpha1.GetPromotionPolicyResponse], error) {
-	if req.Msg.GetProject() == "" {
-		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("project should not be empty"))
-	}
-	if req.Msg.GetName() == "" {
-		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("name should not be empty"))
+	if err := validateProjectAndStageNonEmpty(req.Msg.GetProject(), req.Msg.GetName()); err != nil {
+		return nil, err
 	}
+
 	if err := s.validateProject(ctx, req.Msg.GetProject()); err != nil {
 		return nil, err
 	}
diff --git a/internal/api/get_stage_v1alpha1.go b/internal/api/get_stage_v1alpha1.go
index 3ddd6310..7b14f572 100644
--- a/internal/api/get_stage_v1alpha1.go
+++ b/internal/api/get_stage_v1alpha1.go
@@ -4,7 +4,6 @@ import (
 	"context"
 
 	"connectrpc.com/connect"
-	"github.com/pkg/errors"
 	kubeerr "k8s.io/apimachinery/pkg/api/errors"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
@@ -17,12 +16,10 @@ func (s *server) GetStage(
 	ctx context.Context,
 	req *connect.Request[svcv1alpha1.GetStageRequest],
 ) (*connect.Response[svcv1alpha1.GetStageResponse], error) {
-	if req.Msg.GetProject() == "" {
-		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("project should not be empty"))
-	}
-	if req.Msg.GetName() == "" {
-		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("name should not be empty"))
+	if err := validateProjectAndStageNonEmpty(req.Msg.GetProject(), req.Msg.GetName()); err != nil {
+		return nil, err
 	}
+
 	if err := s.validateProject(ctx, req.Msg.GetProject()); err != nil {
 		return nil, err
 	}
diff --git a/internal/api/helpers.go b/internal/api/helpers.go
new file mode 100644
index 00000000..10c2e79d
--- /dev/null
+++ b/internal/api/helpers.go
@@ -0,0 +1,30 @@
+package api
+
+import (
+	"context"
+	"fmt"
+
+	"connectrpc.com/connect"
+	kubeerr "k8s.io/apimachinery/pkg/api/errors"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	kubev1alpha1 "github.com/akuity/kargo/api/v1alpha1"
+	"github.com/akuity/kargo/internal/api/kubernetes"
+)
+
+// getStage is a helper to get a stage by namespace/name
+func getStage(ctx context.Context, kc kubernetes.Client, project string, name string) (*kubev1alpha1.Stage, error) {
+	var stage kubev1alpha1.Stage
+	objKey := client.ObjectKey{
+		Namespace: project,
+		Name:      name,
+	}
+	err := kc.Get(ctx, objKey, &stage)
+	if err != nil {
+		if kubeerr.IsNotFound(err) {
+			return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("stage %q not found", project))
+		}
+		return nil, connect.NewError(connect.CodeInternal, err)
+	}
+	return &stage, nil
+}
diff --git a/internal/api/promote_stage_v1alpha1.go b/internal/api/promote_stage_v1alpha1.go
index b0c8b23f..e52a118c 100644
--- a/internal/api/promote_stage_v1alpha1.go
+++ b/internal/api/promote_stage_v1alpha1.go
@@ -2,17 +2,11 @@ package api
 
 import (
 	"context"
-	"fmt"
 
 	"connectrpc.com/connect"
-	"github.com/pkg/errors"
-	kubeerr "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/utils/pointer"
-	"sigs.k8s.io/controller-runtime/pkg/client"
 
-	kubev1alpha1 "github.com/akuity/kargo/api/v1alpha1"
 	typesv1alpha1 "github.com/akuity/kargo/internal/api/types/v1alpha1"
+	"github.com/akuity/kargo/internal/kargo"
 	svcv1alpha1 "github.com/akuity/kargo/pkg/api/service/v1alpha1"
 )
 
@@ -20,63 +14,25 @@ func (s *server) PromoteStage(
 	ctx context.Context,
 	req *connect.Request[svcv1alpha1.PromoteStageRequest],
 ) (*connect.Response[svcv1alpha1.PromoteStageResponse], error) {
-	if req.Msg.GetProject() == "" {
-		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("project should not be empty"))
-	}
-	if req.Msg.GetState() == "" {
-		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("state should not be empty"))
+	if err := validateProjectAndStageNonEmpty(req.Msg.GetProject(), req.Msg.GetName()); err != nil {
+		return nil, err
 	}
 	if err := s.validateProject(ctx, req.Msg.GetProject()); err != nil {
 		return nil, err
 	}
-
-	var stage kubev1alpha1.Stage
-	if err := s.client.Get(ctx, client.ObjectKey{
-		Namespace: req.Msg.GetProject(),
-		Name:      req.Msg.GetName(),
-	}, &stage); err != nil {
-		if kubeerr.IsNotFound(err) {
-			return nil, connect.NewError(connect.CodeNotFound,
-				fmt.Errorf("stage %q not found", req.Msg.GetName()))
-		}
-		return nil, connect.NewError(connect.CodeInternal, err)
-	}
-
-	stateExists := false
-	for _, state := range stage.Status.AvailableStates {
-		if req.Msg.GetState() == state.ID {
-			stateExists = true
-			break
-		}
+	stage, err := getStage(ctx, s.client, req.Msg.GetProject(), req.Msg.GetName())
+	if err != nil {
+		return nil, err
 	}
-	if !stateExists {
-		return nil, connect.NewError(connect.CodeNotFound,
-			fmt.Errorf("state %q not found in Stage %q", req.Msg.GetState(), stage.Name))
+	if _, err = validateFreightExists(req.Msg.GetState(), stage.Status.AvailableStates); err != nil {
+		return nil, err
 	}
 
-	promotion := &kubev1alpha1.Promotion{
-		ObjectMeta: metav1.ObjectMeta{
-			GenerateName: fmt.Sprintf("%s-", req.Msg.GetName()),
-			Namespace:    req.Msg.GetProject(),
-			OwnerReferences: []metav1.OwnerReference{
-				{
-					APIVersion:         kubev1alpha1.GroupVersion.String(),
-					Kind:               "Stage",
-					Name:               stage.Name,
-					UID:                stage.UID,
-					BlockOwnerDeletion: pointer.Bool(true),
-				},
-			},
-		},
-		Spec: &kubev1alpha1.PromotionSpec{
-			Stage: stage.Name,
-			State: req.Msg.GetState(),
-		},
-	}
-	if err := s.client.Create(ctx, promotion); err != nil {
+	promotion := kargo.NewPromotion(*stage, req.Msg.GetState())
+	if err := s.client.Create(ctx, &promotion); err != nil {
 		return nil, connect.NewError(connect.CodeInternal, err)
 	}
 	return connect.NewResponse(&svcv1alpha1.PromoteStageResponse{
-		Promotion: typesv1alpha1.ToPromotionProto(*promotion),
+		Promotion: typesv1alpha1.ToPromotionProto(promotion),
 	}), nil
 }
diff --git a/internal/api/promote_subscribers_v1alpha1.go b/internal/api/promote_subscribers_v1alpha1.go
new file mode 100644
index 00000000..3bfe926e
--- /dev/null
+++ b/internal/api/promote_subscribers_v1alpha1.go
@@ -0,0 +1,98 @@
+package api
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	"connectrpc.com/connect"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	kubev1alpha1 "github.com/akuity/kargo/api/v1alpha1"
+	typesv1alpha1 "github.com/akuity/kargo/internal/api/types/v1alpha1"
+	"github.com/akuity/kargo/internal/kargo"
+	"github.com/akuity/kargo/internal/logging"
+	svcv1alpha1 "github.com/akuity/kargo/pkg/api/service/v1alpha1"
+	v1alpha1 "github.com/akuity/kargo/pkg/api/v1alpha1"
+)
+
+func (s *server) PromoteSubscribers(
+	ctx context.Context,
+	req *connect.Request[svcv1alpha1.PromoteSubscribersRequest],
+) (*connect.Response[svcv1alpha1.PromoteSubscribersResponse], error) {
+	if err := validateProjectAndStageNonEmpty(req.Msg.GetProject(), req.Msg.GetStage()); err != nil {
+		return nil, err
+	}
+	if err := s.validateProject(ctx, req.Msg.GetProject()); err != nil {
+		return nil, err
+	}
+	stage, err := getStage(ctx, s.client, req.Msg.GetProject(), req.Msg.GetStage())
+	if err != nil {
+		return nil, err
+	}
+	freightToPromote, err := validateFreightExists(req.Msg.GetFreight(), stage.Status.History)
+	if err != nil {
+		return nil, err
+	}
+	// TODO: this may also need to include Not Applicable if/when that state is introduced
+	if freightToPromote.Health != nil && freightToPromote.Health.Status != kubev1alpha1.HealthStateHealthy {
+		return nil, connect.NewError(connect.CodeInvalidArgument,
+			fmt.Errorf("Cannot promote freight with health status: %s", freightToPromote.Health.Status))
+	}
+
+	subscribers, err := s.findStageSubscribers(ctx, stage)
+	if err != nil {
+		return nil, err
+	}
+	if len(subscribers) == 0 {
+		return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("Stage %q has no subscribers", req.Msg.GetStage()))
+	}
+
+	logger := logging.LoggerFromContext(ctx)
+
+	var promoteErrs []error
+	var createdPromos []*v1alpha1.Promotion
+	for _, subscriber := range subscribers {
+		if _, err := validateFreightExists(req.Msg.GetFreight(), subscriber.Status.AvailableStates); err != nil {
+			// TODO(JS): currently we create promotions to all of this Stage's subscribers, ignoring
+			// whether or not the freight *also* appears in the availableStates of the subscriber.
+			// Normally, it should always be the case that if it's in our history, it should also
+			// appear in our subscriber's availableStates. For now, just log a warning if we are
+			// promoting something that for some reason, has not yet appeared there.
+			logger.Warnf("Freight '%s' does not appear in availableStates of '%s'", req.Msg.GetFreight(), subscriber.Name)
+		}
+		newPromo := kargo.NewPromotion(subscriber, req.Msg.GetFreight())
+		if err := s.client.Create(ctx, &newPromo); err != nil {
+			promoteErrs = append(promoteErrs, err)
+			continue
+		}
+		createdPromos = append(createdPromos, typesv1alpha1.ToPromotionProto(newPromo))
+	}
+
+	return connect.NewResponse(&svcv1alpha1.PromoteSubscribersResponse{
+		Promotions: createdPromos,
+	}), errors.Join(promoteErrs...)
+}
+
+// findStageSubscribers returns a list of Stages that are subscribed to the given Stage
+// TODO: this could be powered by an index.
+func (s *server) findStageSubscribers(ctx context.Context, stage *kubev1alpha1.Stage) ([]kubev1alpha1.Stage, error) {
+	var allStages kubev1alpha1.StageList
+	if err := s.client.List(ctx, &allStages, client.InNamespace(stage.Namespace)); err != nil {
+		return nil, connect.NewError(connect.CodeInternal, err)
+	}
+	var subscribers []kubev1alpha1.Stage
+	for _, s := range allStages.Items {
+		s := s
+		if s.Spec.Subscriptions == nil {
+			continue
+		}
+		for _, upstream := range s.Spec.Subscriptions.UpstreamStages {
+			if upstream.Name != stage.Name {
+				continue
+			}
+			subscribers = append(subscribers, s)
+		}
+	}
+	return subscribers, nil
+}
diff --git a/internal/api/refresh_stage_v1alpha1.go b/internal/api/refresh_stage_v1alpha1.go
index d7d3c311..8f8840d1 100644
--- a/internal/api/refresh_stage_v1alpha1.go
+++ b/internal/api/refresh_stage_v1alpha1.go
@@ -4,7 +4,6 @@ import (
 	"context"
 
 	"connectrpc.com/connect"
-	"github.com/pkg/errors"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	kubev1alpha1 "github.com/akuity/kargo/api/v1alpha1"
@@ -16,11 +15,8 @@ func (s *server) RefreshStage(
 	ctx context.Context,
 	req *connect.Request[svcv1alpha1.RefreshStageRequest],
 ) (*connect.Response[svcv1alpha1.RefreshStageResponse], error) {
-	if req.Msg.GetProject() == "" {
-		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("project should not be empty"))
-	}
-	if req.Msg.GetName() == "" {
-		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("name should not be empty"))
+	if err := validateProjectAndStageNonEmpty(req.Msg.GetProject(), req.Msg.GetName()); err != nil {
+		return nil, err
 	}
 	if err := s.validateProject(ctx, req.Msg.GetProject()); err != nil {
 		return nil, err
diff --git a/internal/api/validators.go b/internal/api/validators.go
index 10a434df..24810d13 100644
--- a/internal/api/validators.go
+++ b/internal/api/validators.go
@@ -2,11 +2,13 @@ package api
 
 import (
 	"context"
+	"fmt"
 
 	"connectrpc.com/connect"
 	"github.com/pkg/errors"
 	"k8s.io/apimachinery/pkg/util/validation/field"
 
+	kubev1alpha1 "github.com/akuity/kargo/api/v1alpha1"
 	"github.com/akuity/kargo/internal/api/validation"
 )
 
@@ -23,3 +25,27 @@ func (s *server) validateProject(ctx context.Context, project string) error {
 	}
 	return nil
 }
+
+func validateProjectAndStageNonEmpty(project string, stage string) error {
+	if project == "" {
+		return connect.NewError(connect.CodeInvalidArgument, errors.New("project should not be empty"))
+	}
+	if stage == "" {
+		return connect.NewError(connect.CodeInvalidArgument, errors.New("stage should not be empty"))
+	}
+	return nil
+}
+
+// validateFreightExists returns the freight with the given ID in the list of freights, otherwise
+// return an error if it doesn't exist
+func validateFreightExists(freight string, freights kubev1alpha1.StageStateStack) (*kubev1alpha1.StageState, error) {
+	if freight == "" {
+		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("freight should not be empty"))
+	}
+	for _, f := range freights {
+		if freight == f.ID {
+			return &f, nil
+		}
+	}
+	return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("freight %q not found in Stage", freight))
+}
diff --git a/internal/cli/option/flag.go b/internal/cli/option/flag.go
index 87b90412..38b34305 100644
--- a/internal/cli/option/flag.go
+++ b/internal/cli/option/flag.go
@@ -21,8 +21,8 @@ func OptionalProject(v Optional[string]) FlagFn {
 	}
 }
 
-func State(v *string) FlagFn {
+func Freight(v *string) FlagFn {
 	return func(fs *pflag.FlagSet) {
-		fs.StringVar(v, "state", "", "State ID")
+		fs.StringVar(v, "freight", "", "Freight ID")
 	}
 }
diff --git a/internal/cli/stage/promote.go b/internal/cli/stage/promote.go
index 56c221fc..dbe9deb9 100644
--- a/internal/cli/stage/promote.go
+++ b/internal/cli/stage/promote.go
@@ -16,7 +16,7 @@ import (
 )
 
 type PromoteFlags struct {
-	State string
+	Freight string
 }
 
 func newPromoteCommand(opt *option.Option) *cobra.Command {
@@ -24,7 +24,7 @@ func newPromoteCommand(opt *option.Option) *cobra.Command {
 	cmd := &cobra.Command{
 		Use:     "promote",
 		Args:    cobra.ExactArgs(2),
-		Example: "kargo stage promote (PROJECT) (NAME) [(--state=)state-id]",
+		Example: "kargo stage promote (PROJECT) (NAME) [(--freight=)freight-id]",
 		RunE: func(cmd *cobra.Command, args []string) error {
 			ctx := cmd.Context()
 			kargoSvcCli, err := client.GetClientFromConfig(ctx, opt)
@@ -40,16 +40,16 @@ func newPromoteCommand(opt *option.Option) *cobra.Command {
 			if name == "" {
 				return errors.New("name is required")
 			}
-			state := strings.TrimSpace(flag.State)
-			if state == "" {
-				// TODO: Get latest available state if empty
-				return errors.New("state is required")
+			freight := strings.TrimSpace(flag.Freight)
+			if freight == "" {
+				// TODO: Get latest available freight if empty
+				return errors.New("freight is required")
 			}
 
 			res, err := kargoSvcCli.PromoteStage(ctx, connect.NewRequest(&v1alpha1.PromoteStageRequest{
 				Project: project,
 				Name:    name,
-				State:   state,
+				State:   freight,
 			}))
 			if err != nil {
 				return errors.Wrap(err, "promote stage")
@@ -69,6 +69,6 @@ func newPromoteCommand(opt *option.Option) *cobra.Command {
 		},
 	}
 	opt.PrintFlags.AddFlags(cmd)
-	option.State(&flag.State)(cmd.Flags())
+	option.Freight(&flag.Freight)(cmd.Flags())
 	return cmd
 }
diff --git a/internal/cli/stage/promote_subscribers.go b/internal/cli/stage/promote_subscribers.go
new file mode 100644
index 00000000..64073ec1
--- /dev/null
+++ b/internal/cli/stage/promote_subscribers.go
@@ -0,0 +1,79 @@
+package stage
+
+import (
+	"fmt"
+	"strings"
+
+	"connectrpc.com/connect"
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+	"k8s.io/utils/pointer"
+
+	typesv1alpha1 "github.com/akuity/kargo/internal/api/types/v1alpha1"
+	"github.com/akuity/kargo/internal/cli/client"
+	"github.com/akuity/kargo/internal/cli/option"
+	v1alpha1 "github.com/akuity/kargo/pkg/api/service/v1alpha1"
+)
+
+type PromoteSubscribersFlags struct {
+	Freight string
+}
+
+func newPromoteSubscribersCommand(opt *option.Option) *cobra.Command {
+	var flag PromoteSubscribersFlags
+	cmd := &cobra.Command{
+		Use:     "promote-subscribers",
+		Args:    cobra.ExactArgs(2),
+		Example: "kargo stage promote-subscribers (PROJECT) (NAME) [(--freight=)freight-id]",
+		RunE: func(cmd *cobra.Command, args []string) error {
+			ctx := cmd.Context()
+			kargoSvcCli, err := client.GetClientFromConfig(ctx, opt)
+			if err != nil {
+				return err
+			}
+
+			project := strings.TrimSpace(args[0])
+			if project == "" {
+				return errors.New("project is required")
+			}
+			name := strings.TrimSpace(args[1])
+			if name == "" {
+				return errors.New("name is required")
+			}
+			freight := strings.TrimSpace(flag.Freight)
+			if freight == "" {
+				return errors.New("freight is required")
+			}
+
+			res, promoteErr := kargoSvcCli.PromoteSubscribers(ctx, connect.NewRequest(&v1alpha1.PromoteSubscribersRequest{
+				Project: project,
+				Stage:   name,
+				Freight: freight,
+			}))
+			if pointer.StringDeref(opt.PrintFlags.OutputFormat, "") == "" {
+				if res != nil && res.Msg != nil {
+					for _, p := range res.Msg.GetPromotions() {
+						fmt.Fprintf(opt.IOStreams.Out, "Promotion Created: %q\n", *p.Metadata.Name)
+					}
+				}
+				if promoteErr != nil {
+					return errors.Wrap(promoteErr, "promote subscribers")
+				}
+				return nil
+			}
+
+			printer, printerErr := opt.PrintFlags.ToPrinter()
+			if printerErr != nil {
+				return errors.Wrap(printerErr, "new printer")
+			}
+			for _, p := range res.Msg.GetPromotions() {
+				kubeP := typesv1alpha1.FromPromotionProto(p)
+				_ = printer.PrintObj(kubeP, opt.IOStreams.Out)
+			}
+			return promoteErr
+		},
+	}
+	opt.PrintFlags.AddFlags(cmd)
+	option.Freight(&flag.Freight)(cmd.Flags())
+	return cmd
+}
diff --git a/internal/cli/stage/stage.go b/internal/cli/stage/stage.go
index 70102f58..b9676506 100644
--- a/internal/cli/stage/stage.go
+++ b/internal/cli/stage/stage.go
@@ -14,5 +14,6 @@ func NewCommand(opt *option.Option) *cobra.Command {
 	cmd.AddCommand(newPromoteCommand(opt))
 	cmd.AddCommand(newEnableAutoPromotion(opt))
 	cmd.AddCommand(newDisableAutoPromotion(opt))
+	cmd.AddCommand(newPromoteSubscribersCommand(opt))
 	return cmd
 }
diff --git a/internal/controller/stages/stages.go b/internal/controller/stages/stages.go
index 317a323a..b697dcf8 100644
--- a/internal/controller/stages/stages.go
+++ b/internal/controller/stages/stages.go
@@ -2,7 +2,6 @@ package stages
 
 import (
 	"context"
-	"fmt"
 	"time"
 
 	argocd "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
@@ -25,6 +24,7 @@ import (
 	"github.com/akuity/kargo/internal/credentials"
 	"github.com/akuity/kargo/internal/helm"
 	"github.com/akuity/kargo/internal/images"
+	"github.com/akuity/kargo/internal/kargo"
 	"github.com/akuity/kargo/internal/kubeclient"
 	"github.com/akuity/kargo/internal/logging"
 )
@@ -444,25 +444,10 @@ func (r *reconciler) syncStage(
 	logger = logger.WithField("state", nextState.ID)
 	logger.Debug("auto-promotion will proceed")
 
-	promo := &api.Promotion{
-		ObjectMeta: metav1.ObjectMeta{
-			Name:      fmt.Sprintf("%s-to-%s", stage.Name, nextState.ID),
-			Namespace: stage.Namespace,
-		},
-		Spec: &api.PromotionSpec{
-			Stage: stage.Name,
-			State: nextState.ID,
-		},
-	}
-
-	if stage.Labels != nil && stage.Labels[controller.ShardLabelKey] != "" {
-		promo.ObjectMeta.Labels = map[string]string{
-			controller.ShardLabelKey: stage.Labels[controller.ShardLabelKey],
-		}
-	}
+	promo := kargo.NewPromotion(*stage, nextState.ID)
 
 	if err :=
-		r.kargoClient.Create(ctx, promo, &client.CreateOptions{}); err != nil {
+		r.kargoClient.Create(ctx, &promo, &client.CreateOptions{}); err != nil {
 		if apierrors.IsAlreadyExists(err) {
 			logger.Debug("Promotion resource already exists")
 			return status, nil
diff --git a/internal/kargo/kargo.go b/internal/kargo/kargo.go
new file mode 100644
index 00000000..0ab246fc
--- /dev/null
+++ b/internal/kargo/kargo.go
@@ -0,0 +1,56 @@
+package kargo
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/oklog/ulid/v2"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	kubev1alpha1 "github.com/akuity/kargo/api/v1alpha1"
+	"github.com/akuity/kargo/internal/controller"
+)
+
+const (
+	// maximum length of the stage name used in the promotion name prefix before it exceeds
+	// kubernetes resource name limit of 253
+	// 253 - 1 (.) - 26 (ulid) - 1 (.) - 7 (sha) = 218
+	maxStageNamePrefixLength = 218
+)
+
+// NewPromotion returns a new Promotion from a given stage and freight with our naming convention.
+// Ensures the owner reference is set to be the stage, and carries over any shard labels
+func NewPromotion(stage kubev1alpha1.Stage, freight string) kubev1alpha1.Promotion {
+	shortHash := freight
+	if len(shortHash) > 7 {
+		shortHash = freight[0:7]
+	}
+	shortStageName := stage.Name
+	if len(stage.Name) > maxStageNamePrefixLength {
+		shortStageName = shortStageName[0:maxStageNamePrefixLength]
+	}
+
+	// ulid.Make() is pseudo-random, not crypto-random, but we don't care.
+	// We just want a unique ID that can be sorted lexicographically
+	promoName := strings.ToLower(fmt.Sprintf("%s.%s.%s", shortStageName, ulid.Make(), shortHash))
+
+	ownerRef := metav1.NewControllerRef(&stage, kubev1alpha1.GroupVersion.WithKind("Stage"))
+
+	promotion := kubev1alpha1.Promotion{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:            promoName,
+			Namespace:       stage.Namespace,
+			OwnerReferences: []metav1.OwnerReference{*ownerRef},
+		},
+		Spec: &kubev1alpha1.PromotionSpec{
+			Stage: stage.Name,
+			State: freight,
+		},
+	}
+	if stage.Labels != nil && stage.Labels[controller.ShardLabelKey] != "" {
+		promotion.ObjectMeta.Labels = map[string]string{
+			controller.ShardLabelKey: stage.Labels[controller.ShardLabelKey],
+		}
+	}
+	return promotion
+}
diff --git a/pkg/api/service/v1alpha1/service.pb.go b/pkg/api/service/v1alpha1/service.pb.go
index f0bcd330..afa63d47 100644
--- a/pkg/api/service/v1alpha1/service.pb.go
+++ b/pkg/api/service/v1alpha1/service.pb.go
@@ -1868,6 +1868,116 @@ func (x *PromoteStageResponse) GetPromotion() *v1alpha1.Promotion {
 	return nil
 }
 
+type PromoteSubscribersRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Project string `protobuf:"bytes,1,opt,name=project,proto3" json:"project,omitempty"`
+	Stage   string `protobuf:"bytes,2,opt,name=stage,proto3" json:"stage,omitempty"`
+	Freight string `protobuf:"bytes,3,opt,name=freight,proto3" json:"freight,omitempty"`
+}
+
+func (x *PromoteSubscribersRequest) Reset() {
+	*x = PromoteSubscribersRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_service_v1alpha1_service_proto_msgTypes[33]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PromoteSubscribersRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PromoteSubscribersRequest) ProtoMessage() {}
+
+func (x *PromoteSubscribersRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_service_v1alpha1_service_proto_msgTypes[33]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use PromoteSubscribersRequest.ProtoReflect.Descriptor instead.
+func (*PromoteSubscribersRequest) Descriptor() ([]byte, []int) {
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{33}
+}
+
+func (x *PromoteSubscribersRequest) GetProject() string {
+	if x != nil {
+		return x.Project
+	}
+	return ""
+}
+
+func (x *PromoteSubscribersRequest) GetStage() string {
+	if x != nil {
+		return x.Stage
+	}
+	return ""
+}
+
+func (x *PromoteSubscribersRequest) GetFreight() string {
+	if x != nil {
+		return x.Freight
+	}
+	return ""
+}
+
+type PromoteSubscribersResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Promotions []*v1alpha1.Promotion `protobuf:"bytes,1,rep,name=promotions,proto3" json:"promotions,omitempty"`
+}
+
+func (x *PromoteSubscribersResponse) Reset() {
+	*x = PromoteSubscribersResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_service_v1alpha1_service_proto_msgTypes[34]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PromoteSubscribersResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PromoteSubscribersResponse) ProtoMessage() {}
+
+func (x *PromoteSubscribersResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_service_v1alpha1_service_proto_msgTypes[34]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use PromoteSubscribersResponse.ProtoReflect.Descriptor instead.
+func (*PromoteSubscribersResponse) Descriptor() ([]byte, []int) {
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{34}
+}
+
+func (x *PromoteSubscribersResponse) GetPromotions() []*v1alpha1.Promotion {
+	if x != nil {
+		return x.Promotions
+	}
+	return nil
+}
+
 type RefreshStageRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1880,7 +1990,7 @@ type RefreshStageRequest struct {
 func (x *RefreshStageRequest) Reset() {
 	*x = RefreshStageRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[33]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[35]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1893,7 +2003,7 @@ func (x *RefreshStageRequest) String() string {
 func (*RefreshStageRequest) ProtoMessage() {}
 
 func (x *RefreshStageRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[33]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[35]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1906,7 +2016,7 @@ func (x *RefreshStageRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use RefreshStageRequest.ProtoReflect.Descriptor instead.
 func (*RefreshStageRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{33}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{35}
 }
 
 func (x *RefreshStageRequest) GetProject() string {
@@ -1934,7 +2044,7 @@ type RefreshStageResponse struct {
 func (x *RefreshStageResponse) Reset() {
 	*x = RefreshStageResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[34]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[36]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1947,7 +2057,7 @@ func (x *RefreshStageResponse) String() string {
 func (*RefreshStageResponse) ProtoMessage() {}
 
 func (x *RefreshStageResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[34]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[36]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -1960,7 +2070,7 @@ func (x *RefreshStageResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use RefreshStageResponse.ProtoReflect.Descriptor instead.
 func (*RefreshStageResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{34}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{36}
 }
 
 func (x *RefreshStageResponse) GetStage() *v1alpha1.Stage {
@@ -1984,7 +2094,7 @@ type TypedPromotionPolicySpec struct {
 func (x *TypedPromotionPolicySpec) Reset() {
 	*x = TypedPromotionPolicySpec{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[35]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[37]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -1997,7 +2107,7 @@ func (x *TypedPromotionPolicySpec) String() string {
 func (*TypedPromotionPolicySpec) ProtoMessage() {}
 
 func (x *TypedPromotionPolicySpec) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[35]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[37]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2010,7 +2120,7 @@ func (x *TypedPromotionPolicySpec) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use TypedPromotionPolicySpec.ProtoReflect.Descriptor instead.
 func (*TypedPromotionPolicySpec) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{35}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{37}
 }
 
 func (x *TypedPromotionPolicySpec) GetProject() string {
@@ -2053,7 +2163,7 @@ type ListPromotionsRequest struct {
 func (x *ListPromotionsRequest) Reset() {
 	*x = ListPromotionsRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[36]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[38]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2066,7 +2176,7 @@ func (x *ListPromotionsRequest) String() string {
 func (*ListPromotionsRequest) ProtoMessage() {}
 
 func (x *ListPromotionsRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[36]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[38]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2079,7 +2189,7 @@ func (x *ListPromotionsRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListPromotionsRequest.ProtoReflect.Descriptor instead.
 func (*ListPromotionsRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{36}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{38}
 }
 
 func (x *ListPromotionsRequest) GetProject() string {
@@ -2107,7 +2217,7 @@ type ListPromotionsResponse struct {
 func (x *ListPromotionsResponse) Reset() {
 	*x = ListPromotionsResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[37]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[39]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2120,7 +2230,7 @@ func (x *ListPromotionsResponse) String() string {
 func (*ListPromotionsResponse) ProtoMessage() {}
 
 func (x *ListPromotionsResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[37]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[39]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2133,7 +2243,7 @@ func (x *ListPromotionsResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListPromotionsResponse.ProtoReflect.Descriptor instead.
 func (*ListPromotionsResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{37}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{39}
 }
 
 func (x *ListPromotionsResponse) GetPromotions() []*v1alpha1.Promotion {
@@ -2155,7 +2265,7 @@ type GetPromotionRequest struct {
 func (x *GetPromotionRequest) Reset() {
 	*x = GetPromotionRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[38]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[40]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2168,7 +2278,7 @@ func (x *GetPromotionRequest) String() string {
 func (*GetPromotionRequest) ProtoMessage() {}
 
 func (x *GetPromotionRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[38]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[40]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2181,7 +2291,7 @@ func (x *GetPromotionRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetPromotionRequest.ProtoReflect.Descriptor instead.
 func (*GetPromotionRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{38}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{40}
 }
 
 func (x *GetPromotionRequest) GetProject() string {
@@ -2209,7 +2319,7 @@ type GetPromotionResponse struct {
 func (x *GetPromotionResponse) Reset() {
 	*x = GetPromotionResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[39]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[41]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2222,7 +2332,7 @@ func (x *GetPromotionResponse) String() string {
 func (*GetPromotionResponse) ProtoMessage() {}
 
 func (x *GetPromotionResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[39]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[41]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2235,7 +2345,7 @@ func (x *GetPromotionResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetPromotionResponse.ProtoReflect.Descriptor instead.
 func (*GetPromotionResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{39}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{41}
 }
 
 func (x *GetPromotionResponse) GetPromotion() *v1alpha1.Promotion {
@@ -2258,7 +2368,7 @@ type SetAutoPromotionForStageRequest struct {
 func (x *SetAutoPromotionForStageRequest) Reset() {
 	*x = SetAutoPromotionForStageRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[40]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[42]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2271,7 +2381,7 @@ func (x *SetAutoPromotionForStageRequest) String() string {
 func (*SetAutoPromotionForStageRequest) ProtoMessage() {}
 
 func (x *SetAutoPromotionForStageRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[40]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[42]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2284,7 +2394,7 @@ func (x *SetAutoPromotionForStageRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use SetAutoPromotionForStageRequest.ProtoReflect.Descriptor instead.
 func (*SetAutoPromotionForStageRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{40}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{42}
 }
 
 func (x *SetAutoPromotionForStageRequest) GetProject() string {
@@ -2319,7 +2429,7 @@ type SetAutoPromotionForStageResponse struct {
 func (x *SetAutoPromotionForStageResponse) Reset() {
 	*x = SetAutoPromotionForStageResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[41]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[43]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2332,7 +2442,7 @@ func (x *SetAutoPromotionForStageResponse) String() string {
 func (*SetAutoPromotionForStageResponse) ProtoMessage() {}
 
 func (x *SetAutoPromotionForStageResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[41]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[43]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2345,7 +2455,7 @@ func (x *SetAutoPromotionForStageResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use SetAutoPromotionForStageResponse.ProtoReflect.Descriptor instead.
 func (*SetAutoPromotionForStageResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{41}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{43}
 }
 
 func (x *SetAutoPromotionForStageResponse) GetPromotionPolicy() *v1alpha1.PromotionPolicy {
@@ -2370,7 +2480,7 @@ type CreatePromotionPolicyRequest struct {
 func (x *CreatePromotionPolicyRequest) Reset() {
 	*x = CreatePromotionPolicyRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[42]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[44]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2383,7 +2493,7 @@ func (x *CreatePromotionPolicyRequest) String() string {
 func (*CreatePromotionPolicyRequest) ProtoMessage() {}
 
 func (x *CreatePromotionPolicyRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[42]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[44]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2396,7 +2506,7 @@ func (x *CreatePromotionPolicyRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CreatePromotionPolicyRequest.ProtoReflect.Descriptor instead.
 func (*CreatePromotionPolicyRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{42}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{44}
 }
 
 func (m *CreatePromotionPolicyRequest) GetPromotionPolicy() isCreatePromotionPolicyRequest_PromotionPolicy {
@@ -2447,7 +2557,7 @@ type CreatePromotionPolicyResponse struct {
 func (x *CreatePromotionPolicyResponse) Reset() {
 	*x = CreatePromotionPolicyResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[43]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[45]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2460,7 +2570,7 @@ func (x *CreatePromotionPolicyResponse) String() string {
 func (*CreatePromotionPolicyResponse) ProtoMessage() {}
 
 func (x *CreatePromotionPolicyResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[43]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[45]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2473,7 +2583,7 @@ func (x *CreatePromotionPolicyResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CreatePromotionPolicyResponse.ProtoReflect.Descriptor instead.
 func (*CreatePromotionPolicyResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{43}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{45}
 }
 
 func (x *CreatePromotionPolicyResponse) GetPromotionPolicy() *v1alpha1.PromotionPolicy {
@@ -2494,7 +2604,7 @@ type ListPromotionPoliciesRequest struct {
 func (x *ListPromotionPoliciesRequest) Reset() {
 	*x = ListPromotionPoliciesRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[44]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[46]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2507,7 +2617,7 @@ func (x *ListPromotionPoliciesRequest) String() string {
 func (*ListPromotionPoliciesRequest) ProtoMessage() {}
 
 func (x *ListPromotionPoliciesRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[44]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[46]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2520,7 +2630,7 @@ func (x *ListPromotionPoliciesRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListPromotionPoliciesRequest.ProtoReflect.Descriptor instead.
 func (*ListPromotionPoliciesRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{44}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{46}
 }
 
 func (x *ListPromotionPoliciesRequest) GetProject() string {
@@ -2541,7 +2651,7 @@ type ListPromotionPoliciesResponse struct {
 func (x *ListPromotionPoliciesResponse) Reset() {
 	*x = ListPromotionPoliciesResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[45]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[47]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2554,7 +2664,7 @@ func (x *ListPromotionPoliciesResponse) String() string {
 func (*ListPromotionPoliciesResponse) ProtoMessage() {}
 
 func (x *ListPromotionPoliciesResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[45]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[47]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2567,7 +2677,7 @@ func (x *ListPromotionPoliciesResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListPromotionPoliciesResponse.ProtoReflect.Descriptor instead.
 func (*ListPromotionPoliciesResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{45}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{47}
 }
 
 func (x *ListPromotionPoliciesResponse) GetPromotionPolicies() []*v1alpha1.PromotionPolicy {
@@ -2589,7 +2699,7 @@ type GetPromotionPolicyRequest struct {
 func (x *GetPromotionPolicyRequest) Reset() {
 	*x = GetPromotionPolicyRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[46]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[48]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2602,7 +2712,7 @@ func (x *GetPromotionPolicyRequest) String() string {
 func (*GetPromotionPolicyRequest) ProtoMessage() {}
 
 func (x *GetPromotionPolicyRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[46]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[48]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2615,7 +2725,7 @@ func (x *GetPromotionPolicyRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetPromotionPolicyRequest.ProtoReflect.Descriptor instead.
 func (*GetPromotionPolicyRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{46}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{48}
 }
 
 func (x *GetPromotionPolicyRequest) GetProject() string {
@@ -2643,7 +2753,7 @@ type GetPromotionPolicyResponse struct {
 func (x *GetPromotionPolicyResponse) Reset() {
 	*x = GetPromotionPolicyResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[47]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[49]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2656,7 +2766,7 @@ func (x *GetPromotionPolicyResponse) String() string {
 func (*GetPromotionPolicyResponse) ProtoMessage() {}
 
 func (x *GetPromotionPolicyResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[47]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[49]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2669,7 +2779,7 @@ func (x *GetPromotionPolicyResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use GetPromotionPolicyResponse.ProtoReflect.Descriptor instead.
 func (*GetPromotionPolicyResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{47}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{49}
 }
 
 func (x *GetPromotionPolicyResponse) GetPromotionPolicy() *v1alpha1.PromotionPolicy {
@@ -2694,7 +2804,7 @@ type UpdatePromotionPolicyRequest struct {
 func (x *UpdatePromotionPolicyRequest) Reset() {
 	*x = UpdatePromotionPolicyRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[48]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[50]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2707,7 +2817,7 @@ func (x *UpdatePromotionPolicyRequest) String() string {
 func (*UpdatePromotionPolicyRequest) ProtoMessage() {}
 
 func (x *UpdatePromotionPolicyRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[48]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[50]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2720,7 +2830,7 @@ func (x *UpdatePromotionPolicyRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use UpdatePromotionPolicyRequest.ProtoReflect.Descriptor instead.
 func (*UpdatePromotionPolicyRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{48}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{50}
 }
 
 func (m *UpdatePromotionPolicyRequest) GetPromotionPolicy() isUpdatePromotionPolicyRequest_PromotionPolicy {
@@ -2771,7 +2881,7 @@ type UpdatePromotionPolicyResponse struct {
 func (x *UpdatePromotionPolicyResponse) Reset() {
 	*x = UpdatePromotionPolicyResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[49]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[51]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2784,7 +2894,7 @@ func (x *UpdatePromotionPolicyResponse) String() string {
 func (*UpdatePromotionPolicyResponse) ProtoMessage() {}
 
 func (x *UpdatePromotionPolicyResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[49]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[51]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2797,7 +2907,7 @@ func (x *UpdatePromotionPolicyResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use UpdatePromotionPolicyResponse.ProtoReflect.Descriptor instead.
 func (*UpdatePromotionPolicyResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{49}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{51}
 }
 
 func (x *UpdatePromotionPolicyResponse) GetPromotionPolicy() *v1alpha1.PromotionPolicy {
@@ -2819,7 +2929,7 @@ type DeletePromotionPolicyRequest struct {
 func (x *DeletePromotionPolicyRequest) Reset() {
 	*x = DeletePromotionPolicyRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[50]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[52]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2832,7 +2942,7 @@ func (x *DeletePromotionPolicyRequest) String() string {
 func (*DeletePromotionPolicyRequest) ProtoMessage() {}
 
 func (x *DeletePromotionPolicyRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[50]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[52]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2845,7 +2955,7 @@ func (x *DeletePromotionPolicyRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DeletePromotionPolicyRequest.ProtoReflect.Descriptor instead.
 func (*DeletePromotionPolicyRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{50}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{52}
 }
 
 func (x *DeletePromotionPolicyRequest) GetProject() string {
@@ -2871,7 +2981,7 @@ type DeletePromotionPolicyResponse struct {
 func (x *DeletePromotionPolicyResponse) Reset() {
 	*x = DeletePromotionPolicyResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[51]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[53]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2884,7 +2994,7 @@ func (x *DeletePromotionPolicyResponse) String() string {
 func (*DeletePromotionPolicyResponse) ProtoMessage() {}
 
 func (x *DeletePromotionPolicyResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[51]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[53]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2897,7 +3007,7 @@ func (x *DeletePromotionPolicyResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DeletePromotionPolicyResponse.ProtoReflect.Descriptor instead.
 func (*DeletePromotionPolicyResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{51}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{53}
 }
 
 type Project struct {
@@ -2912,7 +3022,7 @@ type Project struct {
 func (x *Project) Reset() {
 	*x = Project{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[52]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[54]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2925,7 +3035,7 @@ func (x *Project) String() string {
 func (*Project) ProtoMessage() {}
 
 func (x *Project) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[52]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[54]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2938,7 +3048,7 @@ func (x *Project) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use Project.ProtoReflect.Descriptor instead.
 func (*Project) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{52}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{54}
 }
 
 func (x *Project) GetName() string {
@@ -2966,7 +3076,7 @@ type CreateProjectRequest struct {
 func (x *CreateProjectRequest) Reset() {
 	*x = CreateProjectRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[53]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[55]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -2979,7 +3089,7 @@ func (x *CreateProjectRequest) String() string {
 func (*CreateProjectRequest) ProtoMessage() {}
 
 func (x *CreateProjectRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[53]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[55]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -2992,7 +3102,7 @@ func (x *CreateProjectRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CreateProjectRequest.ProtoReflect.Descriptor instead.
 func (*CreateProjectRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{53}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{55}
 }
 
 func (x *CreateProjectRequest) GetName() string {
@@ -3013,7 +3123,7 @@ type CreateProjectResponse struct {
 func (x *CreateProjectResponse) Reset() {
 	*x = CreateProjectResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[54]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[56]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3026,7 +3136,7 @@ func (x *CreateProjectResponse) String() string {
 func (*CreateProjectResponse) ProtoMessage() {}
 
 func (x *CreateProjectResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[54]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[56]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3039,7 +3149,7 @@ func (x *CreateProjectResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use CreateProjectResponse.ProtoReflect.Descriptor instead.
 func (*CreateProjectResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{54}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{56}
 }
 
 func (x *CreateProjectResponse) GetProject() *Project {
@@ -3058,7 +3168,7 @@ type ListProjectsRequest struct {
 func (x *ListProjectsRequest) Reset() {
 	*x = ListProjectsRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[55]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[57]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3071,7 +3181,7 @@ func (x *ListProjectsRequest) String() string {
 func (*ListProjectsRequest) ProtoMessage() {}
 
 func (x *ListProjectsRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[55]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[57]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3084,7 +3194,7 @@ func (x *ListProjectsRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListProjectsRequest.ProtoReflect.Descriptor instead.
 func (*ListProjectsRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{55}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{57}
 }
 
 type ListProjectsResponse struct {
@@ -3098,7 +3208,7 @@ type ListProjectsResponse struct {
 func (x *ListProjectsResponse) Reset() {
 	*x = ListProjectsResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[56]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[58]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3111,7 +3221,7 @@ func (x *ListProjectsResponse) String() string {
 func (*ListProjectsResponse) ProtoMessage() {}
 
 func (x *ListProjectsResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[56]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[58]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3124,7 +3234,7 @@ func (x *ListProjectsResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListProjectsResponse.ProtoReflect.Descriptor instead.
 func (*ListProjectsResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{56}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{58}
 }
 
 func (x *ListProjectsResponse) GetProjects() []*Project {
@@ -3145,7 +3255,7 @@ type DeleteProjectRequest struct {
 func (x *DeleteProjectRequest) Reset() {
 	*x = DeleteProjectRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[57]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[59]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3158,7 +3268,7 @@ func (x *DeleteProjectRequest) String() string {
 func (*DeleteProjectRequest) ProtoMessage() {}
 
 func (x *DeleteProjectRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[57]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[59]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3171,7 +3281,7 @@ func (x *DeleteProjectRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DeleteProjectRequest.ProtoReflect.Descriptor instead.
 func (*DeleteProjectRequest) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{57}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{59}
 }
 
 func (x *DeleteProjectRequest) GetName() string {
@@ -3190,7 +3300,7 @@ type DeleteProjectResponse struct {
 func (x *DeleteProjectResponse) Reset() {
 	*x = DeleteProjectResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_service_v1alpha1_service_proto_msgTypes[58]
+		mi := &file_service_v1alpha1_service_proto_msgTypes[60]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -3203,7 +3313,7 @@ func (x *DeleteProjectResponse) String() string {
 func (*DeleteProjectResponse) ProtoMessage() {}
 
 func (x *DeleteProjectResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_service_v1alpha1_service_proto_msgTypes[58]
+	mi := &file_service_v1alpha1_service_proto_msgTypes[60]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -3216,7 +3326,7 @@ func (x *DeleteProjectResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DeleteProjectResponse.ProtoReflect.Descriptor instead.
 func (*DeleteProjectResponse) Descriptor() ([]byte, []int) {
-	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{58}
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{60}
 }
 
 var File_service_v1alpha1_service_proto protoreflect.FileDescriptor
@@ -3428,76 +3538,115 @@ var file_service_v1alpha1_service_proto_rawDesc = []byte{
 	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79,
 	0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
 	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f,
-	0x6e, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x43, 0x0a, 0x13,
-	0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a,
-	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x22, 0x5d, 0x0a, 0x14, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x67,
-	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x73, 0x74, 0x61,
-	0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75,
-	0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b, 0x61, 0x72,
-	0x67, 0x6f, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65,
-	0x22, 0x92, 0x01, 0x0a, 0x18, 0x54, 0x79, 0x70, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a,
+	0x6e, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x65, 0x0a, 0x19,
+	0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
+	0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f,
+	0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a,
+	0x65, 0x63, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x72, 0x65,
+	0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x66, 0x72, 0x65, 0x69,
+	0x67, 0x68, 0x74, 0x22, 0x71, 0x0a, 0x1a, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x75,
+	0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x12, 0x53, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+	0x6f, 0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e,
+	0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6d,
+	0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x43, 0x0a, 0x13, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73,
+	0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a,
 	0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
 	0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73,
-	0x74, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67,
-	0x65, 0x12, 0x32, 0x0a, 0x15, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x6f,
-	0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x13, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x41, 0x75, 0x74, 0x6f, 0x50, 0x72, 0x6f, 0x6d,
-	0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x56, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f,
-	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18,
-	0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x19, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67,
-	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65,
-	0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x22, 0x6d, 0x0a,
-	0x16, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x53, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6d, 0x6f,
-	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x69,
-	0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e,
-	0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31,
-	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e,
-	0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x43, 0x0a, 0x13,
-	0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a,
-	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x22, 0x69, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f,
-	0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x09, 0x70, 0x72, 0x6f,
-	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67,
-	0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79,
-	0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76,
-	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f,
-	0x6e, 0x52, 0x09, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x69, 0x0a, 0x1f,
-	0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e,
-	0x46, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
-	0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61,
-	0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12,
-	0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
-	0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x88, 0x01, 0x0a, 0x20, 0x53, 0x65, 0x74, 0x41,
-	0x75, 0x74, 0x6f, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x53,
-	0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x10,
-	0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5d, 0x0a, 0x14, 0x52,
+	0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e,
+	0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x6b, 0x67,
+	0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x74,
+	0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x22, 0x92, 0x01, 0x0a, 0x18, 0x54,
+	0x79, 0x70, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c,
+	0x69, 0x63, 0x79, 0x53, 0x70, 0x65, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65,
+	0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
+	0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x32, 0x0a, 0x15, 0x65,
+	0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x70, 0x72, 0x6f, 0x6d, 0x6f,
+	0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x65, 0x6e, 0x61, 0x62,
+	0x6c, 0x65, 0x41, 0x75, 0x74, 0x6f, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x22,
+	0x56, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a,
+	0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65,
+	0x63, 0x74, 0x12, 0x19, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x48, 0x00, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a,
+	0x06, 0x5f, 0x73, 0x74, 0x61, 0x67, 0x65, 0x22, 0x6d, 0x0a, 0x16, 0x4c, 0x69, 0x73, 0x74, 0x50,
+	0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x12, 0x53, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+	0x6f, 0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e,
+	0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x6d,
+	0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x43, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f,
+	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a,
+	0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
+	0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x69, 0x0a, 0x14, 0x47,
+	0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x12, 0x51, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
 	0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f,
 	0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
-	0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
-	0x79, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69,
-	0x63, 0x79, 0x22, 0x9c, 0x01, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f,
-	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b,
-	0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61,
-	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6d, 0x6f,
-	0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x53, 0x70, 0x65, 0x63, 0x48, 0x00,
-	0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x18,
-	0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x42, 0x12, 0x0a,
+	0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x70, 0x72, 0x6f,
+	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x69, 0x0a, 0x1f, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74,
+	0x6f, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x61,
+	0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f,
+	0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a,
+	0x65, 0x63, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x6e, 0x61,
+	0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x6e, 0x61, 0x62, 0x6c,
+	0x65, 0x22, 0x88, 0x01, 0x0a, 0x20, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x50, 0x72, 0x6f,
+	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65,
+	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
+	0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6b,
+	0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61,
+	0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d,
+	0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0f, 0x70, 0x72, 0x6f,
+	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x9c, 0x01, 0x0a,
+	0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e,
+	0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a,
+	0x05, 0x74, 0x79, 0x70, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61,
+	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
+	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x54, 0x79, 0x70, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f,
+	0x6c, 0x69, 0x63, 0x79, 0x53, 0x70, 0x65, 0x63, 0x48, 0x00, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65,
+	0x64, 0x12, 0x14, 0x0a, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48,
+	0x00, 0x52, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x42, 0x12, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f,
+	0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x85, 0x01, 0x0a, 0x1d,
+	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50,
+	0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a,
 	0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63,
-	0x79, 0x22, 0x85, 0x01, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d,
+	0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
+	0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b, 0x61, 0x72, 0x67,
+	0x6f, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
+	0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69,
+	0x63, 0x79, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c,
+	0x69, 0x63, 0x79, 0x22, 0x38, 0x0a, 0x1c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f,
+	0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x89, 0x01,
+	0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50,
+	0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+	0x68, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c,
+	0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69,
+	0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e,
+	0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31,
+	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e,
+	0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x11, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f,
+	0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22, 0x49, 0x0a, 0x19, 0x47, 0x65, 0x74,
+	0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
+	0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
+	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d,
 	0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f,
 	0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e,
 	0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e,
@@ -3505,313 +3654,297 @@ var file_service_v1alpha1_service_proto_rawDesc = []byte{
 	0x79, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e,
 	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69,
 	0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x38, 0x0a, 0x1c, 0x4c, 0x69, 0x73,
-	0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69,
-	0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f,
-	0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a,
-	0x65, 0x63, 0x74, 0x22, 0x89, 0x01, 0x0a, 0x1d, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d,
-	0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x68, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69,
-	0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
-	0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61,
-	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x6b, 0x67, 0x2e,
-	0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f,
-	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x11, 0x70, 0x72,
-	0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x22,
-	0x49, 0x0a, 0x19, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50,
-	0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07,
-	0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70,
-	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x82, 0x01, 0x0a, 0x1a, 0x47,
-	0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
-	0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x10, 0x70, 0x72, 0x6f,
-	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
-	0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x6b,
-	0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50,
-	0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0f,
-	0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22,
-	0x9c, 0x01, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x12, 0x52, 0x0a, 0x05, 0x74, 0x79, 0x70, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
-	0x3a, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67,
-	0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
-	0x61, 0x31, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x64, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f,
-	0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x53, 0x70, 0x65, 0x63, 0x48, 0x00, 0x52, 0x05, 0x74,
-	0x79, 0x70, 0x65, 0x64, 0x12, 0x14, 0x0a, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x42, 0x12, 0x0a, 0x10, 0x70, 0x72,
-	0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x85,
-	0x01, 0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69,
-	0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
-	0x12, 0x64, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f,
-	0x6c, 0x69, 0x63, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69, 0x74,
-	0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b,
-	0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70, 0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61,
-	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50,
-	0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e,
-	0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x4c, 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
+	0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x9c, 0x01, 0x0a, 0x1c, 0x55, 0x70,
+	0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c,
+	0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x52, 0x0a, 0x05, 0x74, 0x79,
+	0x70, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x3a, 0x2e, 0x61, 0x6b, 0x75, 0x69,
+	0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76,
+	0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x54, 0x79, 0x70,
+	0x65, 0x64, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
+	0x79, 0x53, 0x70, 0x65, 0x63, 0x48, 0x00, 0x52, 0x05, 0x74, 0x79, 0x70, 0x65, 0x64, 0x12, 0x14,
+	0x0a, 0x04, 0x79, 0x61, 0x6d, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04,
+	0x79, 0x61, 0x6d, 0x6c, 0x42, 0x12, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f,
+	0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x22, 0x85, 0x01, 0x0a, 0x1d, 0x55, 0x70, 0x64,
+	0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69,
+	0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x64, 0x0a, 0x10, 0x70, 0x72,
+	0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
+	0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x70,
+	0x6b, 0x67, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
 	0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
-	0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
-	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
-	0x6e, 0x61, 0x6d, 0x65, 0x22, 0x1f, 0x0a, 0x1d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72,
-	0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x5a, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
-	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
-	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3b, 0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74,
-	0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
-	0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65,
-	0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
-	0x65, 0x22, 0x2a, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65,
-	0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5c, 0x0a,
-	0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
-	0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79,
-	0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
-	0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65,
-	0x63, 0x74, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x15, 0x0a, 0x13, 0x4c,
-	0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x22, 0x5d, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63,
-	0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x70, 0x72,
-	0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61,
-	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
-	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
-	0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
-	0x73, 0x22, 0x2a, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65,
-	0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d,
-	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x17, 0x0a,
-	0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xb0, 0x1a, 0x0a, 0x0c, 0x4b, 0x61, 0x72, 0x67, 0x6f,
-	0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x83, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x56,
-	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x37, 0x2e, 0x61, 0x6b, 0x75,
-	0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72,
-	0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65,
-	0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e,
-	0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31,
-	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
-	0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x86, 0x01,
-	0x0a, 0x0f, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x12, 0x38, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61,
+	0x0f, 0x70, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
+	0x22, 0x4c, 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
+	0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x1f,
+	0x0a, 0x1d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f,
+	0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
+	0x5a, 0x0a, 0x07, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3b,
+	0x0a, 0x0b, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52,
+	0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x22, 0x2a, 0x0a, 0x14, 0x43,
+	0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x5c, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74,
+	0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x12, 0x43, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x29, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61,
 	0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c,
-	0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x61, 0x6b,
+	0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x07, 0x70, 0x72,
+	0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x15, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f,
+	0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x5d, 0x0a, 0x14,
+	0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73,
+	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e,
+	0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63,
+	0x74, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x22, 0x2a, 0x0a, 0x14, 0x44,
+	0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74,
+	0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x32, 0xc2, 0x1b, 0x0a, 0x0c, 0x4b, 0x61, 0x72, 0x67, 0x6f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
+	0x65, 0x12, 0x83, 0x01, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x49, 0x6e, 0x66, 0x6f, 0x12, 0x37, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f,
+	0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76,
+	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69,
+	0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e,
+	0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e,
+	0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x2e, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x86, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x50,
+	0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x38, 0x2e, 0x61, 0x6b,
 	0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65,
 	0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47,
 	0x65, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x0a, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c,
-	0x6f, 0x67, 0x69, 0x6e, 0x12, 0x33, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f,
-	0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76,
-	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x6f, 0x67,
-	0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x61, 0x6b, 0x75, 0x69,
-	0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76,
-	0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x64, 0x6d,
-	0x69, 0x6e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
-	0x83, 0x01, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69,
+	0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
+	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x75, 0x62, 0x6c,
+	0x69, 0x63, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x12, 0x77, 0x0a, 0x0a, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x12, 0x33,
+	0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f,
+	0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
+	0x31, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e,
+	0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31,
+	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x4c, 0x6f, 0x67, 0x69,
+	0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x83, 0x01, 0x0a, 0x0e, 0x43, 0x72,
+	0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x2e, 0x61,
+	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
+	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69,
+	0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
+	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52,
+	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+	0x83, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
 	0x63, 0x65, 0x12, 0x37, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b,
 	0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61,
-	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f,
+	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f,
 	0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x61, 0x6b,
 	0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65,
-	0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43,
-	0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x83, 0x01, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
+	0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55,
+	0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x83, 0x01, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
 	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74,
 	0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61,
+	0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65,
 	0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
 	0x74, 0x1a, 0x38, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61,
 	0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c,
-	0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75,
-	0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x83, 0x01, 0x0a, 0x0e,
-	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x37,
-	0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f,
-	0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
-	0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79,
-	0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
-	0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
-	0x65, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x12, 0x7a, 0x0a, 0x0b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65,
-	0x12, 0x34, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72,
+	0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75,
+	0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a, 0x0b, 0x43,
+	0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x34, 0x2e, 0x61, 0x6b, 0x75,
+	0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72,
+	0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72,
+	0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72,
 	0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
 	0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52,
-	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e,
-	0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
-	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
-	0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a,
-	0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x12, 0x33, 0x2e, 0x61, 0x6b,
-	0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65,
-	0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c,
-	0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x34, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72,
-	0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61,
-	0x67, 0x65, 0x12, 0x31, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b,
-	0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61,
-	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x32, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x0a, 0x4c, 0x69, 0x73, 0x74, 0x53,
+	0x74, 0x61, 0x67, 0x65, 0x73, 0x12, 0x33, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69,
 	0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
-	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x67,
-	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x0b, 0x57, 0x61, 0x74,
-	0x63, 0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x12, 0x34, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74,
+	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x74, 0x61,
+	0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x34, 0x2e, 0x61, 0x6b, 0x75,
+	0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72,
+	0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69,
+	0x73, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x12, 0x71, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x31, 0x2e, 0x61,
+	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
+	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x32, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67,
+	0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
+	0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x12, 0x7c, 0x0a, 0x0b, 0x57, 0x61, 0x74, 0x63, 0x68, 0x53, 0x74, 0x61, 0x67,
+	0x65, 0x73, 0x12, 0x34, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b,
+	0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61,
+	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x53, 0x74, 0x61, 0x67, 0x65,
+	0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74,
 	0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
 	0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x57, 0x61, 0x74, 0x63,
-	0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35,
-	0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f,
-	0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
-	0x31, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x12, 0x7a, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74,
-	0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x34, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e,
+	0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30,
+	0x01, 0x12, 0x7a, 0x0a, 0x0b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65,
+	0x12, 0x34, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72,
+	0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
+	0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e,
 	0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
 	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
-	0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x61,
+	0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a,
+	0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x34, 0x2e, 0x61,
 	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
 	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
-	0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a, 0x0b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61,
-	0x67, 0x65, 0x12, 0x34, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b,
+	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x1a, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b,
 	0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61,
 	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67,
-	0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74,
-	0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65,
-	0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
-	0x7d, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12,
+	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x0c, 0x50, 0x72, 0x6f,
+	0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69,
+	0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76,
+	0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f,
+	0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x36, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72,
+	0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
+	0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8f, 0x01, 0x0a, 0x12, 0x50, 0x72, 0x6f,
+	0x6d, 0x6f, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x72, 0x73, 0x12,
+	0x3b, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67,
+	0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
+	0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72,
+	0x69, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x61,
+	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
+	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65,
+	0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x0c, 0x52, 0x65,
+	0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x35, 0x2e, 0x61, 0x6b, 0x75,
+	0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72,
+	0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65,
+	0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x1a, 0x36, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61,
+	0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c,
+	0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x67,
+	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x83, 0x01, 0x0a, 0x0e, 0x4c, 0x69,
+	0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x37, 0x2e, 0x61,
+	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
+	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65,
+	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69,
+	0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
+	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f,
+	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+	0x7d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x12,
 	0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67,
 	0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
-	0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52,
+	0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52,
 	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e,
 	0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
-	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x65, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d,
-	0x0a, 0x0c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x35,
-	0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f,
-	0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
-	0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69,
-	0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e,
-	0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68,
-	0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x83, 0x01,
-	0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73,
-	0x12, 0x37, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72,
-	0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f,
-	0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x38, 0x2e, 0x61, 0x6b, 0x75, 0x69,
-	0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76,
-	0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73,
-	0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
-	0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x69, 0x6f, 0x6e, 0x12, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e,
-	0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31,
-	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x61, 0x6b, 0x75,
-	0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72,
-	0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65,
-	0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
-	0x73, 0x65, 0x12, 0xa1, 0x01, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x50, 0x72,
-	0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12,
-	0x41, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67,
-	0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
-	0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x1a, 0x42, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b,
-	0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61,
-	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x50, 0x72, 0x6f,
-	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74,
-	0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79,
-	0x12, 0x3e, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72,
-	0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x3f, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72,
-	0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x12, 0x98, 0x01, 0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x3e, 0x2e, 0x61, 0x6b,
-	0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65,
-	0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c,
-	0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69,
-	0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x61, 0x6b,
+	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f,
+	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0xa1,
+	0x01, 0x0a, 0x18, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
+	0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x41, 0x2e, 0x61, 0x6b,
 	0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65,
-	0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c,
-	0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69,
-	0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8f, 0x01, 0x0a,
-	0x12, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c,
-	0x69, 0x63, 0x79, 0x12, 0x3b, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e,
-	0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31,
-	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74,
-	0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x3c, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72,
-	0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e,
-	0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x98,
-	0x01, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69,
-	0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x3e, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74,
-	0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61,
-	0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
-	0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74,
-	0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61,
-	0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63,
-	0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x15, 0x44, 0x65,
-	0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c,
-	0x69, 0x63, 0x79, 0x12, 0x3e, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e,
-	0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31,
-	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f,
-	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e,
-	0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31,
-	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f,
-	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70,
-	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50,
-	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x36, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e,
-	0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
-	0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
-	0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37,
+	0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x53,
+	0x65, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x46,
+	0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x42,
 	0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f,
 	0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
-	0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50,
-	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79,
+	0x31, 0x2e, 0x53, 0x65, 0x74, 0x41, 0x75, 0x74, 0x6f, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69,
+	0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f,
+	0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x3e, 0x2e, 0x61,
+	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
+	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50,
+	0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x61,
+	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
+	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50,
+	0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x98, 0x01,
+	0x0a, 0x15, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50,
+	0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x3e, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79,
 	0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
 	0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50,
-	0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36,
-	0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f,
-	0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61,
-	0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65,
-	0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74,
-	0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x36, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74,
-	0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69,
-	0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65,
-	0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x37, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72,
-	0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63,
-	0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x97, 0x02, 0x0a, 0x24, 0x63, 0x6f,
-	0x6d, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67,
+	0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79,
+	0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
+	0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50,
+	0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x8f, 0x01, 0x0a, 0x12, 0x47, 0x65, 0x74,
+	0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12,
+	0x3b, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67,
 	0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
-	0x61, 0x31, 0x42, 0x0c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f,
-	0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61,
-	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2f, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f,
-	0x61, 0x70, 0x69, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x76, 0x31, 0x61, 0x6c,
-	0x70, 0x68, 0x61, 0x31, 0x3b, 0x73, 0x76, 0x63, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
-	0xa2, 0x02, 0x04, 0x41, 0x49, 0x4b, 0x53, 0xaa, 0x02, 0x20, 0x41, 0x6b, 0x75, 0x69, 0x74, 0x79,
-	0x2e, 0x49, 0x6f, 0x2e, 0x4b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63,
-	0x65, 0x2e, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x20, 0x41, 0x6b, 0x75,
-	0x69, 0x74, 0x79, 0x5c, 0x49, 0x6f, 0x5c, 0x4b, 0x61, 0x72, 0x67, 0x6f, 0x5c, 0x53, 0x65, 0x72,
-	0x76, 0x69, 0x63, 0x65, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xe2, 0x02, 0x2c,
-	0x41, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x5c, 0x49, 0x6f, 0x5c, 0x4b, 0x61, 0x72, 0x67, 0x6f, 0x5c,
-	0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
-	0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x24, 0x41,
-	0x6b, 0x75, 0x69, 0x74, 0x79, 0x3a, 0x3a, 0x49, 0x6f, 0x3a, 0x3a, 0x4b, 0x61, 0x72, 0x67, 0x6f,
-	0x3a, 0x3a, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70,
-	0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x61, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50,
+	0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3c, 0x2e, 0x61,
+	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
+	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x47, 0x65, 0x74, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69,
+	0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x15, 0x55,
+	0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f,
+	0x6c, 0x69, 0x63, 0x79, 0x12, 0x3e, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f,
+	0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76,
+	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72,
+	0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x1a, 0x3f, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f,
+	0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76,
+	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x72,
+	0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x98, 0x01, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
+	0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12,
+	0x3e, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67,
+	0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
+	0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69,
+	0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
+	0x3f, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67,
+	0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
+	0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6d, 0x6f, 0x74, 0x69,
+	0x6f, 0x6e, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
+	0x12, 0x80, 0x01, 0x0a, 0x0d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65,
+	0x63, 0x74, 0x12, 0x36, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b,
+	0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61,
+	0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a,
+	0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x61, 0x6b, 0x75,
+	0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72,
+	0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x43, 0x72,
+	0x65, 0x61, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
+	0x6e, 0x73, 0x65, 0x12, 0x7d, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65,
+	0x63, 0x74, 0x73, 0x12, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e,
+	0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31,
+	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65,
+	0x63, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x36, 0x2e, 0x61, 0x6b, 0x75,
+	0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72,
+	0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4c, 0x69,
+	0x73, 0x74, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x12, 0x80, 0x01, 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f,
+	0x6a, 0x65, 0x63, 0x74, 0x12, 0x36, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f,
+	0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76,
+	0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72,
+	0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x37, 0x2e, 0x61,
+	0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73,
+	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
+	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x97, 0x02, 0x0a, 0x24, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6b,
+	0x75, 0x69, 0x74, 0x79, 0x2e, 0x69, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x73, 0x65,
+	0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x42, 0x0c,
+	0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c,
+	0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x6b, 0x75, 0x69, 0x74,
+	0x79, 0x2f, 0x6b, 0x61, 0x72, 0x67, 0x6f, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70, 0x69, 0x2f,
+	0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
+	0x3b, 0x73, 0x76, 0x63, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xa2, 0x02, 0x04, 0x41,
+	0x49, 0x4b, 0x53, 0xaa, 0x02, 0x20, 0x41, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x49, 0x6f, 0x2e,
+	0x4b, 0x61, 0x72, 0x67, 0x6f, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x56, 0x31,
+	0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xca, 0x02, 0x20, 0x41, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x5c,
+	0x49, 0x6f, 0x5c, 0x4b, 0x61, 0x72, 0x67, 0x6f, 0x5c, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
+	0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0xe2, 0x02, 0x2c, 0x41, 0x6b, 0x75, 0x69,
+	0x74, 0x79, 0x5c, 0x49, 0x6f, 0x5c, 0x4b, 0x61, 0x72, 0x67, 0x6f, 0x5c, 0x53, 0x65, 0x72, 0x76,
+	0x69, 0x63, 0x65, 0x5c, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x5c, 0x47, 0x50, 0x42,
+	0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x24, 0x41, 0x6b, 0x75, 0x69, 0x74,
+	0x79, 0x3a, 0x3a, 0x49, 0x6f, 0x3a, 0x3a, 0x4b, 0x61, 0x72, 0x67, 0x6f, 0x3a, 0x3a, 0x53, 0x65,
+	0x72, 0x76, 0x69, 0x63, 0x65, 0x3a, 0x3a, 0x56, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62,
+	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -3826,7 +3959,7 @@ func file_service_v1alpha1_service_proto_rawDescGZIP() []byte {
 	return file_service_v1alpha1_service_proto_rawDescData
 }
 
-var file_service_v1alpha1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 59)
+var file_service_v1alpha1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 61)
 var file_service_v1alpha1_service_proto_goTypes = []interface{}{
 	(*ComponentVersions)(nil),                // 0: akuity.io.kargo.service.v1alpha1.ComponentVersions
 	(*VersionInfo)(nil),                      // 1: akuity.io.kargo.service.v1alpha1.VersionInfo
@@ -3861,124 +3994,129 @@ var file_service_v1alpha1_service_proto_goTypes = []interface{}{
 	(*DeleteStageResponse)(nil),              // 30: akuity.io.kargo.service.v1alpha1.DeleteStageResponse
 	(*PromoteStageRequest)(nil),              // 31: akuity.io.kargo.service.v1alpha1.PromoteStageRequest
 	(*PromoteStageResponse)(nil),             // 32: akuity.io.kargo.service.v1alpha1.PromoteStageResponse
-	(*RefreshStageRequest)(nil),              // 33: akuity.io.kargo.service.v1alpha1.RefreshStageRequest
-	(*RefreshStageResponse)(nil),             // 34: akuity.io.kargo.service.v1alpha1.RefreshStageResponse
-	(*TypedPromotionPolicySpec)(nil),         // 35: akuity.io.kargo.service.v1alpha1.TypedPromotionPolicySpec
-	(*ListPromotionsRequest)(nil),            // 36: akuity.io.kargo.service.v1alpha1.ListPromotionsRequest
-	(*ListPromotionsResponse)(nil),           // 37: akuity.io.kargo.service.v1alpha1.ListPromotionsResponse
-	(*GetPromotionRequest)(nil),              // 38: akuity.io.kargo.service.v1alpha1.GetPromotionRequest
-	(*GetPromotionResponse)(nil),             // 39: akuity.io.kargo.service.v1alpha1.GetPromotionResponse
-	(*SetAutoPromotionForStageRequest)(nil),  // 40: akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageRequest
-	(*SetAutoPromotionForStageResponse)(nil), // 41: akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageResponse
-	(*CreatePromotionPolicyRequest)(nil),     // 42: akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyRequest
-	(*CreatePromotionPolicyResponse)(nil),    // 43: akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyResponse
-	(*ListPromotionPoliciesRequest)(nil),     // 44: akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesRequest
-	(*ListPromotionPoliciesResponse)(nil),    // 45: akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesResponse
-	(*GetPromotionPolicyRequest)(nil),        // 46: akuity.io.kargo.service.v1alpha1.GetPromotionPolicyRequest
-	(*GetPromotionPolicyResponse)(nil),       // 47: akuity.io.kargo.service.v1alpha1.GetPromotionPolicyResponse
-	(*UpdatePromotionPolicyRequest)(nil),     // 48: akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyRequest
-	(*UpdatePromotionPolicyResponse)(nil),    // 49: akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyResponse
-	(*DeletePromotionPolicyRequest)(nil),     // 50: akuity.io.kargo.service.v1alpha1.DeletePromotionPolicyRequest
-	(*DeletePromotionPolicyResponse)(nil),    // 51: akuity.io.kargo.service.v1alpha1.DeletePromotionPolicyResponse
-	(*Project)(nil),                          // 52: akuity.io.kargo.service.v1alpha1.Project
-	(*CreateProjectRequest)(nil),             // 53: akuity.io.kargo.service.v1alpha1.CreateProjectRequest
-	(*CreateProjectResponse)(nil),            // 54: akuity.io.kargo.service.v1alpha1.CreateProjectResponse
-	(*ListProjectsRequest)(nil),              // 55: akuity.io.kargo.service.v1alpha1.ListProjectsRequest
-	(*ListProjectsResponse)(nil),             // 56: akuity.io.kargo.service.v1alpha1.ListProjectsResponse
-	(*DeleteProjectRequest)(nil),             // 57: akuity.io.kargo.service.v1alpha1.DeleteProjectRequest
-	(*DeleteProjectResponse)(nil),            // 58: akuity.io.kargo.service.v1alpha1.DeleteProjectResponse
-	(*timestamppb.Timestamp)(nil),            // 59: google.protobuf.Timestamp
-	(*v1alpha1.StageSpec)(nil),               // 60: github.com.akuity.kargo.pkg.api.v1alpha1.StageSpec
-	(*v1alpha1.Stage)(nil),                   // 61: github.com.akuity.kargo.pkg.api.v1alpha1.Stage
-	(*v1alpha1.Promotion)(nil),               // 62: github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
-	(*v1alpha1.PromotionPolicy)(nil),         // 63: github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
+	(*PromoteSubscribersRequest)(nil),        // 33: akuity.io.kargo.service.v1alpha1.PromoteSubscribersRequest
+	(*PromoteSubscribersResponse)(nil),       // 34: akuity.io.kargo.service.v1alpha1.PromoteSubscribersResponse
+	(*RefreshStageRequest)(nil),              // 35: akuity.io.kargo.service.v1alpha1.RefreshStageRequest
+	(*RefreshStageResponse)(nil),             // 36: akuity.io.kargo.service.v1alpha1.RefreshStageResponse
+	(*TypedPromotionPolicySpec)(nil),         // 37: akuity.io.kargo.service.v1alpha1.TypedPromotionPolicySpec
+	(*ListPromotionsRequest)(nil),            // 38: akuity.io.kargo.service.v1alpha1.ListPromotionsRequest
+	(*ListPromotionsResponse)(nil),           // 39: akuity.io.kargo.service.v1alpha1.ListPromotionsResponse
+	(*GetPromotionRequest)(nil),              // 40: akuity.io.kargo.service.v1alpha1.GetPromotionRequest
+	(*GetPromotionResponse)(nil),             // 41: akuity.io.kargo.service.v1alpha1.GetPromotionResponse
+	(*SetAutoPromotionForStageRequest)(nil),  // 42: akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageRequest
+	(*SetAutoPromotionForStageResponse)(nil), // 43: akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageResponse
+	(*CreatePromotionPolicyRequest)(nil),     // 44: akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyRequest
+	(*CreatePromotionPolicyResponse)(nil),    // 45: akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyResponse
+	(*ListPromotionPoliciesRequest)(nil),     // 46: akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesRequest
+	(*ListPromotionPoliciesResponse)(nil),    // 47: akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesResponse
+	(*GetPromotionPolicyRequest)(nil),        // 48: akuity.io.kargo.service.v1alpha1.GetPromotionPolicyRequest
+	(*GetPromotionPolicyResponse)(nil),       // 49: akuity.io.kargo.service.v1alpha1.GetPromotionPolicyResponse
+	(*UpdatePromotionPolicyRequest)(nil),     // 50: akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyRequest
+	(*UpdatePromotionPolicyResponse)(nil),    // 51: akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyResponse
+	(*DeletePromotionPolicyRequest)(nil),     // 52: akuity.io.kargo.service.v1alpha1.DeletePromotionPolicyRequest
+	(*DeletePromotionPolicyResponse)(nil),    // 53: akuity.io.kargo.service.v1alpha1.DeletePromotionPolicyResponse
+	(*Project)(nil),                          // 54: akuity.io.kargo.service.v1alpha1.Project
+	(*CreateProjectRequest)(nil),             // 55: akuity.io.kargo.service.v1alpha1.CreateProjectRequest
+	(*CreateProjectResponse)(nil),            // 56: akuity.io.kargo.service.v1alpha1.CreateProjectResponse
+	(*ListProjectsRequest)(nil),              // 57: akuity.io.kargo.service.v1alpha1.ListProjectsRequest
+	(*ListProjectsResponse)(nil),             // 58: akuity.io.kargo.service.v1alpha1.ListProjectsResponse
+	(*DeleteProjectRequest)(nil),             // 59: akuity.io.kargo.service.v1alpha1.DeleteProjectRequest
+	(*DeleteProjectResponse)(nil),            // 60: akuity.io.kargo.service.v1alpha1.DeleteProjectResponse
+	(*timestamppb.Timestamp)(nil),            // 61: google.protobuf.Timestamp
+	(*v1alpha1.StageSpec)(nil),               // 62: github.com.akuity.kargo.pkg.api.v1alpha1.StageSpec
+	(*v1alpha1.Stage)(nil),                   // 63: github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	(*v1alpha1.Promotion)(nil),               // 64: github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
+	(*v1alpha1.PromotionPolicy)(nil),         // 65: github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
 }
 var file_service_v1alpha1_service_proto_depIdxs = []int32{
 	1,  // 0: akuity.io.kargo.service.v1alpha1.ComponentVersions.server:type_name -> akuity.io.kargo.service.v1alpha1.VersionInfo
 	1,  // 1: akuity.io.kargo.service.v1alpha1.ComponentVersions.cli:type_name -> akuity.io.kargo.service.v1alpha1.VersionInfo
-	59, // 2: akuity.io.kargo.service.v1alpha1.VersionInfo.build_time:type_name -> google.protobuf.Timestamp
+	61, // 2: akuity.io.kargo.service.v1alpha1.VersionInfo.build_time:type_name -> google.protobuf.Timestamp
 	1,  // 3: akuity.io.kargo.service.v1alpha1.GetVersionInfoResponse.version_info:type_name -> akuity.io.kargo.service.v1alpha1.VersionInfo
 	6,  // 4: akuity.io.kargo.service.v1alpha1.GetPublicConfigResponse.oidc_config:type_name -> akuity.io.kargo.service.v1alpha1.OIDCConfig
-	60, // 5: akuity.io.kargo.service.v1alpha1.TypedStageSpec.spec:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.StageSpec
+	62, // 5: akuity.io.kargo.service.v1alpha1.TypedStageSpec.spec:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.StageSpec
 	11, // 6: akuity.io.kargo.service.v1alpha1.CreateResourceResponse.results:type_name -> akuity.io.kargo.service.v1alpha1.CreateResourceResult
 	14, // 7: akuity.io.kargo.service.v1alpha1.UpdateResourceResponse.results:type_name -> akuity.io.kargo.service.v1alpha1.UpdateResourceResult
 	17, // 8: akuity.io.kargo.service.v1alpha1.DeleteResourceResponse.results:type_name -> akuity.io.kargo.service.v1alpha1.DeleteResourceResult
 	9,  // 9: akuity.io.kargo.service.v1alpha1.CreateStageRequest.typed:type_name -> akuity.io.kargo.service.v1alpha1.TypedStageSpec
-	61, // 10: akuity.io.kargo.service.v1alpha1.CreateStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
-	61, // 11: akuity.io.kargo.service.v1alpha1.ListStagesResponse.stages:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
-	61, // 12: akuity.io.kargo.service.v1alpha1.GetStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
-	61, // 13: akuity.io.kargo.service.v1alpha1.WatchStagesResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	63, // 10: akuity.io.kargo.service.v1alpha1.CreateStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	63, // 11: akuity.io.kargo.service.v1alpha1.ListStagesResponse.stages:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	63, // 12: akuity.io.kargo.service.v1alpha1.GetStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	63, // 13: akuity.io.kargo.service.v1alpha1.WatchStagesResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
 	9,  // 14: akuity.io.kargo.service.v1alpha1.UpdateStageRequest.typed:type_name -> akuity.io.kargo.service.v1alpha1.TypedStageSpec
-	61, // 15: akuity.io.kargo.service.v1alpha1.UpdateStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
-	62, // 16: akuity.io.kargo.service.v1alpha1.PromoteStageResponse.promotion:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
-	61, // 17: akuity.io.kargo.service.v1alpha1.RefreshStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
-	62, // 18: akuity.io.kargo.service.v1alpha1.ListPromotionsResponse.promotions:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
-	62, // 19: akuity.io.kargo.service.v1alpha1.GetPromotionResponse.promotion:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
-	63, // 20: akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageResponse.promotion_policy:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
-	35, // 21: akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyRequest.typed:type_name -> akuity.io.kargo.service.v1alpha1.TypedPromotionPolicySpec
-	63, // 22: akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyResponse.promotion_policy:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
-	63, // 23: akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesResponse.promotion_policies:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
-	63, // 24: akuity.io.kargo.service.v1alpha1.GetPromotionPolicyResponse.promotion_policy:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
-	35, // 25: akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyRequest.typed:type_name -> akuity.io.kargo.service.v1alpha1.TypedPromotionPolicySpec
-	63, // 26: akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyResponse.promotion_policy:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
-	59, // 27: akuity.io.kargo.service.v1alpha1.Project.create_time:type_name -> google.protobuf.Timestamp
-	52, // 28: akuity.io.kargo.service.v1alpha1.CreateProjectResponse.project:type_name -> akuity.io.kargo.service.v1alpha1.Project
-	52, // 29: akuity.io.kargo.service.v1alpha1.ListProjectsResponse.projects:type_name -> akuity.io.kargo.service.v1alpha1.Project
-	2,  // 30: akuity.io.kargo.service.v1alpha1.KargoService.GetVersionInfo:input_type -> akuity.io.kargo.service.v1alpha1.GetVersionInfoRequest
-	4,  // 31: akuity.io.kargo.service.v1alpha1.KargoService.GetPublicConfig:input_type -> akuity.io.kargo.service.v1alpha1.GetPublicConfigRequest
-	7,  // 32: akuity.io.kargo.service.v1alpha1.KargoService.AdminLogin:input_type -> akuity.io.kargo.service.v1alpha1.AdminLoginRequest
-	10, // 33: akuity.io.kargo.service.v1alpha1.KargoService.CreateResource:input_type -> akuity.io.kargo.service.v1alpha1.CreateResourceRequest
-	13, // 34: akuity.io.kargo.service.v1alpha1.KargoService.UpdateResource:input_type -> akuity.io.kargo.service.v1alpha1.UpdateResourceRequest
-	16, // 35: akuity.io.kargo.service.v1alpha1.KargoService.DeleteResource:input_type -> akuity.io.kargo.service.v1alpha1.DeleteResourceRequest
-	19, // 36: akuity.io.kargo.service.v1alpha1.KargoService.CreateStage:input_type -> akuity.io.kargo.service.v1alpha1.CreateStageRequest
-	21, // 37: akuity.io.kargo.service.v1alpha1.KargoService.ListStages:input_type -> akuity.io.kargo.service.v1alpha1.ListStagesRequest
-	23, // 38: akuity.io.kargo.service.v1alpha1.KargoService.GetStage:input_type -> akuity.io.kargo.service.v1alpha1.GetStageRequest
-	25, // 39: akuity.io.kargo.service.v1alpha1.KargoService.WatchStages:input_type -> akuity.io.kargo.service.v1alpha1.WatchStagesRequest
-	27, // 40: akuity.io.kargo.service.v1alpha1.KargoService.UpdateStage:input_type -> akuity.io.kargo.service.v1alpha1.UpdateStageRequest
-	29, // 41: akuity.io.kargo.service.v1alpha1.KargoService.DeleteStage:input_type -> akuity.io.kargo.service.v1alpha1.DeleteStageRequest
-	31, // 42: akuity.io.kargo.service.v1alpha1.KargoService.PromoteStage:input_type -> akuity.io.kargo.service.v1alpha1.PromoteStageRequest
-	33, // 43: akuity.io.kargo.service.v1alpha1.KargoService.RefreshStage:input_type -> akuity.io.kargo.service.v1alpha1.RefreshStageRequest
-	36, // 44: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotions:input_type -> akuity.io.kargo.service.v1alpha1.ListPromotionsRequest
-	38, // 45: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotion:input_type -> akuity.io.kargo.service.v1alpha1.GetPromotionRequest
-	40, // 46: akuity.io.kargo.service.v1alpha1.KargoService.SetAutoPromotionForStage:input_type -> akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageRequest
-	42, // 47: akuity.io.kargo.service.v1alpha1.KargoService.CreatePromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyRequest
-	44, // 48: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotionPolicies:input_type -> akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesRequest
-	46, // 49: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.GetPromotionPolicyRequest
-	48, // 50: akuity.io.kargo.service.v1alpha1.KargoService.UpdatePromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyRequest
-	50, // 51: akuity.io.kargo.service.v1alpha1.KargoService.DeletePromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.DeletePromotionPolicyRequest
-	53, // 52: akuity.io.kargo.service.v1alpha1.KargoService.CreateProject:input_type -> akuity.io.kargo.service.v1alpha1.CreateProjectRequest
-	55, // 53: akuity.io.kargo.service.v1alpha1.KargoService.ListProjects:input_type -> akuity.io.kargo.service.v1alpha1.ListProjectsRequest
-	57, // 54: akuity.io.kargo.service.v1alpha1.KargoService.DeleteProject:input_type -> akuity.io.kargo.service.v1alpha1.DeleteProjectRequest
-	3,  // 55: akuity.io.kargo.service.v1alpha1.KargoService.GetVersionInfo:output_type -> akuity.io.kargo.service.v1alpha1.GetVersionInfoResponse
-	5,  // 56: akuity.io.kargo.service.v1alpha1.KargoService.GetPublicConfig:output_type -> akuity.io.kargo.service.v1alpha1.GetPublicConfigResponse
-	8,  // 57: akuity.io.kargo.service.v1alpha1.KargoService.AdminLogin:output_type -> akuity.io.kargo.service.v1alpha1.AdminLoginResponse
-	12, // 58: akuity.io.kargo.service.v1alpha1.KargoService.CreateResource:output_type -> akuity.io.kargo.service.v1alpha1.CreateResourceResponse
-	15, // 59: akuity.io.kargo.service.v1alpha1.KargoService.UpdateResource:output_type -> akuity.io.kargo.service.v1alpha1.UpdateResourceResponse
-	18, // 60: akuity.io.kargo.service.v1alpha1.KargoService.DeleteResource:output_type -> akuity.io.kargo.service.v1alpha1.DeleteResourceResponse
-	20, // 61: akuity.io.kargo.service.v1alpha1.KargoService.CreateStage:output_type -> akuity.io.kargo.service.v1alpha1.CreateStageResponse
-	22, // 62: akuity.io.kargo.service.v1alpha1.KargoService.ListStages:output_type -> akuity.io.kargo.service.v1alpha1.ListStagesResponse
-	24, // 63: akuity.io.kargo.service.v1alpha1.KargoService.GetStage:output_type -> akuity.io.kargo.service.v1alpha1.GetStageResponse
-	26, // 64: akuity.io.kargo.service.v1alpha1.KargoService.WatchStages:output_type -> akuity.io.kargo.service.v1alpha1.WatchStagesResponse
-	28, // 65: akuity.io.kargo.service.v1alpha1.KargoService.UpdateStage:output_type -> akuity.io.kargo.service.v1alpha1.UpdateStageResponse
-	30, // 66: akuity.io.kargo.service.v1alpha1.KargoService.DeleteStage:output_type -> akuity.io.kargo.service.v1alpha1.DeleteStageResponse
-	32, // 67: akuity.io.kargo.service.v1alpha1.KargoService.PromoteStage:output_type -> akuity.io.kargo.service.v1alpha1.PromoteStageResponse
-	34, // 68: akuity.io.kargo.service.v1alpha1.KargoService.RefreshStage:output_type -> akuity.io.kargo.service.v1alpha1.RefreshStageResponse
-	37, // 69: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotions:output_type -> akuity.io.kargo.service.v1alpha1.ListPromotionsResponse
-	39, // 70: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotion:output_type -> akuity.io.kargo.service.v1alpha1.GetPromotionResponse
-	41, // 71: akuity.io.kargo.service.v1alpha1.KargoService.SetAutoPromotionForStage:output_type -> akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageResponse
-	43, // 72: akuity.io.kargo.service.v1alpha1.KargoService.CreatePromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyResponse
-	45, // 73: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotionPolicies:output_type -> akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesResponse
-	47, // 74: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.GetPromotionPolicyResponse
-	49, // 75: akuity.io.kargo.service.v1alpha1.KargoService.UpdatePromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyResponse
-	51, // 76: akuity.io.kargo.service.v1alpha1.KargoService.DeletePromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.DeletePromotionPolicyResponse
-	54, // 77: akuity.io.kargo.service.v1alpha1.KargoService.CreateProject:output_type -> akuity.io.kargo.service.v1alpha1.CreateProjectResponse
-	56, // 78: akuity.io.kargo.service.v1alpha1.KargoService.ListProjects:output_type -> akuity.io.kargo.service.v1alpha1.ListProjectsResponse
-	58, // 79: akuity.io.kargo.service.v1alpha1.KargoService.DeleteProject:output_type -> akuity.io.kargo.service.v1alpha1.DeleteProjectResponse
-	55, // [55:80] is the sub-list for method output_type
-	30, // [30:55] is the sub-list for method input_type
-	30, // [30:30] is the sub-list for extension type_name
-	30, // [30:30] is the sub-list for extension extendee
-	0,  // [0:30] is the sub-list for field type_name
+	63, // 15: akuity.io.kargo.service.v1alpha1.UpdateStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	64, // 16: akuity.io.kargo.service.v1alpha1.PromoteStageResponse.promotion:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
+	64, // 17: akuity.io.kargo.service.v1alpha1.PromoteSubscribersResponse.promotions:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
+	63, // 18: akuity.io.kargo.service.v1alpha1.RefreshStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	64, // 19: akuity.io.kargo.service.v1alpha1.ListPromotionsResponse.promotions:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
+	64, // 20: akuity.io.kargo.service.v1alpha1.GetPromotionResponse.promotion:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
+	65, // 21: akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageResponse.promotion_policy:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
+	37, // 22: akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyRequest.typed:type_name -> akuity.io.kargo.service.v1alpha1.TypedPromotionPolicySpec
+	65, // 23: akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyResponse.promotion_policy:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
+	65, // 24: akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesResponse.promotion_policies:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
+	65, // 25: akuity.io.kargo.service.v1alpha1.GetPromotionPolicyResponse.promotion_policy:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
+	37, // 26: akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyRequest.typed:type_name -> akuity.io.kargo.service.v1alpha1.TypedPromotionPolicySpec
+	65, // 27: akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyResponse.promotion_policy:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
+	61, // 28: akuity.io.kargo.service.v1alpha1.Project.create_time:type_name -> google.protobuf.Timestamp
+	54, // 29: akuity.io.kargo.service.v1alpha1.CreateProjectResponse.project:type_name -> akuity.io.kargo.service.v1alpha1.Project
+	54, // 30: akuity.io.kargo.service.v1alpha1.ListProjectsResponse.projects:type_name -> akuity.io.kargo.service.v1alpha1.Project
+	2,  // 31: akuity.io.kargo.service.v1alpha1.KargoService.GetVersionInfo:input_type -> akuity.io.kargo.service.v1alpha1.GetVersionInfoRequest
+	4,  // 32: akuity.io.kargo.service.v1alpha1.KargoService.GetPublicConfig:input_type -> akuity.io.kargo.service.v1alpha1.GetPublicConfigRequest
+	7,  // 33: akuity.io.kargo.service.v1alpha1.KargoService.AdminLogin:input_type -> akuity.io.kargo.service.v1alpha1.AdminLoginRequest
+	10, // 34: akuity.io.kargo.service.v1alpha1.KargoService.CreateResource:input_type -> akuity.io.kargo.service.v1alpha1.CreateResourceRequest
+	13, // 35: akuity.io.kargo.service.v1alpha1.KargoService.UpdateResource:input_type -> akuity.io.kargo.service.v1alpha1.UpdateResourceRequest
+	16, // 36: akuity.io.kargo.service.v1alpha1.KargoService.DeleteResource:input_type -> akuity.io.kargo.service.v1alpha1.DeleteResourceRequest
+	19, // 37: akuity.io.kargo.service.v1alpha1.KargoService.CreateStage:input_type -> akuity.io.kargo.service.v1alpha1.CreateStageRequest
+	21, // 38: akuity.io.kargo.service.v1alpha1.KargoService.ListStages:input_type -> akuity.io.kargo.service.v1alpha1.ListStagesRequest
+	23, // 39: akuity.io.kargo.service.v1alpha1.KargoService.GetStage:input_type -> akuity.io.kargo.service.v1alpha1.GetStageRequest
+	25, // 40: akuity.io.kargo.service.v1alpha1.KargoService.WatchStages:input_type -> akuity.io.kargo.service.v1alpha1.WatchStagesRequest
+	27, // 41: akuity.io.kargo.service.v1alpha1.KargoService.UpdateStage:input_type -> akuity.io.kargo.service.v1alpha1.UpdateStageRequest
+	29, // 42: akuity.io.kargo.service.v1alpha1.KargoService.DeleteStage:input_type -> akuity.io.kargo.service.v1alpha1.DeleteStageRequest
+	31, // 43: akuity.io.kargo.service.v1alpha1.KargoService.PromoteStage:input_type -> akuity.io.kargo.service.v1alpha1.PromoteStageRequest
+	33, // 44: akuity.io.kargo.service.v1alpha1.KargoService.PromoteSubscribers:input_type -> akuity.io.kargo.service.v1alpha1.PromoteSubscribersRequest
+	35, // 45: akuity.io.kargo.service.v1alpha1.KargoService.RefreshStage:input_type -> akuity.io.kargo.service.v1alpha1.RefreshStageRequest
+	38, // 46: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotions:input_type -> akuity.io.kargo.service.v1alpha1.ListPromotionsRequest
+	40, // 47: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotion:input_type -> akuity.io.kargo.service.v1alpha1.GetPromotionRequest
+	42, // 48: akuity.io.kargo.service.v1alpha1.KargoService.SetAutoPromotionForStage:input_type -> akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageRequest
+	44, // 49: akuity.io.kargo.service.v1alpha1.KargoService.CreatePromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyRequest
+	46, // 50: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotionPolicies:input_type -> akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesRequest
+	48, // 51: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.GetPromotionPolicyRequest
+	50, // 52: akuity.io.kargo.service.v1alpha1.KargoService.UpdatePromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyRequest
+	52, // 53: akuity.io.kargo.service.v1alpha1.KargoService.DeletePromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.DeletePromotionPolicyRequest
+	55, // 54: akuity.io.kargo.service.v1alpha1.KargoService.CreateProject:input_type -> akuity.io.kargo.service.v1alpha1.CreateProjectRequest
+	57, // 55: akuity.io.kargo.service.v1alpha1.KargoService.ListProjects:input_type -> akuity.io.kargo.service.v1alpha1.ListProjectsRequest
+	59, // 56: akuity.io.kargo.service.v1alpha1.KargoService.DeleteProject:input_type -> akuity.io.kargo.service.v1alpha1.DeleteProjectRequest
+	3,  // 57: akuity.io.kargo.service.v1alpha1.KargoService.GetVersionInfo:output_type -> akuity.io.kargo.service.v1alpha1.GetVersionInfoResponse
+	5,  // 58: akuity.io.kargo.service.v1alpha1.KargoService.GetPublicConfig:output_type -> akuity.io.kargo.service.v1alpha1.GetPublicConfigResponse
+	8,  // 59: akuity.io.kargo.service.v1alpha1.KargoService.AdminLogin:output_type -> akuity.io.kargo.service.v1alpha1.AdminLoginResponse
+	12, // 60: akuity.io.kargo.service.v1alpha1.KargoService.CreateResource:output_type -> akuity.io.kargo.service.v1alpha1.CreateResourceResponse
+	15, // 61: akuity.io.kargo.service.v1alpha1.KargoService.UpdateResource:output_type -> akuity.io.kargo.service.v1alpha1.UpdateResourceResponse
+	18, // 62: akuity.io.kargo.service.v1alpha1.KargoService.DeleteResource:output_type -> akuity.io.kargo.service.v1alpha1.DeleteResourceResponse
+	20, // 63: akuity.io.kargo.service.v1alpha1.KargoService.CreateStage:output_type -> akuity.io.kargo.service.v1alpha1.CreateStageResponse
+	22, // 64: akuity.io.kargo.service.v1alpha1.KargoService.ListStages:output_type -> akuity.io.kargo.service.v1alpha1.ListStagesResponse
+	24, // 65: akuity.io.kargo.service.v1alpha1.KargoService.GetStage:output_type -> akuity.io.kargo.service.v1alpha1.GetStageResponse
+	26, // 66: akuity.io.kargo.service.v1alpha1.KargoService.WatchStages:output_type -> akuity.io.kargo.service.v1alpha1.WatchStagesResponse
+	28, // 67: akuity.io.kargo.service.v1alpha1.KargoService.UpdateStage:output_type -> akuity.io.kargo.service.v1alpha1.UpdateStageResponse
+	30, // 68: akuity.io.kargo.service.v1alpha1.KargoService.DeleteStage:output_type -> akuity.io.kargo.service.v1alpha1.DeleteStageResponse
+	32, // 69: akuity.io.kargo.service.v1alpha1.KargoService.PromoteStage:output_type -> akuity.io.kargo.service.v1alpha1.PromoteStageResponse
+	34, // 70: akuity.io.kargo.service.v1alpha1.KargoService.PromoteSubscribers:output_type -> akuity.io.kargo.service.v1alpha1.PromoteSubscribersResponse
+	36, // 71: akuity.io.kargo.service.v1alpha1.KargoService.RefreshStage:output_type -> akuity.io.kargo.service.v1alpha1.RefreshStageResponse
+	39, // 72: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotions:output_type -> akuity.io.kargo.service.v1alpha1.ListPromotionsResponse
+	41, // 73: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotion:output_type -> akuity.io.kargo.service.v1alpha1.GetPromotionResponse
+	43, // 74: akuity.io.kargo.service.v1alpha1.KargoService.SetAutoPromotionForStage:output_type -> akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageResponse
+	45, // 75: akuity.io.kargo.service.v1alpha1.KargoService.CreatePromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyResponse
+	47, // 76: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotionPolicies:output_type -> akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesResponse
+	49, // 77: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.GetPromotionPolicyResponse
+	51, // 78: akuity.io.kargo.service.v1alpha1.KargoService.UpdatePromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyResponse
+	53, // 79: akuity.io.kargo.service.v1alpha1.KargoService.DeletePromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.DeletePromotionPolicyResponse
+	56, // 80: akuity.io.kargo.service.v1alpha1.KargoService.CreateProject:output_type -> akuity.io.kargo.service.v1alpha1.CreateProjectResponse
+	58, // 81: akuity.io.kargo.service.v1alpha1.KargoService.ListProjects:output_type -> akuity.io.kargo.service.v1alpha1.ListProjectsResponse
+	60, // 82: akuity.io.kargo.service.v1alpha1.KargoService.DeleteProject:output_type -> akuity.io.kargo.service.v1alpha1.DeleteProjectResponse
+	57, // [57:83] is the sub-list for method output_type
+	31, // [31:57] is the sub-list for method input_type
+	31, // [31:31] is the sub-list for extension type_name
+	31, // [31:31] is the sub-list for extension extendee
+	0,  // [0:31] is the sub-list for field type_name
 }
 
 func init() { file_service_v1alpha1_service_proto_init() }
@@ -4384,7 +4522,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[33].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*RefreshStageRequest); i {
+			switch v := v.(*PromoteSubscribersRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4396,7 +4534,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*RefreshStageResponse); i {
+			switch v := v.(*PromoteSubscribersResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4408,7 +4546,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[35].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*TypedPromotionPolicySpec); i {
+			switch v := v.(*RefreshStageRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4420,7 +4558,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[36].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListPromotionsRequest); i {
+			switch v := v.(*RefreshStageResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4432,7 +4570,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[37].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListPromotionsResponse); i {
+			switch v := v.(*TypedPromotionPolicySpec); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4444,7 +4582,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[38].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GetPromotionRequest); i {
+			switch v := v.(*ListPromotionsRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4456,7 +4594,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[39].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GetPromotionResponse); i {
+			switch v := v.(*ListPromotionsResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4468,7 +4606,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[40].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SetAutoPromotionForStageRequest); i {
+			switch v := v.(*GetPromotionRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4480,7 +4618,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*SetAutoPromotionForStageResponse); i {
+			switch v := v.(*GetPromotionResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4492,7 +4630,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[42].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CreatePromotionPolicyRequest); i {
+			switch v := v.(*SetAutoPromotionForStageRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4504,7 +4642,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[43].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CreatePromotionPolicyResponse); i {
+			switch v := v.(*SetAutoPromotionForStageResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4516,7 +4654,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListPromotionPoliciesRequest); i {
+			switch v := v.(*CreatePromotionPolicyRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4528,7 +4666,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListPromotionPoliciesResponse); i {
+			switch v := v.(*CreatePromotionPolicyResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4540,7 +4678,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GetPromotionPolicyRequest); i {
+			switch v := v.(*ListPromotionPoliciesRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4552,7 +4690,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*GetPromotionPolicyResponse); i {
+			switch v := v.(*ListPromotionPoliciesResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4564,7 +4702,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UpdatePromotionPolicyRequest); i {
+			switch v := v.(*GetPromotionPolicyRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4576,7 +4714,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*UpdatePromotionPolicyResponse); i {
+			switch v := v.(*GetPromotionPolicyResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4588,7 +4726,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DeletePromotionPolicyRequest); i {
+			switch v := v.(*UpdatePromotionPolicyRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4600,7 +4738,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DeletePromotionPolicyResponse); i {
+			switch v := v.(*UpdatePromotionPolicyResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4612,7 +4750,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Project); i {
+			switch v := v.(*DeletePromotionPolicyRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4624,7 +4762,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CreateProjectRequest); i {
+			switch v := v.(*DeletePromotionPolicyResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4636,7 +4774,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*CreateProjectResponse); i {
+			switch v := v.(*Project); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4648,7 +4786,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListProjectsRequest); i {
+			switch v := v.(*CreateProjectRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4660,7 +4798,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListProjectsResponse); i {
+			switch v := v.(*CreateProjectResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4672,7 +4810,7 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[57].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DeleteProjectRequest); i {
+			switch v := v.(*ListProjectsRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -4684,6 +4822,30 @@ func file_service_v1alpha1_service_proto_init() {
 			}
 		}
 		file_service_v1alpha1_service_proto_msgTypes[58].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ListProjectsResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_service_v1alpha1_service_proto_msgTypes[59].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DeleteProjectRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_service_v1alpha1_service_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*DeleteProjectResponse); i {
 			case 0:
 				return &v.state
@@ -4717,12 +4879,12 @@ func file_service_v1alpha1_service_proto_init() {
 		(*UpdateStageRequest_Typed)(nil),
 		(*UpdateStageRequest_Yaml)(nil),
 	}
-	file_service_v1alpha1_service_proto_msgTypes[36].OneofWrappers = []interface{}{}
-	file_service_v1alpha1_service_proto_msgTypes[42].OneofWrappers = []interface{}{
+	file_service_v1alpha1_service_proto_msgTypes[38].OneofWrappers = []interface{}{}
+	file_service_v1alpha1_service_proto_msgTypes[44].OneofWrappers = []interface{}{
 		(*CreatePromotionPolicyRequest_Typed)(nil),
 		(*CreatePromotionPolicyRequest_Yaml)(nil),
 	}
-	file_service_v1alpha1_service_proto_msgTypes[48].OneofWrappers = []interface{}{
+	file_service_v1alpha1_service_proto_msgTypes[50].OneofWrappers = []interface{}{
 		(*UpdatePromotionPolicyRequest_Typed)(nil),
 		(*UpdatePromotionPolicyRequest_Yaml)(nil),
 	}
@@ -4732,7 +4894,7 @@ func file_service_v1alpha1_service_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_service_v1alpha1_service_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   59,
+			NumMessages:   61,
 			NumExtensions: 0,
 			NumServices:   1,
 		},
diff --git a/pkg/api/service/v1alpha1/svcv1alpha1connect/service.connect.go b/pkg/api/service/v1alpha1/svcv1alpha1connect/service.connect.go
index a26be8d9..fdbd59e8 100644
--- a/pkg/api/service/v1alpha1/svcv1alpha1connect/service.connect.go
+++ b/pkg/api/service/v1alpha1/svcv1alpha1connect/service.connect.go
@@ -69,6 +69,9 @@ const (
 	// KargoServicePromoteStageProcedure is the fully-qualified name of the KargoService's PromoteStage
 	// RPC.
 	KargoServicePromoteStageProcedure = "/akuity.io.kargo.service.v1alpha1.KargoService/PromoteStage"
+	// KargoServicePromoteSubscribersProcedure is the fully-qualified name of the KargoService's
+	// PromoteSubscribers RPC.
+	KargoServicePromoteSubscribersProcedure = "/akuity.io.kargo.service.v1alpha1.KargoService/PromoteSubscribers"
 	// KargoServiceRefreshStageProcedure is the fully-qualified name of the KargoService's RefreshStage
 	// RPC.
 	KargoServiceRefreshStageProcedure = "/akuity.io.kargo.service.v1alpha1.KargoService/RefreshStage"
@@ -124,6 +127,7 @@ type KargoServiceClient interface {
 	UpdateStage(context.Context, *connect.Request[v1alpha1.UpdateStageRequest]) (*connect.Response[v1alpha1.UpdateStageResponse], error)
 	DeleteStage(context.Context, *connect.Request[v1alpha1.DeleteStageRequest]) (*connect.Response[v1alpha1.DeleteStageResponse], error)
 	PromoteStage(context.Context, *connect.Request[v1alpha1.PromoteStageRequest]) (*connect.Response[v1alpha1.PromoteStageResponse], error)
+	PromoteSubscribers(context.Context, *connect.Request[v1alpha1.PromoteSubscribersRequest]) (*connect.Response[v1alpha1.PromoteSubscribersResponse], error)
 	RefreshStage(context.Context, *connect.Request[v1alpha1.RefreshStageRequest]) (*connect.Response[v1alpha1.RefreshStageResponse], error)
 	// Promotion APIs
 	ListPromotions(context.Context, *connect.Request[v1alpha1.ListPromotionsRequest]) (*connect.Response[v1alpha1.ListPromotionsResponse], error)
@@ -214,6 +218,11 @@ func NewKargoServiceClient(httpClient connect.HTTPClient, baseURL string, opts .
 			baseURL+KargoServicePromoteStageProcedure,
 			opts...,
 		),
+		promoteSubscribers: connect.NewClient[v1alpha1.PromoteSubscribersRequest, v1alpha1.PromoteSubscribersResponse](
+			httpClient,
+			baseURL+KargoServicePromoteSubscribersProcedure,
+			opts...,
+		),
 		refreshStage: connect.NewClient[v1alpha1.RefreshStageRequest, v1alpha1.RefreshStageResponse](
 			httpClient,
 			baseURL+KargoServiceRefreshStageProcedure,
@@ -292,6 +301,7 @@ type kargoServiceClient struct {
 	updateStage              *connect.Client[v1alpha1.UpdateStageRequest, v1alpha1.UpdateStageResponse]
 	deleteStage              *connect.Client[v1alpha1.DeleteStageRequest, v1alpha1.DeleteStageResponse]
 	promoteStage             *connect.Client[v1alpha1.PromoteStageRequest, v1alpha1.PromoteStageResponse]
+	promoteSubscribers       *connect.Client[v1alpha1.PromoteSubscribersRequest, v1alpha1.PromoteSubscribersResponse]
 	refreshStage             *connect.Client[v1alpha1.RefreshStageRequest, v1alpha1.RefreshStageResponse]
 	listPromotions           *connect.Client[v1alpha1.ListPromotionsRequest, v1alpha1.ListPromotionsResponse]
 	getPromotion             *connect.Client[v1alpha1.GetPromotionRequest, v1alpha1.GetPromotionResponse]
@@ -371,6 +381,11 @@ func (c *kargoServiceClient) PromoteStage(ctx context.Context, req *connect.Requ
 	return c.promoteStage.CallUnary(ctx, req)
 }
 
+// PromoteSubscribers calls akuity.io.kargo.service.v1alpha1.KargoService.PromoteSubscribers.
+func (c *kargoServiceClient) PromoteSubscribers(ctx context.Context, req *connect.Request[v1alpha1.PromoteSubscribersRequest]) (*connect.Response[v1alpha1.PromoteSubscribersResponse], error) {
+	return c.promoteSubscribers.CallUnary(ctx, req)
+}
+
 // RefreshStage calls akuity.io.kargo.service.v1alpha1.KargoService.RefreshStage.
 func (c *kargoServiceClient) RefreshStage(ctx context.Context, req *connect.Request[v1alpha1.RefreshStageRequest]) (*connect.Response[v1alpha1.RefreshStageResponse], error) {
 	return c.refreshStage.CallUnary(ctx, req)
@@ -450,6 +465,7 @@ type KargoServiceHandler interface {
 	UpdateStage(context.Context, *connect.Request[v1alpha1.UpdateStageRequest]) (*connect.Response[v1alpha1.UpdateStageResponse], error)
 	DeleteStage(context.Context, *connect.Request[v1alpha1.DeleteStageRequest]) (*connect.Response[v1alpha1.DeleteStageResponse], error)
 	PromoteStage(context.Context, *connect.Request[v1alpha1.PromoteStageRequest]) (*connect.Response[v1alpha1.PromoteStageResponse], error)
+	PromoteSubscribers(context.Context, *connect.Request[v1alpha1.PromoteSubscribersRequest]) (*connect.Response[v1alpha1.PromoteSubscribersResponse], error)
 	RefreshStage(context.Context, *connect.Request[v1alpha1.RefreshStageRequest]) (*connect.Response[v1alpha1.RefreshStageResponse], error)
 	// Promotion APIs
 	ListPromotions(context.Context, *connect.Request[v1alpha1.ListPromotionsRequest]) (*connect.Response[v1alpha1.ListPromotionsResponse], error)
@@ -536,6 +552,11 @@ func NewKargoServiceHandler(svc KargoServiceHandler, opts ...connect.HandlerOpti
 		svc.PromoteStage,
 		opts...,
 	)
+	kargoServicePromoteSubscribersHandler := connect.NewUnaryHandler(
+		KargoServicePromoteSubscribersProcedure,
+		svc.PromoteSubscribers,
+		opts...,
+	)
 	kargoServiceRefreshStageHandler := connect.NewUnaryHandler(
 		KargoServiceRefreshStageProcedure,
 		svc.RefreshStage,
@@ -624,6 +645,8 @@ func NewKargoServiceHandler(svc KargoServiceHandler, opts ...connect.HandlerOpti
 			kargoServiceDeleteStageHandler.ServeHTTP(w, r)
 		case KargoServicePromoteStageProcedure:
 			kargoServicePromoteStageHandler.ServeHTTP(w, r)
+		case KargoServicePromoteSubscribersProcedure:
+			kargoServicePromoteSubscribersHandler.ServeHTTP(w, r)
 		case KargoServiceRefreshStageProcedure:
 			kargoServiceRefreshStageHandler.ServeHTTP(w, r)
 		case KargoServiceListPromotionsProcedure:
@@ -709,6 +732,10 @@ func (UnimplementedKargoServiceHandler) PromoteStage(context.Context, *connect.R
 	return nil, connect.NewError(connect.CodeUnimplemented, errors.New("akuity.io.kargo.service.v1alpha1.KargoService.PromoteStage is not implemented"))
 }
 
+func (UnimplementedKargoServiceHandler) PromoteSubscribers(context.Context, *connect.Request[v1alpha1.PromoteSubscribersRequest]) (*connect.Response[v1alpha1.PromoteSubscribersResponse], error) {
+	return nil, connect.NewError(connect.CodeUnimplemented, errors.New("akuity.io.kargo.service.v1alpha1.KargoService.PromoteSubscribers is not implemented"))
+}
+
 func (UnimplementedKargoServiceHandler) RefreshStage(context.Context, *connect.Request[v1alpha1.RefreshStageRequest]) (*connect.Response[v1alpha1.RefreshStageResponse], error) {
 	return nil, connect.NewError(connect.CodeUnimplemented, errors.New("akuity.io.kargo.service.v1alpha1.KargoService.RefreshStage is not implemented"))
 }
diff --git a/ui/src/gen/service/v1alpha1/service-KargoService_connectquery.ts b/ui/src/gen/service/v1alpha1/service-KargoService_connectquery.ts
index 6afbacf5..050965a2 100644
--- a/ui/src/gen/service/v1alpha1/service-KargoService_connectquery.ts
+++ b/ui/src/gen/service/v1alpha1/service-KargoService_connectquery.ts
@@ -5,7 +5,7 @@
 
 import { createQueryService } from "@bufbuild/connect-query";
 import { MethodKind } from "@bufbuild/protobuf";
-import { AdminLoginRequest, AdminLoginResponse, CreateProjectRequest, CreateProjectResponse, CreatePromotionPolicyRequest, CreatePromotionPolicyResponse, CreateResourceRequest, CreateResourceResponse, CreateStageRequest, CreateStageResponse, DeleteProjectRequest, DeleteProjectResponse, DeletePromotionPolicyRequest, DeletePromotionPolicyResponse, DeleteResourceRequest, DeleteResourceResponse, DeleteStageRequest, DeleteStageResponse, GetPromotionPolicyRequest, GetPromotionPolicyResponse, GetPromotionRequest, GetPromotionResponse, GetPublicConfigRequest, GetPublicConfigResponse, GetStageRequest, GetStageResponse, GetVersionInfoRequest, GetVersionInfoResponse, ListProjectsRequest, ListProjectsResponse, ListPromotionPoliciesRequest, ListPromotionPoliciesResponse, ListPromotionsRequest, ListPromotionsResponse, ListStagesRequest, ListStagesResponse, PromoteStageRequest, PromoteStageResponse, RefreshStageRequest, RefreshStageResponse, SetAutoPromotionForStageRequest, SetAutoPromotionForStageResponse, UpdatePromotionPolicyRequest, UpdatePromotionPolicyResponse, UpdateResourceRequest, UpdateResourceResponse, UpdateStageRequest, UpdateStageResponse } from "./service_pb.js";
+import { AdminLoginRequest, AdminLoginResponse, CreateProjectRequest, CreateProjectResponse, CreatePromotionPolicyRequest, CreatePromotionPolicyResponse, CreateResourceRequest, CreateResourceResponse, CreateStageRequest, CreateStageResponse, DeleteProjectRequest, DeleteProjectResponse, DeletePromotionPolicyRequest, DeletePromotionPolicyResponse, DeleteResourceRequest, DeleteResourceResponse, DeleteStageRequest, DeleteStageResponse, GetPromotionPolicyRequest, GetPromotionPolicyResponse, GetPromotionRequest, GetPromotionResponse, GetPublicConfigRequest, GetPublicConfigResponse, GetStageRequest, GetStageResponse, GetVersionInfoRequest, GetVersionInfoResponse, ListProjectsRequest, ListProjectsResponse, ListPromotionPoliciesRequest, ListPromotionPoliciesResponse, ListPromotionsRequest, ListPromotionsResponse, ListStagesRequest, ListStagesResponse, PromoteStageRequest, PromoteStageResponse, PromoteSubscribersRequest, PromoteSubscribersResponse, RefreshStageRequest, RefreshStageResponse, SetAutoPromotionForStageRequest, SetAutoPromotionForStageResponse, UpdatePromotionPolicyRequest, UpdatePromotionPolicyResponse, UpdateResourceRequest, UpdateResourceResponse, UpdateStageRequest, UpdateStageResponse } from "./service_pb.js";
 
 export const typeName = "akuity.io.kargo.service.v1alpha1.KargoService";
 
@@ -216,6 +216,23 @@ export const promoteStage = createQueryService({
   },
 }).promoteStage;
 
+/**
+ * @generated from rpc akuity.io.kargo.service.v1alpha1.KargoService.PromoteSubscribers
+ */
+export const promoteSubscribers = createQueryService({
+  service: {
+    methods: {
+      promoteSubscribers: {
+        name: "PromoteSubscribers",
+        kind: MethodKind.Unary,
+        I: PromoteSubscribersRequest,
+        O: PromoteSubscribersResponse,
+      },
+    },
+    typeName: "akuity.io.kargo.service.v1alpha1.KargoService",
+  },
+}).promoteSubscribers;
+
 /**
  * @generated from rpc akuity.io.kargo.service.v1alpha1.KargoService.RefreshStage
  */
diff --git a/ui/src/gen/service/v1alpha1/service_connect.ts b/ui/src/gen/service/v1alpha1/service_connect.ts
index e7056039..79e23b14 100644
--- a/ui/src/gen/service/v1alpha1/service_connect.ts
+++ b/ui/src/gen/service/v1alpha1/service_connect.ts
@@ -3,7 +3,7 @@
 /* eslint-disable */
 // @ts-nocheck
 
-import { AdminLoginRequest, AdminLoginResponse, CreateProjectRequest, CreateProjectResponse, CreatePromotionPolicyRequest, CreatePromotionPolicyResponse, CreateResourceRequest, CreateResourceResponse, CreateStageRequest, CreateStageResponse, DeleteProjectRequest, DeleteProjectResponse, DeletePromotionPolicyRequest, DeletePromotionPolicyResponse, DeleteResourceRequest, DeleteResourceResponse, DeleteStageRequest, DeleteStageResponse, GetPromotionPolicyRequest, GetPromotionPolicyResponse, GetPromotionRequest, GetPromotionResponse, GetPublicConfigRequest, GetPublicConfigResponse, GetStageRequest, GetStageResponse, GetVersionInfoRequest, GetVersionInfoResponse, ListProjectsRequest, ListProjectsResponse, ListPromotionPoliciesRequest, ListPromotionPoliciesResponse, ListPromotionsRequest, ListPromotionsResponse, ListStagesRequest, ListStagesResponse, PromoteStageRequest, PromoteStageResponse, RefreshStageRequest, RefreshStageResponse, SetAutoPromotionForStageRequest, SetAutoPromotionForStageResponse, UpdatePromotionPolicyRequest, UpdatePromotionPolicyResponse, UpdateResourceRequest, UpdateResourceResponse, UpdateStageRequest, UpdateStageResponse, WatchStagesRequest, WatchStagesResponse } from "./service_pb.js";
+import { AdminLoginRequest, AdminLoginResponse, CreateProjectRequest, CreateProjectResponse, CreatePromotionPolicyRequest, CreatePromotionPolicyResponse, CreateResourceRequest, CreateResourceResponse, CreateStageRequest, CreateStageResponse, DeleteProjectRequest, DeleteProjectResponse, DeletePromotionPolicyRequest, DeletePromotionPolicyResponse, DeleteResourceRequest, DeleteResourceResponse, DeleteStageRequest, DeleteStageResponse, GetPromotionPolicyRequest, GetPromotionPolicyResponse, GetPromotionRequest, GetPromotionResponse, GetPublicConfigRequest, GetPublicConfigResponse, GetStageRequest, GetStageResponse, GetVersionInfoRequest, GetVersionInfoResponse, ListProjectsRequest, ListProjectsResponse, ListPromotionPoliciesRequest, ListPromotionPoliciesResponse, ListPromotionsRequest, ListPromotionsResponse, ListStagesRequest, ListStagesResponse, PromoteStageRequest, PromoteStageResponse, PromoteSubscribersRequest, PromoteSubscribersResponse, RefreshStageRequest, RefreshStageResponse, SetAutoPromotionForStageRequest, SetAutoPromotionForStageResponse, UpdatePromotionPolicyRequest, UpdatePromotionPolicyResponse, UpdateResourceRequest, UpdateResourceResponse, UpdateStageRequest, UpdateStageResponse, WatchStagesRequest, WatchStagesResponse } from "./service_pb.js";
 import { MethodKind } from "@bufbuild/protobuf";
 
 /**
@@ -132,6 +132,15 @@ export const KargoService = {
       O: PromoteStageResponse,
       kind: MethodKind.Unary,
     },
+    /**
+     * @generated from rpc akuity.io.kargo.service.v1alpha1.KargoService.PromoteSubscribers
+     */
+    promoteSubscribers: {
+      name: "PromoteSubscribers",
+      I: PromoteSubscribersRequest,
+      O: PromoteSubscribersResponse,
+      kind: MethodKind.Unary,
+    },
     /**
      * @generated from rpc akuity.io.kargo.service.v1alpha1.KargoService.RefreshStage
      */
diff --git a/ui/src/gen/service/v1alpha1/service_pb.ts b/ui/src/gen/service/v1alpha1/service_pb.ts
index 1447aed9..4a4b5bc0 100644
--- a/ui/src/gen/service/v1alpha1/service_pb.ts
+++ b/ui/src/gen/service/v1alpha1/service_pb.ts
@@ -1391,6 +1391,92 @@ export class PromoteStageResponse extends Message<PromoteStageResponse> {
   }
 }
 
+/**
+ * @generated from message akuity.io.kargo.service.v1alpha1.PromoteSubscribersRequest
+ */
+export class PromoteSubscribersRequest extends Message<PromoteSubscribersRequest> {
+  /**
+   * @generated from field: string project = 1;
+   */
+  project = "";
+
+  /**
+   * @generated from field: string stage = 2;
+   */
+  stage = "";
+
+  /**
+   * @generated from field: string freight = 3;
+   */
+  freight = "";
+
+  constructor(data?: PartialMessage<PromoteSubscribersRequest>) {
+    super();
+    proto3.util.initPartial(data, this);
+  }
+
+  static readonly runtime: typeof proto3 = proto3;
+  static readonly typeName = "akuity.io.kargo.service.v1alpha1.PromoteSubscribersRequest";
+  static readonly fields: FieldList = proto3.util.newFieldList(() => [
+    { no: 1, name: "project", kind: "scalar", T: 9 /* ScalarType.STRING */ },
+    { no: 2, name: "stage", kind: "scalar", T: 9 /* ScalarType.STRING */ },
+    { no: 3, name: "freight", kind: "scalar", T: 9 /* ScalarType.STRING */ },
+  ]);
+
+  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PromoteSubscribersRequest {
+    return new PromoteSubscribersRequest().fromBinary(bytes, options);
+  }
+
+  static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PromoteSubscribersRequest {
+    return new PromoteSubscribersRequest().fromJson(jsonValue, options);
+  }
+
+  static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PromoteSubscribersRequest {
+    return new PromoteSubscribersRequest().fromJsonString(jsonString, options);
+  }
+
+  static equals(a: PromoteSubscribersRequest | PlainMessage<PromoteSubscribersRequest> | undefined, b: PromoteSubscribersRequest | PlainMessage<PromoteSubscribersRequest> | undefined): boolean {
+    return proto3.util.equals(PromoteSubscribersRequest, a, b);
+  }
+}
+
+/**
+ * @generated from message akuity.io.kargo.service.v1alpha1.PromoteSubscribersResponse
+ */
+export class PromoteSubscribersResponse extends Message<PromoteSubscribersResponse> {
+  /**
+   * @generated from field: repeated github.com.akuity.kargo.pkg.api.v1alpha1.Promotion promotions = 1;
+   */
+  promotions: Promotion[] = [];
+
+  constructor(data?: PartialMessage<PromoteSubscribersResponse>) {
+    super();
+    proto3.util.initPartial(data, this);
+  }
+
+  static readonly runtime: typeof proto3 = proto3;
+  static readonly typeName = "akuity.io.kargo.service.v1alpha1.PromoteSubscribersResponse";
+  static readonly fields: FieldList = proto3.util.newFieldList(() => [
+    { no: 1, name: "promotions", kind: "message", T: Promotion, repeated: true },
+  ]);
+
+  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): PromoteSubscribersResponse {
+    return new PromoteSubscribersResponse().fromBinary(bytes, options);
+  }
+
+  static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): PromoteSubscribersResponse {
+    return new PromoteSubscribersResponse().fromJson(jsonValue, options);
+  }
+
+  static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): PromoteSubscribersResponse {
+    return new PromoteSubscribersResponse().fromJsonString(jsonString, options);
+  }
+
+  static equals(a: PromoteSubscribersResponse | PlainMessage<PromoteSubscribersResponse> | undefined, b: PromoteSubscribersResponse | PlainMessage<PromoteSubscribersResponse> | undefined): boolean {
+    return proto3.util.equals(PromoteSubscribersResponse, a, b);
+  }
+}
+
 /**
  * @generated from message akuity.io.kargo.service.v1alpha1.RefreshStageRequest
  */


Test patch:
diff --git a/internal/api/promote_stage_v1alpha1_test.go b/internal/api/promote_stage_v1alpha1_test.go
index 006e62c8..24f8f5b2 100644
--- a/internal/api/promote_stage_v1alpha1_test.go
+++ b/internal/api/promote_stage_v1alpha1_test.go
@@ -107,7 +107,7 @@ func TestPromoteStage(t *testing.T) {
 				return
 			}
 
-			require.True(t, strings.HasPrefix(res.Msg.GetPromotion().GetMetadata().GetName(), ts.req.GetName()+"-"))
+			require.True(t, strings.HasPrefix(res.Msg.GetPromotion().GetMetadata().GetName(), ts.req.GetName()+"."))
 			require.Equal(t, ts.req.GetName(), res.Msg.GetPromotion().GetSpec().GetStage())
 			require.Equal(t, ts.req.GetState(), res.Msg.GetPromotion().GetSpec().GetState())
 
diff --git a/internal/api/promote_subscribers_v1alpha1_test.go b/internal/api/promote_subscribers_v1alpha1_test.go
new file mode 100644
index 00000000..78e9c1d7
--- /dev/null
+++ b/internal/api/promote_subscribers_v1alpha1_test.go
@@ -0,0 +1,149 @@
+package api
+
+import (
+	"context"
+	"strings"
+	"testing"
+
+	"connectrpc.com/connect"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/client-go/rest"
+	libClient "sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+	kubev1alpha1 "github.com/akuity/kargo/api/v1alpha1"
+	"github.com/akuity/kargo/internal/api/kubernetes"
+	"github.com/akuity/kargo/internal/api/user"
+	svcv1alpha1 "github.com/akuity/kargo/pkg/api/service/v1alpha1"
+)
+
+func TestPromoteSubscribers(t *testing.T) {
+	testSets := map[string]struct {
+		req                *svcv1alpha1.PromoteSubscribersRequest
+		errMsg             string
+		expectedCode       connect.Code
+		expectedPromotions int32
+	}{
+		"empty freight": {
+			req: &svcv1alpha1.PromoteSubscribersRequest{
+				Project: "kargo-demo",
+				Stage:   "upstream",
+			},
+			errMsg:       "freight should not be empty",
+			expectedCode: connect.CodeInvalidArgument,
+		},
+		"non-existing Stage": {
+			req: &svcv1alpha1.PromoteSubscribersRequest{
+				Project: "kargo-demo",
+				Stage:   "does-not-exist",
+				Freight: "c353927ca7af42b38c0cdcfa393b2c552740e547",
+			},
+			errMsg:       `stage "kargo-demo" not found`,
+			expectedCode: connect.CodeNotFound,
+		},
+		"existing Stage with non-existing freight": {
+			req: &svcv1alpha1.PromoteSubscribersRequest{
+				Project: "kargo-demo",
+				Stage:   "upstream",
+				Freight: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
+			},
+			errMsg:       `freight "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" not found in Stage`,
+			expectedCode: connect.CodeNotFound,
+		},
+		"existing Stage with no subscribers": {
+			req: &svcv1alpha1.PromoteSubscribersRequest{
+				Project: "kargo-demo",
+				Stage:   "downstream1",
+				Freight: "f08b2e72c9b2b7b263da6d55f9536e49b5ce972c",
+			},
+			errMsg:       `Stage "downstream1" has no subscribers`,
+			expectedCode: connect.CodeNotFound,
+		},
+		"existing Stage with unhealthy freight": {
+			req: &svcv1alpha1.PromoteSubscribersRequest{
+				Project: "kargo-demo",
+				Stage:   "upstream",
+				Freight: "abc1237ca7af42b38c0cdcfa393b2c552740e547",
+			},
+			errMsg:       "Cannot promote freight with health status: Unhealthy",
+			expectedCode: connect.CodeInvalidArgument,
+		},
+		"existing Stage with subscribers": {
+			req: &svcv1alpha1.PromoteSubscribersRequest{
+				Project: "kargo-demo",
+				Stage:   "upstream",
+				Freight: "c353927ca7af42b38c0cdcfa393b2c552740e547",
+			},
+			expectedPromotions: 2,
+		},
+	}
+	for name, ts := range testSets {
+		ts := ts
+		t.Run(name, func(t *testing.T) {
+			t.Parallel()
+
+			// Simulate an admin user to prevent any authz issues with the authorizing
+			// client.
+			ctx := user.ContextWithInfo(
+				context.Background(),
+				user.Info{
+					IsAdmin: true,
+				},
+			)
+
+			client, err := kubernetes.NewClient(
+				ctx,
+				&rest.Config{},
+				kubernetes.ClientOptions{
+					NewInternalClient: func(
+						context.Context,
+						*rest.Config,
+						*runtime.Scheme,
+					) (libClient.Client, error) {
+						return fake.NewClientBuilder().
+							WithScheme(mustNewScheme()).
+							WithObjects(
+								mustNewObject[corev1.Namespace]("testdata/namespace.yaml"),
+							).
+							WithLists(&kubev1alpha1.StageList{
+								Items: []kubev1alpha1.Stage{
+									*mustNewObject[kubev1alpha1.Stage]("testdata/promote-subscribers-upstream.yaml"),
+									*mustNewObject[kubev1alpha1.Stage]("testdata/promote-subscribers-downstream1.yaml"),
+									*mustNewObject[kubev1alpha1.Stage]("testdata/promote-subscribers-downstream2.yaml"),
+								},
+							}).
+							Build(), nil
+					},
+				},
+			)
+			require.NoError(t, err)
+
+			res, err := (&server{
+				client: client,
+			}).PromoteSubscribers(ctx, connect.NewRequest(ts.req))
+			if ts.errMsg != "" {
+				require.ErrorContains(t, err, ts.errMsg)
+				require.Equal(t, ts.expectedCode, connect.CodeOf(err))
+				return
+			}
+			assert.Len(t, res.Msg.GetPromotions(), int(ts.expectedPromotions))
+			for _, p := range res.Msg.GetPromotions() {
+				parts := strings.Split(p.GetMetadata().GetName(), ".")
+				require.True(t, strings.HasPrefix(parts[0], "downstream"))
+				require.True(t, strings.HasPrefix(p.GetSpec().GetStage(), "downstream"))
+				require.Equal(t, parts[2], ts.req.Freight[0:7])
+				require.Equal(t, ts.req.GetFreight(), p.GetSpec().GetState())
+
+				var actual kubev1alpha1.Promotion
+				require.NoError(t, client.Get(ctx, libClient.ObjectKey{
+					Namespace: ts.req.GetProject(),
+					Name:      p.GetMetadata().GetName(),
+				}, &actual))
+			}
+
+		})
+	}
+}
diff --git a/internal/api/refresh_stage_v1alpha1_test.go b/internal/api/refresh_stage_v1alpha1_test.go
index d72b184f..b490d1ee 100644
--- a/internal/api/refresh_stage_v1alpha1_test.go
+++ b/internal/api/refresh_stage_v1alpha1_test.go
@@ -108,13 +108,13 @@ func TestRefreshStage(t *testing.T) {
 				return
 			}
 			require.NoError(t, err)
-			// Make sure we set timestamp is close to now
-			// Assume it doesn't take 3 seconds to run this unit test.
 			stage := res.Msg.GetStage()
 			annotation := stage.Metadata.Annotations[kubev1alpha1.AnnotationKeyRefresh]
 			refreshTime, err := time.Parse(time.RFC3339, annotation)
 			require.NoError(t, err)
-			require.LessOrEqual(t, time.Now().UTC().Sub(refreshTime), 3*time.Second)
+			// Make sure we set timestamp is close to now
+			// Assume it doesn't take 3 seconds to run this unit test.
+			require.WithinDuration(t, time.Now(), refreshTime, 3*time.Second)
 			require.Equal(t, ts.req.GetProject(), stage.GetMetadata().GetNamespace())
 			require.Equal(t, ts.req.GetName(), stage.GetMetadata().GetName())
 		})
diff --git a/internal/api/testdata/promote-subscribers-downstream1.yaml b/internal/api/testdata/promote-subscribers-downstream1.yaml
new file mode 100644
index 00000000..e1afd257
--- /dev/null
+++ b/internal/api/testdata/promote-subscribers-downstream1.yaml
@@ -0,0 +1,26 @@
+apiVersion: kargo.akuity.io/v1alpha1
+kind: Stage
+metadata:
+  name: downstream1
+  namespace: kargo-demo
+spec:
+  promotionMechanisms:
+    gitRepoUpdates:
+    - kustomize:
+        images:
+        - image: ghcr.io/akuity/guestbook
+          path: env/staging
+      readBranch: ""
+      repoURL: https://github.com/akuity/guestbook-deploy.git
+      writeBranch: main
+  subscriptions:
+    upstreamStages:
+    - name: upstream
+status:
+  history:
+  - firstSeen: "2023-08-25T06:04:19Z"
+    id: f08b2e72c9b2b7b263da6d55f9536e49b5ce972c
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.4
+    provenance: dev
diff --git a/internal/api/testdata/promote-subscribers-downstream2.yaml b/internal/api/testdata/promote-subscribers-downstream2.yaml
new file mode 100644
index 00000000..7c2d3721
--- /dev/null
+++ b/internal/api/testdata/promote-subscribers-downstream2.yaml
@@ -0,0 +1,18 @@
+apiVersion: kargo.akuity.io/v1alpha1
+kind: Stage
+metadata:
+  name: downstream2
+  namespace: kargo-demo
+spec:
+  promotionMechanisms:
+    gitRepoUpdates:
+    - kustomize:
+        images:
+        - image: ghcr.io/akuity/guestbook
+          path: env/staging
+      readBranch: ""
+      repoURL: https://github.com/akuity/guestbook-deploy.git
+      writeBranch: main
+  subscriptions:
+    upstreamStages:
+    - name: upstream
diff --git a/internal/api/testdata/promote-subscribers-upstream.yaml b/internal/api/testdata/promote-subscribers-upstream.yaml
new file mode 100644
index 00000000..530d353f
--- /dev/null
+++ b/internal/api/testdata/promote-subscribers-upstream.yaml
@@ -0,0 +1,52 @@
+apiVersion: kargo.akuity.io/v1alpha1
+kind: Stage
+metadata:
+  name: upstream
+  namespace: kargo-demo
+spec:
+  promotionMechanisms:
+    gitRepoUpdates:
+    - kustomize:
+        images:
+        - image: ghcr.io/akuity/guestbook
+          path: env/staging
+      readBranch: ""
+      repoURL: https://github.com/akuity/guestbook-deploy.git
+      writeBranch: main
+  subscriptions:
+    upstreamStages:
+    - name: dev
+status:
+  availableStates:
+  - firstSeen: "2023-08-28T15:18:57Z"
+    id: c353927ca7af42b38c0cdcfa393b2c552740e547
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.1
+    provenance: dev
+  currentState:
+    firstSeen: "2023-08-28T15:18:57Z"
+    health:
+      status: Healthy
+    id: c353927ca7af42b38c0cdcfa393b2c552740e547
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.1
+    provenance: dev
+  history:
+  - firstSeen: "2023-08-28T15:18:57Z"
+    health:
+      status: Healthy
+    id: c353927ca7af42b38c0cdcfa393b2c552740e547
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.1
+    provenance: dev
+  - firstSeen: "2023-08-28T15:17:57Z"
+    health:
+      status: Unhealthy
+    id: abc1237ca7af42b38c0cdcfa393b2c552740e547
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.0
+    provenance: dev
diff --git a/internal/kargo/kargo_test.go b/internal/kargo/kargo_test.go
new file mode 100644
index 00000000..ab8acd81
--- /dev/null
+++ b/internal/kargo/kargo_test.go
@@ -0,0 +1,96 @@
+package kargo
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	api "github.com/akuity/kargo/api/v1alpha1"
+	kubev1alpha1 "github.com/akuity/kargo/api/v1alpha1"
+	"github.com/akuity/kargo/internal/controller"
+)
+
+func TestNewPromotion(t *testing.T) {
+	const (
+		testFreight          = "f08b2e72c9b2b7b263da6d55f9536e49b5ce972c"
+		veryLongResourceName = "the-kubernetes-maximum-length-of-a-label-value-is-only-sixty-" +
+			"three-characters-meanwhile-the-maximum-length-of-a-kubernetes-resource-name-" +
+			"is-two-hundred-and-fifty-three-characters-but-this-string-is-two-hundred-" +
+			"and-thirty-seven-characters"
+	)
+	t.Parallel()
+	testCases := []struct {
+		name       string
+		stage      api.Stage
+		freight    string
+		assertions func(*testing.T, api.Stage, kubev1alpha1.Promotion)
+	}{
+		{
+			name: "Promote stage",
+			stage: api.Stage{
+				ObjectMeta: metav1.ObjectMeta{
+					UID:       "80b44831-ac8d-4900-9df9-ee95f80c0fae",
+					Name:      "test",
+					Namespace: "kargo-demo",
+				},
+			},
+			freight: testFreight,
+			assertions: func(t *testing.T, stage api.Stage, promo kubev1alpha1.Promotion) {
+				parts := strings.Split(promo.Name, ".")
+				require.Equal(t, "test", parts[0])
+				require.Equal(t, testFreight[0:7], parts[2])
+			},
+		},
+		{
+			name: "Promote stage with shard",
+			stage: api.Stage{
+				ObjectMeta: metav1.ObjectMeta{
+					UID:       "80b44831-ac8d-4900-9df9-ee95f80c0fae",
+					Name:      "test",
+					Namespace: "kargo-demo",
+					Labels: map[string]string{
+						controller.ShardLabelKey: "another-shard",
+					},
+				},
+			},
+			freight: testFreight,
+			assertions: func(t *testing.T, stage api.Stage, promo kubev1alpha1.Promotion) {
+				parts := strings.Split(promo.Name, ".")
+				require.Equal(t, "test", parts[0])
+				require.Equal(t, testFreight[0:7], parts[2])
+				require.Equal(t, "another-shard", promo.Labels[controller.ShardLabelKey])
+			},
+		},
+		{
+			name: "Promote stage with very long name",
+			stage: api.Stage{
+				ObjectMeta: metav1.ObjectMeta{
+					UID:       "80b44831-ac8d-4900-9df9-ee95f80c0fae",
+					Name:      veryLongResourceName,
+					Namespace: "kargo-demo",
+				},
+			},
+			freight: testFreight,
+			assertions: func(t *testing.T, stage api.Stage, promo kubev1alpha1.Promotion) {
+				require.Len(t, promo.Name, 253)
+				parts := strings.Split(promo.Name, ".")
+				require.Equal(t, veryLongResourceName[0:maxStageNamePrefixLength], parts[0])
+				require.Equal(t, testFreight[0:7], parts[2])
+			},
+		},
+	}
+	for _, tc := range testCases {
+		tc := tc
+		t.Run(tc.name, func(t *testing.T) {
+			promo := NewPromotion(tc.stage, tc.freight)
+			require.True(t, metav1.IsControlledBy(&promo, &tc.stage))
+			require.Equal(t, tc.freight, promo.Spec.State)
+			require.Equal(t, tc.stage.Name, promo.Spec.Stage)
+			require.Equal(t, tc.freight, promo.Spec.State)
+			require.LessOrEqual(t, len(promo.Name), 253)
+			tc.assertions(t, tc.stage, promo)
+		})
+	}
+}