Repository: akuity/kargo
Pull request #629 (2023-09-01 08:40:55)

feat: Add QueryFreight API
Resolves https://github.com/akuity/kargo/issues/625

This PR introduces the QueryFreight API that is needed to power the timeline (aka freight-line) view. The term Query is used instead of List, because this API supports `group_by` and `order_by` and returns a nested structure instead of a simple list.

Here is an example query:
```json
{"project":"kargo-guestbook","group_by":"container_repo"}
```

And the return value:
```json
{
    "groups": {
        "ghcr.io/jessesuen/guestbook": {
            "freights": [
                {
                    "id": "f08b2e72c9b2b7b263da6d55f9536e49b5ce972c",
                    "first_seen": "2023-08-25T06:04:19Z",
                    "provenance": "",
                    "commits": [],
                    "images": [
                        {
                            "repo_url": "ghcr.io/jessesuen/guestbook",
                            "tag": "v0.0.4"
                        }
                    ],
                    "charts": []
                },
                {
                    "id": "c353927ca7af42b38c0cdcfa393b2c552740e547",
                    "first_seen": "2023-08-28T15:18:57Z",
                    "provenance": "",
                    "commits": [],
                    "images": [
                        {
                            "repo_url": "ghcr.io/jessesuen/guestbook",
                            "tag": "v0.0.5"
                        }
                    ],
                    "charts": []
                },
                {
                    "id": "6075e1ab632d6ed194b6ecda069998c46702b6ea",
                    "first_seen": "2023-08-28T15:23:57Z",
                    "provenance": "",
                    "commits": [],
                    "images": [
                        {
                            "repo_url": "ghcr.io/jessesuen/guestbook",
                            "tag": "v0.0.6"
                        }
                    ],
                    "charts": []
                }
            ]
        }
    }
}
```

NOTE: protobuf does not provide a way to support something like:
```proto
message QueryFreightsResponse {
  map<string, repeated github.com.akuity.kargo.pkg.api.v1alpha1.StageState> groups = 1;
}
```

And so the return value is more deeply nested than I would have liked.

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


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 901959d272110b220b128658333300a31ddb6e11 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/docs-kargo-akuity-io/deploys/64f1a3997418320008428e44 |
jessesuen: Another example:

Request:
```json
{
    "project": "kargo-guestbook",
    "stage": "prod-euw1"
}
```

Result:
```json
{
    "groups": {
        "": {
            "freights": [
                {
                    "id": "c353927ca7af42b38c0cdcfa393b2c552740e547",
                    "first_seen": "2023-08-28T15:18:57Z",
                    "provenance": "",
                    "commits": [],
                    "images": [
                        {
                            "repo_url": "ghcr.io/jessesuen/guestbook",
                            "tag": "v0.0.5"
                        }
                    ],
                    "charts": []
                }
            ]
        }
    }
}
```
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/akuity/kargo/pull/629?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity) Report
Patch coverage: **`47.63%`** and project coverage change: **`-0.28%`** :warning:
> Comparison is base [(`86c6621`)](https://app.codecov.io/gh/akuity/kargo/commit/86c662122a94286b0fb7ed433e6c3febcba00bda?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity) 44.69% compared to head [(`901959d`)](https://app.codecov.io/gh/akuity/kargo/pull/629?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity) 44.42%.

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


```diff
@@            Coverage Diff             @@
##             main     #629      +/-   ##
==========================================
- Coverage   44.69%   44.42%   -0.28%     
==========================================
  Files          84       88       +4     
  Lines        5971     6125     +154     
==========================================
+ Hits         2669     2721      +52     
- Misses       3164     3261      +97     
- Partials      138      143       +5     
```


| [Files Changed](https://app.codecov.io/gh/akuity/kargo/pull/629?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/629?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/629?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/query\_freights\_v1alpha1.go](https://app.codecov.io/gh/akuity/kargo/pull/629?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL3F1ZXJ5X2ZyZWlnaHRzX3YxYWxwaGExLmdv) | `0.00% <0.00%> (ø)` | |
| [internal/controller/stages/stages.go](https://app.codecov.io/gh/akuity/kargo/pull/629?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvY29udHJvbGxlci9zdGFnZXMvc3RhZ2VzLmdv) | `48.40% <50.00%> (-0.58%)` | :arrow_down: |
| [internal/api/validators.go](https://app.codecov.io/gh/akuity/kargo/pull/629?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL3ZhbGlkYXRvcnMuZ28=) | `60.52% <62.96%> (+5.98%)` | :arrow_up: |
| [internal/api/promote\_stage\_v1alpha1.go](https://app.codecov.io/gh/akuity/kargo/pull/629?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL3Byb21vdGVfc3RhZ2VfdjFhbHBoYTEuZ28=) | `57.14% <70.00%> (-24.34%)` | :arrow_down: |
| [internal/api/promote\_subscribers\_v1alpha1.go](https://app.codecov.io/gh/akuity/kargo/pull/629?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity#diff-aW50ZXJuYWwvYXBpL3Byb21vdGVfc3Vic2NyaWJlcnNfdjFhbHBoYTEuZ28=) | `70.17% <70.17%> (ø)` | |
| [internal/api/helpers.go](https://app.codecov.io/gh/akuity/kargo/pull/629?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/629?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/629?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: |
| ... and [1 more](https://app.codecov.io/gh/akuity/kargo/pull/629?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=akuity) | |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/akuity/kargo/pull/629?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).

jessesuen: The PR is lacking testing. I'll be adding that before merge. 

Patch:
diff --git a/api/service/v1alpha1/service.proto b/api/service/v1alpha1/service.proto
index cb4e52a0..9c29411c 100644
--- a/api/service/v1alpha1/service.proto
+++ b/api/service/v1alpha1/service.proto
@@ -51,6 +51,11 @@ service KargoService {
   rpc CreateProject(CreateProjectRequest) returns (CreateProjectResponse);
   rpc ListProjects(ListProjectsRequest) returns (ListProjectsResponse);
   rpc DeleteProject(DeleteProjectRequest) returns (DeleteProjectResponse);
+
+  /* Freight APIs */
+
+  rpc QueryFreights(QueryFreightsRequest) returns (QueryFreightsResponse);
+
 }
 
 message ComponentVersions {
@@ -345,3 +350,20 @@ message DeleteProjectRequest {
 message DeleteProjectResponse {
   /* explicitly empty */
 }
+
+message QueryFreightsRequest {
+  string project = 1;
+  string stage = 2;
+  string group_by = 3;
+  string group = 4;
+  string order_by = 5;
+  bool reverse = 6;
+}
+
+message QueryFreightsResponse {
+  map<string, FreightList> groups = 1;
+}
+
+message FreightList {
+  repeated github.com.akuity.kargo.pkg.api.v1alpha1.StageState freights = 1;
+}
diff --git a/internal/api/helpers.go b/internal/api/helpers.go
index 10c2e79d..45299796 100644
--- a/internal/api/helpers.go
+++ b/internal/api/helpers.go
@@ -22,7 +22,7 @@ func getStage(ctx context.Context, kc kubernetes.Client, project string, name st
 	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.CodeNotFound, fmt.Errorf("stage %q not found", name))
 		}
 		return nil, connect.NewError(connect.CodeInternal, err)
 	}
diff --git a/internal/api/query_freights_v1alpha1.go b/internal/api/query_freights_v1alpha1.go
new file mode 100644
index 00000000..598c83ec
--- /dev/null
+++ b/internal/api/query_freights_v1alpha1.go
@@ -0,0 +1,180 @@
+package api
+
+import (
+	"context"
+	"sort"
+
+	"connectrpc.com/connect"
+	"github.com/Masterminds/semver"
+	"github.com/pkg/errors"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	kubev1alpha1 "github.com/akuity/kargo/api/v1alpha1"
+	"github.com/akuity/kargo/internal/api/types/v1alpha1"
+	svcv1alpha1 "github.com/akuity/kargo/pkg/api/service/v1alpha1"
+	apiv1alpha1 "github.com/akuity/kargo/pkg/api/v1alpha1"
+)
+
+const (
+	GroupByContainerRepository = "container_repo"
+	GroupByGitRepository       = "git_repo"
+	GroupByHelmRepository      = "helm_repo"
+
+	OrderByFirstSeen = "first_seen"
+	OrderByTag       = "tag"
+)
+
+func (s *server) QueryFreights(
+	ctx context.Context,
+	req *connect.Request[svcv1alpha1.QueryFreightsRequest],
+) (*connect.Response[svcv1alpha1.QueryFreightsResponse], error) {
+	if req.Msg.GetProject() == "" {
+		return nil, connect.NewError(connect.CodeInvalidArgument, errors.New("project should not be empty"))
+	}
+	if err := s.validateProject(ctx, req.Msg.GetProject()); err != nil {
+		return nil, err
+	}
+	if err := validateGroupByOrderBy(req.Msg.GetGroup(), req.Msg.GetGroupBy(), req.Msg.GetOrderBy()); err != nil {
+		return nil, err
+	}
+
+	var stages []kubev1alpha1.Stage
+	if req.Msg.GetStage() != "" {
+		stage, err := getStage(ctx, s.client, req.Msg.GetProject(), req.Msg.GetStage())
+		if err != nil {
+			return nil, err
+		}
+		stages = []kubev1alpha1.Stage{*stage}
+	} else {
+		var list kubev1alpha1.StageList
+		if err := s.client.List(ctx, &list, client.InNamespace(req.Msg.GetProject())); err != nil {
+			return nil, connect.NewError(connect.CodeInternal, err)
+		}
+		stages = list.Items
+	}
+
+	seen := make(map[string]bool)
+	freightGroups := make(map[string]*svcv1alpha1.FreightList)
+	for _, s := range stages {
+		addToGroups(req.Msg, freightGroups, s, seen)
+	}
+	sortFreightGroups(req.Msg.GetOrderBy(), req.Msg.GetReverse(), freightGroups)
+
+	return connect.NewResponse(&svcv1alpha1.QueryFreightsResponse{
+		Groups: freightGroups,
+	}), nil
+}
+
+func addToGroups(
+	req *svcv1alpha1.QueryFreightsRequest,
+	groups map[string]*svcv1alpha1.FreightList,
+	stage kubev1alpha1.Stage,
+	seen map[string]bool,
+) {
+
+	appendToStageGroups := func(stack kubev1alpha1.StageStateStack) {
+		for _, f := range stack {
+			if seen[f.ID] {
+				continue
+			}
+			// clear out stage-specific information
+			f.Health = nil
+			f.Provenance = ""
+			switch req.GetGroupBy() {
+			case GroupByContainerRepository:
+				for _, i := range f.Images {
+					if req.GetGroup() == "" || i.RepoURL == req.GetGroup() {
+						groups[i.RepoURL] = appendToFreightList(groups[i.RepoURL], f)
+					}
+				}
+			case GroupByGitRepository:
+				for _, c := range f.Commits {
+					if req.GetGroup() == "" || c.RepoURL == req.GetGroup() {
+						groups[c.RepoURL] = appendToFreightList(groups[c.RepoURL], f)
+					}
+				}
+			case GroupByHelmRepository:
+				for _, c := range f.Charts {
+					if req.GetGroup() == "" || c.RegistryURL == req.GetGroup() {
+						groups[c.RegistryURL] = appendToFreightList(groups[c.RegistryURL], f)
+					}
+				}
+			default:
+				if req.GetGroup() == "" {
+					groups[""] = appendToFreightList(groups[""], f)
+				}
+			}
+			seen[f.ID] = true
+		}
+	}
+	appendToStageGroups(stage.Status.AvailableStates)
+	appendToStageGroups(stage.Status.History)
+}
+
+func appendToFreightList(list *svcv1alpha1.FreightList, f kubev1alpha1.StageState) *svcv1alpha1.FreightList {
+	if list == nil {
+		list = &svcv1alpha1.FreightList{}
+	}
+	list.Freights = append(list.Freights, v1alpha1.ToStageStateProto(f))
+	return list
+}
+
+func sortFreightGroups(orderBy string, reverse bool, groups map[string]*svcv1alpha1.FreightList) {
+	for k := range groups {
+		var dataToSort sort.Interface
+		switch orderBy {
+		case OrderByTag:
+			dataToSort = ByTag(groups[k].Freights)
+		default:
+			dataToSort = ByFirstSeen(groups[k].Freights)
+		}
+		if reverse {
+			dataToSort = sort.Reverse(dataToSort)
+		}
+		sort.Sort(dataToSort)
+	}
+}
+
+type ByFirstSeen []*apiv1alpha1.StageState
+
+func (a ByFirstSeen) Len() int      { return len(a) }
+func (a ByFirstSeen) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a ByFirstSeen) Less(i, j int) bool {
+	return a[i].FirstSeen.AsTime().Before(a[j].FirstSeen.AsTime())
+}
+
+// NOTE: sorting by tag will sort by the first container image we found
+// or the first helm chart we found in the freight.
+type ByTag []*apiv1alpha1.StageState
+
+func (a ByTag) Len() int      { return len(a) }
+func (a ByTag) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
+func (a ByTag) Less(i, j int) bool {
+	iRepo, iTag, iVer := getRepoAndTag(a[i])
+	jRepo, jTag, jVer := getRepoAndTag(a[j])
+	// Only compare the two freight if we are comparing against the same repository
+	if iRepo == jRepo {
+		if iVer != nil && jVer != nil {
+			return iVer.LessThan(jVer)
+		}
+		// repo is the same, but tags are not a semver. do lexicographical comparison
+		return iTag < jTag
+	}
+	// They are not comparable. Fallback to firstSeen
+	return a[i].FirstSeen.AsTime().Before(a[j].FirstSeen.AsTime())
+}
+
+func getRepoAndTag(s *apiv1alpha1.StageState) (string, string, *semver.Version) {
+	var repo, tag string
+	if len(s.Images) > 0 {
+		repo = s.Images[0].RepoUrl
+		tag = s.Images[0].Tag
+	} else if len(s.Charts) > 0 {
+		repo = s.Charts[0].RegistryUrl + "/" + s.Charts[0].Name
+		tag = s.Charts[0].Version
+	} else {
+		return "", "", nil
+	}
+	v, _ := semver.NewVersion(tag)
+	return repo, tag, v
+}
diff --git a/internal/api/validators.go b/internal/api/validators.go
index 24810d13..bc04e868 100644
--- a/internal/api/validators.go
+++ b/internal/api/validators.go
@@ -49,3 +49,27 @@ func validateFreightExists(freight string, freights kubev1alpha1.StageStateStack
 	}
 	return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("freight %q not found in Stage", freight))
 }
+
+func validateGroupByOrderBy(group string, groupBy string, orderBy string) error {
+	if group != "" && groupBy == "" {
+		return connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Cannot filter by group without group by"))
+	}
+	switch groupBy {
+	case GroupByContainerRepository, GroupByGitRepository, GroupByHelmRepository, "":
+	default:
+		return connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Invalid group by: %s", groupBy))
+	}
+	switch orderBy {
+	case OrderByTag:
+		if groupBy != GroupByContainerRepository && groupBy != GroupByHelmRepository {
+			return connect.NewError(connect.CodeInvalidArgument,
+				fmt.Errorf("Tag ordering only valid when grouping by: %s, %s",
+					GroupByContainerRepository, GroupByHelmRepository))
+		}
+	case OrderByFirstSeen, "":
+	default:
+		return connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Invalid order by: %s", orderBy))
+	}
+
+	return nil
+}
diff --git a/pkg/api/service/v1alpha1/service.pb.go b/pkg/api/service/v1alpha1/service.pb.go
index afa63d47..0a4d4854 100644
--- a/pkg/api/service/v1alpha1/service.pb.go
+++ b/pkg/api/service/v1alpha1/service.pb.go
@@ -3329,6 +3329,187 @@ func (*DeleteProjectResponse) Descriptor() ([]byte, []int) {
 	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{60}
 }
 
+type QueryFreightsRequest 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"`
+	GroupBy string `protobuf:"bytes,3,opt,name=group_by,json=groupBy,proto3" json:"group_by,omitempty"`
+	Group   string `protobuf:"bytes,4,opt,name=group,proto3" json:"group,omitempty"`
+	OrderBy string `protobuf:"bytes,5,opt,name=order_by,json=orderBy,proto3" json:"order_by,omitempty"`
+	Reverse bool   `protobuf:"varint,6,opt,name=reverse,proto3" json:"reverse,omitempty"`
+}
+
+func (x *QueryFreightsRequest) Reset() {
+	*x = QueryFreightsRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_service_v1alpha1_service_proto_msgTypes[61]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *QueryFreightsRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*QueryFreightsRequest) ProtoMessage() {}
+
+func (x *QueryFreightsRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_service_v1alpha1_service_proto_msgTypes[61]
+	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 QueryFreightsRequest.ProtoReflect.Descriptor instead.
+func (*QueryFreightsRequest) Descriptor() ([]byte, []int) {
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{61}
+}
+
+func (x *QueryFreightsRequest) GetProject() string {
+	if x != nil {
+		return x.Project
+	}
+	return ""
+}
+
+func (x *QueryFreightsRequest) GetStage() string {
+	if x != nil {
+		return x.Stage
+	}
+	return ""
+}
+
+func (x *QueryFreightsRequest) GetGroupBy() string {
+	if x != nil {
+		return x.GroupBy
+	}
+	return ""
+}
+
+func (x *QueryFreightsRequest) GetGroup() string {
+	if x != nil {
+		return x.Group
+	}
+	return ""
+}
+
+func (x *QueryFreightsRequest) GetOrderBy() string {
+	if x != nil {
+		return x.OrderBy
+	}
+	return ""
+}
+
+func (x *QueryFreightsRequest) GetReverse() bool {
+	if x != nil {
+		return x.Reverse
+	}
+	return false
+}
+
+type QueryFreightsResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Groups map[string]*FreightList `protobuf:"bytes,1,rep,name=groups,proto3" json:"groups,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
+}
+
+func (x *QueryFreightsResponse) Reset() {
+	*x = QueryFreightsResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_service_v1alpha1_service_proto_msgTypes[62]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *QueryFreightsResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*QueryFreightsResponse) ProtoMessage() {}
+
+func (x *QueryFreightsResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_service_v1alpha1_service_proto_msgTypes[62]
+	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 QueryFreightsResponse.ProtoReflect.Descriptor instead.
+func (*QueryFreightsResponse) Descriptor() ([]byte, []int) {
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{62}
+}
+
+func (x *QueryFreightsResponse) GetGroups() map[string]*FreightList {
+	if x != nil {
+		return x.Groups
+	}
+	return nil
+}
+
+type FreightList struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Freights []*v1alpha1.StageState `protobuf:"bytes,1,rep,name=freights,proto3" json:"freights,omitempty"`
+}
+
+func (x *FreightList) Reset() {
+	*x = FreightList{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_service_v1alpha1_service_proto_msgTypes[63]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *FreightList) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*FreightList) ProtoMessage() {}
+
+func (x *FreightList) ProtoReflect() protoreflect.Message {
+	mi := &file_service_v1alpha1_service_proto_msgTypes[63]
+	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 FreightList.ProtoReflect.Descriptor instead.
+func (*FreightList) Descriptor() ([]byte, []int) {
+	return file_service_v1alpha1_service_proto_rawDescGZIP(), []int{63}
+}
+
+func (x *FreightList) GetFreights() []*v1alpha1.StageState {
+	if x != nil {
+		return x.Freights
+	}
+	return nil
+}
+
 var File_service_v1alpha1_service_proto protoreflect.FileDescriptor
 
 var file_service_v1alpha1_service_proto_rawDesc = []byte{
@@ -3706,245 +3887,284 @@ var file_service_v1alpha1_service_proto_rawDesc = []byte{
 	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,
-	0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x61, 0x6b, 0x75, 0x69, 0x74, 0x79, 0x2e, 0x69,
+	0x22, 0xac, 0x01, 0x0a, 0x14, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x72, 0x65, 0x69, 0x67, 0x68,
+	0x74, 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, 0x19, 0x0a, 0x08, 0x67, 0x72, 0x6f,
+	0x75, 0x70, 0x5f, 0x62, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f,
+	0x75, 0x70, 0x42, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x04, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x6f, 0x72,
+	0x64, 0x65, 0x72, 0x5f, 0x62, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x72,
+	0x64, 0x65, 0x72, 0x42, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65,
+	0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x22,
+	0xde, 0x01, 0x0a, 0x15, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x72, 0x65, 0x69, 0x67, 0x68, 0x74,
+	0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x5b, 0x0a, 0x06, 0x67, 0x72, 0x6f,
+	0x75, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x43, 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, 0x51, 0x75, 0x65,
+	0x72, 0x79, 0x46, 0x72, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x2e, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06,
+	0x67, 0x72, 0x6f, 0x75, 0x70, 0x73, 0x1a, 0x68, 0x0a, 0x0b, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x73,
+	0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x43, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d, 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, 0x46, 0x72, 0x65, 0x69, 0x67, 0x68,
+	0x74, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01,
+	0x22, 0x5f, 0x0a, 0x0b, 0x46, 0x72, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x12,
+	0x50, 0x0a, 0x08, 0x66, 0x72, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x34, 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, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x08, 0x66, 0x72, 0x65, 0x69, 0x67, 0x68, 0x74,
+	0x73, 0x32, 0xc5, 0x1c, 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, 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,
+	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, 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,
+	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,
-	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, 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, 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, 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, 0x6f, 0x2e, 0x6b, 0x61, 0x72, 0x67,
+	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, 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, 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, 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,
+	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, 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,
+	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, 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,
-	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, 0x35, 0x2e, 0x61, 0x6b, 0x75, 0x69,
+	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, 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,
+	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, 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, 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,
+	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, 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, 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, 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, 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,
+	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, 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,
+	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, 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, 0x75, 0x69, 0x74, 0x79,
+	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, 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,
+	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,
-	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,
+	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, 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,
+	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, 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, 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,
+	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, 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,
+	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, 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,
+	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, 0x12, 0x80, 0x01, 0x0a, 0x0d, 0x51, 0x75, 0x65, 0x72, 0x79,
+	0x46, 0x72, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 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, 0x51, 0x75, 0x65, 0x72,
+	0x79, 0x46, 0x72, 0x65, 0x69, 0x67, 0x68, 0x74, 0x73, 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, 0x51, 0x75, 0x65, 0x72, 0x79, 0x46, 0x72, 0x65, 0x69, 0x67, 0x68, 0x74,
+	0x73, 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 (
@@ -3959,7 +4179,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, 61)
+var file_service_v1alpha1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 65)
 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
@@ -4022,101 +4242,111 @@ var file_service_v1alpha1_service_proto_goTypes = []interface{}{
 	(*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
+	(*QueryFreightsRequest)(nil),             // 61: akuity.io.kargo.service.v1alpha1.QueryFreightsRequest
+	(*QueryFreightsResponse)(nil),            // 62: akuity.io.kargo.service.v1alpha1.QueryFreightsResponse
+	(*FreightList)(nil),                      // 63: akuity.io.kargo.service.v1alpha1.FreightList
+	nil,                                      // 64: akuity.io.kargo.service.v1alpha1.QueryFreightsResponse.GroupsEntry
+	(*timestamppb.Timestamp)(nil),            // 65: google.protobuf.Timestamp
+	(*v1alpha1.StageSpec)(nil),               // 66: github.com.akuity.kargo.pkg.api.v1alpha1.StageSpec
+	(*v1alpha1.Stage)(nil),                   // 67: github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	(*v1alpha1.Promotion)(nil),               // 68: github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
+	(*v1alpha1.PromotionPolicy)(nil),         // 69: github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
+	(*v1alpha1.StageState)(nil),              // 70: github.com.akuity.kargo.pkg.api.v1alpha1.StageState
 }
 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
-	61, // 2: akuity.io.kargo.service.v1alpha1.VersionInfo.build_time:type_name -> google.protobuf.Timestamp
+	65, // 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
-	62, // 5: akuity.io.kargo.service.v1alpha1.TypedStageSpec.spec:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.StageSpec
+	66, // 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
-	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
+	67, // 10: akuity.io.kargo.service.v1alpha1.CreateStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	67, // 11: akuity.io.kargo.service.v1alpha1.ListStagesResponse.stages:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	67, // 12: akuity.io.kargo.service.v1alpha1.GetStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	67, // 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
-	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
+	67, // 15: akuity.io.kargo.service.v1alpha1.UpdateStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	68, // 16: akuity.io.kargo.service.v1alpha1.PromoteStageResponse.promotion:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
+	68, // 17: akuity.io.kargo.service.v1alpha1.PromoteSubscribersResponse.promotions:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
+	67, // 18: akuity.io.kargo.service.v1alpha1.RefreshStageResponse.stage:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Stage
+	68, // 19: akuity.io.kargo.service.v1alpha1.ListPromotionsResponse.promotions:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
+	68, // 20: akuity.io.kargo.service.v1alpha1.GetPromotionResponse.promotion:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.Promotion
+	69, // 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
+	69, // 23: akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyResponse.promotion_policy:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
+	69, // 24: akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesResponse.promotion_policies:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
+	69, // 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
+	69, // 27: akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyResponse.promotion_policy:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.PromotionPolicy
+	65, // 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
+	64, // 31: akuity.io.kargo.service.v1alpha1.QueryFreightsResponse.groups:type_name -> akuity.io.kargo.service.v1alpha1.QueryFreightsResponse.GroupsEntry
+	70, // 32: akuity.io.kargo.service.v1alpha1.FreightList.freights:type_name -> github.com.akuity.kargo.pkg.api.v1alpha1.StageState
+	63, // 33: akuity.io.kargo.service.v1alpha1.QueryFreightsResponse.GroupsEntry.value:type_name -> akuity.io.kargo.service.v1alpha1.FreightList
+	2,  // 34: akuity.io.kargo.service.v1alpha1.KargoService.GetVersionInfo:input_type -> akuity.io.kargo.service.v1alpha1.GetVersionInfoRequest
+	4,  // 35: akuity.io.kargo.service.v1alpha1.KargoService.GetPublicConfig:input_type -> akuity.io.kargo.service.v1alpha1.GetPublicConfigRequest
+	7,  // 36: akuity.io.kargo.service.v1alpha1.KargoService.AdminLogin:input_type -> akuity.io.kargo.service.v1alpha1.AdminLoginRequest
+	10, // 37: akuity.io.kargo.service.v1alpha1.KargoService.CreateResource:input_type -> akuity.io.kargo.service.v1alpha1.CreateResourceRequest
+	13, // 38: akuity.io.kargo.service.v1alpha1.KargoService.UpdateResource:input_type -> akuity.io.kargo.service.v1alpha1.UpdateResourceRequest
+	16, // 39: akuity.io.kargo.service.v1alpha1.KargoService.DeleteResource:input_type -> akuity.io.kargo.service.v1alpha1.DeleteResourceRequest
+	19, // 40: akuity.io.kargo.service.v1alpha1.KargoService.CreateStage:input_type -> akuity.io.kargo.service.v1alpha1.CreateStageRequest
+	21, // 41: akuity.io.kargo.service.v1alpha1.KargoService.ListStages:input_type -> akuity.io.kargo.service.v1alpha1.ListStagesRequest
+	23, // 42: akuity.io.kargo.service.v1alpha1.KargoService.GetStage:input_type -> akuity.io.kargo.service.v1alpha1.GetStageRequest
+	25, // 43: akuity.io.kargo.service.v1alpha1.KargoService.WatchStages:input_type -> akuity.io.kargo.service.v1alpha1.WatchStagesRequest
+	27, // 44: akuity.io.kargo.service.v1alpha1.KargoService.UpdateStage:input_type -> akuity.io.kargo.service.v1alpha1.UpdateStageRequest
+	29, // 45: akuity.io.kargo.service.v1alpha1.KargoService.DeleteStage:input_type -> akuity.io.kargo.service.v1alpha1.DeleteStageRequest
+	31, // 46: akuity.io.kargo.service.v1alpha1.KargoService.PromoteStage:input_type -> akuity.io.kargo.service.v1alpha1.PromoteStageRequest
+	33, // 47: akuity.io.kargo.service.v1alpha1.KargoService.PromoteSubscribers:input_type -> akuity.io.kargo.service.v1alpha1.PromoteSubscribersRequest
+	35, // 48: akuity.io.kargo.service.v1alpha1.KargoService.RefreshStage:input_type -> akuity.io.kargo.service.v1alpha1.RefreshStageRequest
+	38, // 49: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotions:input_type -> akuity.io.kargo.service.v1alpha1.ListPromotionsRequest
+	40, // 50: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotion:input_type -> akuity.io.kargo.service.v1alpha1.GetPromotionRequest
+	42, // 51: akuity.io.kargo.service.v1alpha1.KargoService.SetAutoPromotionForStage:input_type -> akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageRequest
+	44, // 52: akuity.io.kargo.service.v1alpha1.KargoService.CreatePromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyRequest
+	46, // 53: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotionPolicies:input_type -> akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesRequest
+	48, // 54: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.GetPromotionPolicyRequest
+	50, // 55: akuity.io.kargo.service.v1alpha1.KargoService.UpdatePromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyRequest
+	52, // 56: akuity.io.kargo.service.v1alpha1.KargoService.DeletePromotionPolicy:input_type -> akuity.io.kargo.service.v1alpha1.DeletePromotionPolicyRequest
+	55, // 57: akuity.io.kargo.service.v1alpha1.KargoService.CreateProject:input_type -> akuity.io.kargo.service.v1alpha1.CreateProjectRequest
+	57, // 58: akuity.io.kargo.service.v1alpha1.KargoService.ListProjects:input_type -> akuity.io.kargo.service.v1alpha1.ListProjectsRequest
+	59, // 59: akuity.io.kargo.service.v1alpha1.KargoService.DeleteProject:input_type -> akuity.io.kargo.service.v1alpha1.DeleteProjectRequest
+	61, // 60: akuity.io.kargo.service.v1alpha1.KargoService.QueryFreights:input_type -> akuity.io.kargo.service.v1alpha1.QueryFreightsRequest
+	3,  // 61: akuity.io.kargo.service.v1alpha1.KargoService.GetVersionInfo:output_type -> akuity.io.kargo.service.v1alpha1.GetVersionInfoResponse
+	5,  // 62: akuity.io.kargo.service.v1alpha1.KargoService.GetPublicConfig:output_type -> akuity.io.kargo.service.v1alpha1.GetPublicConfigResponse
+	8,  // 63: akuity.io.kargo.service.v1alpha1.KargoService.AdminLogin:output_type -> akuity.io.kargo.service.v1alpha1.AdminLoginResponse
+	12, // 64: akuity.io.kargo.service.v1alpha1.KargoService.CreateResource:output_type -> akuity.io.kargo.service.v1alpha1.CreateResourceResponse
+	15, // 65: akuity.io.kargo.service.v1alpha1.KargoService.UpdateResource:output_type -> akuity.io.kargo.service.v1alpha1.UpdateResourceResponse
+	18, // 66: akuity.io.kargo.service.v1alpha1.KargoService.DeleteResource:output_type -> akuity.io.kargo.service.v1alpha1.DeleteResourceResponse
+	20, // 67: akuity.io.kargo.service.v1alpha1.KargoService.CreateStage:output_type -> akuity.io.kargo.service.v1alpha1.CreateStageResponse
+	22, // 68: akuity.io.kargo.service.v1alpha1.KargoService.ListStages:output_type -> akuity.io.kargo.service.v1alpha1.ListStagesResponse
+	24, // 69: akuity.io.kargo.service.v1alpha1.KargoService.GetStage:output_type -> akuity.io.kargo.service.v1alpha1.GetStageResponse
+	26, // 70: akuity.io.kargo.service.v1alpha1.KargoService.WatchStages:output_type -> akuity.io.kargo.service.v1alpha1.WatchStagesResponse
+	28, // 71: akuity.io.kargo.service.v1alpha1.KargoService.UpdateStage:output_type -> akuity.io.kargo.service.v1alpha1.UpdateStageResponse
+	30, // 72: akuity.io.kargo.service.v1alpha1.KargoService.DeleteStage:output_type -> akuity.io.kargo.service.v1alpha1.DeleteStageResponse
+	32, // 73: akuity.io.kargo.service.v1alpha1.KargoService.PromoteStage:output_type -> akuity.io.kargo.service.v1alpha1.PromoteStageResponse
+	34, // 74: akuity.io.kargo.service.v1alpha1.KargoService.PromoteSubscribers:output_type -> akuity.io.kargo.service.v1alpha1.PromoteSubscribersResponse
+	36, // 75: akuity.io.kargo.service.v1alpha1.KargoService.RefreshStage:output_type -> akuity.io.kargo.service.v1alpha1.RefreshStageResponse
+	39, // 76: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotions:output_type -> akuity.io.kargo.service.v1alpha1.ListPromotionsResponse
+	41, // 77: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotion:output_type -> akuity.io.kargo.service.v1alpha1.GetPromotionResponse
+	43, // 78: akuity.io.kargo.service.v1alpha1.KargoService.SetAutoPromotionForStage:output_type -> akuity.io.kargo.service.v1alpha1.SetAutoPromotionForStageResponse
+	45, // 79: akuity.io.kargo.service.v1alpha1.KargoService.CreatePromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.CreatePromotionPolicyResponse
+	47, // 80: akuity.io.kargo.service.v1alpha1.KargoService.ListPromotionPolicies:output_type -> akuity.io.kargo.service.v1alpha1.ListPromotionPoliciesResponse
+	49, // 81: akuity.io.kargo.service.v1alpha1.KargoService.GetPromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.GetPromotionPolicyResponse
+	51, // 82: akuity.io.kargo.service.v1alpha1.KargoService.UpdatePromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.UpdatePromotionPolicyResponse
+	53, // 83: akuity.io.kargo.service.v1alpha1.KargoService.DeletePromotionPolicy:output_type -> akuity.io.kargo.service.v1alpha1.DeletePromotionPolicyResponse
+	56, // 84: akuity.io.kargo.service.v1alpha1.KargoService.CreateProject:output_type -> akuity.io.kargo.service.v1alpha1.CreateProjectResponse
+	58, // 85: akuity.io.kargo.service.v1alpha1.KargoService.ListProjects:output_type -> akuity.io.kargo.service.v1alpha1.ListProjectsResponse
+	60, // 86: akuity.io.kargo.service.v1alpha1.KargoService.DeleteProject:output_type -> akuity.io.kargo.service.v1alpha1.DeleteProjectResponse
+	62, // 87: akuity.io.kargo.service.v1alpha1.KargoService.QueryFreights:output_type -> akuity.io.kargo.service.v1alpha1.QueryFreightsResponse
+	61, // [61:88] is the sub-list for method output_type
+	34, // [34:61] is the sub-list for method input_type
+	34, // [34:34] is the sub-list for extension type_name
+	34, // [34:34] is the sub-list for extension extendee
+	0,  // [0:34] is the sub-list for field type_name
 }
 
 func init() { file_service_v1alpha1_service_proto_init() }
@@ -4857,6 +5087,42 @@ func file_service_v1alpha1_service_proto_init() {
 				return nil
 			}
 		}
+		file_service_v1alpha1_service_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*QueryFreightsRequest); 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[62].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*QueryFreightsResponse); 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[63].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*FreightList); 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[0].OneofWrappers = []interface{}{}
 	file_service_v1alpha1_service_proto_msgTypes[11].OneofWrappers = []interface{}{
@@ -4894,7 +5160,7 @@ func file_service_v1alpha1_service_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_service_v1alpha1_service_proto_rawDesc,
 			NumEnums:      0,
-			NumMessages:   61,
+			NumMessages:   65,
 			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 fdbd59e8..42dc92b8 100644
--- a/pkg/api/service/v1alpha1/svcv1alpha1connect/service.connect.go
+++ b/pkg/api/service/v1alpha1/svcv1alpha1connect/service.connect.go
@@ -108,6 +108,9 @@ const (
 	// KargoServiceDeleteProjectProcedure is the fully-qualified name of the KargoService's
 	// DeleteProject RPC.
 	KargoServiceDeleteProjectProcedure = "/akuity.io.kargo.service.v1alpha1.KargoService/DeleteProject"
+	// KargoServiceQueryFreightsProcedure is the fully-qualified name of the KargoService's
+	// QueryFreights RPC.
+	KargoServiceQueryFreightsProcedure = "/akuity.io.kargo.service.v1alpha1.KargoService/QueryFreights"
 )
 
 // KargoServiceClient is a client for the akuity.io.kargo.service.v1alpha1.KargoService service.
@@ -141,6 +144,7 @@ type KargoServiceClient interface {
 	CreateProject(context.Context, *connect.Request[v1alpha1.CreateProjectRequest]) (*connect.Response[v1alpha1.CreateProjectResponse], error)
 	ListProjects(context.Context, *connect.Request[v1alpha1.ListProjectsRequest]) (*connect.Response[v1alpha1.ListProjectsResponse], error)
 	DeleteProject(context.Context, *connect.Request[v1alpha1.DeleteProjectRequest]) (*connect.Response[v1alpha1.DeleteProjectResponse], error)
+	QueryFreights(context.Context, *connect.Request[v1alpha1.QueryFreightsRequest]) (*connect.Response[v1alpha1.QueryFreightsResponse], error)
 }
 
 // NewKargoServiceClient constructs a client for the akuity.io.kargo.service.v1alpha1.KargoService
@@ -283,6 +287,11 @@ func NewKargoServiceClient(httpClient connect.HTTPClient, baseURL string, opts .
 			baseURL+KargoServiceDeleteProjectProcedure,
 			opts...,
 		),
+		queryFreights: connect.NewClient[v1alpha1.QueryFreightsRequest, v1alpha1.QueryFreightsResponse](
+			httpClient,
+			baseURL+KargoServiceQueryFreightsProcedure,
+			opts...,
+		),
 	}
 }
 
@@ -314,6 +323,7 @@ type kargoServiceClient struct {
 	createProject            *connect.Client[v1alpha1.CreateProjectRequest, v1alpha1.CreateProjectResponse]
 	listProjects             *connect.Client[v1alpha1.ListProjectsRequest, v1alpha1.ListProjectsResponse]
 	deleteProject            *connect.Client[v1alpha1.DeleteProjectRequest, v1alpha1.DeleteProjectResponse]
+	queryFreights            *connect.Client[v1alpha1.QueryFreightsRequest, v1alpha1.QueryFreightsResponse]
 }
 
 // GetVersionInfo calls akuity.io.kargo.service.v1alpha1.KargoService.GetVersionInfo.
@@ -447,6 +457,11 @@ func (c *kargoServiceClient) DeleteProject(ctx context.Context, req *connect.Req
 	return c.deleteProject.CallUnary(ctx, req)
 }
 
+// QueryFreights calls akuity.io.kargo.service.v1alpha1.KargoService.QueryFreights.
+func (c *kargoServiceClient) QueryFreights(ctx context.Context, req *connect.Request[v1alpha1.QueryFreightsRequest]) (*connect.Response[v1alpha1.QueryFreightsResponse], error) {
+	return c.queryFreights.CallUnary(ctx, req)
+}
+
 // KargoServiceHandler is an implementation of the akuity.io.kargo.service.v1alpha1.KargoService
 // service.
 type KargoServiceHandler interface {
@@ -479,6 +494,7 @@ type KargoServiceHandler interface {
 	CreateProject(context.Context, *connect.Request[v1alpha1.CreateProjectRequest]) (*connect.Response[v1alpha1.CreateProjectResponse], error)
 	ListProjects(context.Context, *connect.Request[v1alpha1.ListProjectsRequest]) (*connect.Response[v1alpha1.ListProjectsResponse], error)
 	DeleteProject(context.Context, *connect.Request[v1alpha1.DeleteProjectRequest]) (*connect.Response[v1alpha1.DeleteProjectResponse], error)
+	QueryFreights(context.Context, *connect.Request[v1alpha1.QueryFreightsRequest]) (*connect.Response[v1alpha1.QueryFreightsResponse], error)
 }
 
 // NewKargoServiceHandler builds an HTTP handler from the service implementation. It returns the
@@ -617,6 +633,11 @@ func NewKargoServiceHandler(svc KargoServiceHandler, opts ...connect.HandlerOpti
 		svc.DeleteProject,
 		opts...,
 	)
+	kargoServiceQueryFreightsHandler := connect.NewUnaryHandler(
+		KargoServiceQueryFreightsProcedure,
+		svc.QueryFreights,
+		opts...,
+	)
 	return "/akuity.io.kargo.service.v1alpha1.KargoService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		switch r.URL.Path {
 		case KargoServiceGetVersionInfoProcedure:
@@ -671,6 +692,8 @@ func NewKargoServiceHandler(svc KargoServiceHandler, opts ...connect.HandlerOpti
 			kargoServiceListProjectsHandler.ServeHTTP(w, r)
 		case KargoServiceDeleteProjectProcedure:
 			kargoServiceDeleteProjectHandler.ServeHTTP(w, r)
+		case KargoServiceQueryFreightsProcedure:
+			kargoServiceQueryFreightsHandler.ServeHTTP(w, r)
 		default:
 			http.NotFound(w, r)
 		}
@@ -783,3 +806,7 @@ func (UnimplementedKargoServiceHandler) ListProjects(context.Context, *connect.R
 func (UnimplementedKargoServiceHandler) DeleteProject(context.Context, *connect.Request[v1alpha1.DeleteProjectRequest]) (*connect.Response[v1alpha1.DeleteProjectResponse], error) {
 	return nil, connect.NewError(connect.CodeUnimplemented, errors.New("akuity.io.kargo.service.v1alpha1.KargoService.DeleteProject is not implemented"))
 }
+
+func (UnimplementedKargoServiceHandler) QueryFreights(context.Context, *connect.Request[v1alpha1.QueryFreightsRequest]) (*connect.Response[v1alpha1.QueryFreightsResponse], error) {
+	return nil, connect.NewError(connect.CodeUnimplemented, errors.New("akuity.io.kargo.service.v1alpha1.KargoService.QueryFreights 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 050965a2..eb7598e4 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, PromoteSubscribersRequest, PromoteSubscribersResponse, 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, QueryFreightsRequest, QueryFreightsResponse, RefreshStageRequest, RefreshStageResponse, SetAutoPromotionForStageRequest, SetAutoPromotionForStageResponse, UpdatePromotionPolicyRequest, UpdatePromotionPolicyResponse, UpdateResourceRequest, UpdateResourceResponse, UpdateStageRequest, UpdateStageResponse } from "./service_pb.js";
 
 export const typeName = "akuity.io.kargo.service.v1alpha1.KargoService";
 
@@ -438,3 +438,20 @@ export const deleteProject = createQueryService({
     typeName: "akuity.io.kargo.service.v1alpha1.KargoService",
   },
 }).deleteProject;
+
+/**
+ * @generated from rpc akuity.io.kargo.service.v1alpha1.KargoService.QueryFreights
+ */
+export const queryFreights = createQueryService({
+  service: {
+    methods: {
+      queryFreights: {
+        name: "QueryFreights",
+        kind: MethodKind.Unary,
+        I: QueryFreightsRequest,
+        O: QueryFreightsResponse,
+      },
+    },
+    typeName: "akuity.io.kargo.service.v1alpha1.KargoService",
+  },
+}).queryFreights;
diff --git a/ui/src/gen/service/v1alpha1/service_connect.ts b/ui/src/gen/service/v1alpha1/service_connect.ts
index 79e23b14..34d7dd5c 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, PromoteSubscribersRequest, PromoteSubscribersResponse, 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, QueryFreightsRequest, QueryFreightsResponse, RefreshStageRequest, RefreshStageResponse, SetAutoPromotionForStageRequest, SetAutoPromotionForStageResponse, UpdatePromotionPolicyRequest, UpdatePromotionPolicyResponse, UpdateResourceRequest, UpdateResourceResponse, UpdateStageRequest, UpdateStageResponse, WatchStagesRequest, WatchStagesResponse } from "./service_pb.js";
 import { MethodKind } from "@bufbuild/protobuf";
 
 /**
@@ -251,6 +251,15 @@ export const KargoService = {
       O: DeleteProjectResponse,
       kind: MethodKind.Unary,
     },
+    /**
+     * @generated from rpc akuity.io.kargo.service.v1alpha1.KargoService.QueryFreights
+     */
+    queryFreights: {
+      name: "QueryFreights",
+      I: QueryFreightsRequest,
+      O: QueryFreightsResponse,
+      kind: MethodKind.Unary,
+    },
   }
 } as const;
 
diff --git a/ui/src/gen/service/v1alpha1/service_pb.ts b/ui/src/gen/service/v1alpha1/service_pb.ts
index 4a4b5bc0..bcd0688e 100644
--- a/ui/src/gen/service/v1alpha1/service_pb.ts
+++ b/ui/src/gen/service/v1alpha1/service_pb.ts
@@ -5,7 +5,7 @@
 
 import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf";
 import { Message, proto3, Timestamp } from "@bufbuild/protobuf";
-import { Promotion, PromotionPolicy, Stage, StageSpec } from "../../v1alpha1/types_pb.js";
+import { Promotion, PromotionPolicy, Stage, StageSpec, StageState } from "../../v1alpha1/types_pb.js";
 
 /**
  * @generated from message akuity.io.kargo.service.v1alpha1.ComponentVersions
@@ -2519,3 +2519,144 @@ export class DeleteProjectResponse extends Message<DeleteProjectResponse> {
   }
 }
 
+/**
+ * @generated from message akuity.io.kargo.service.v1alpha1.QueryFreightsRequest
+ */
+export class QueryFreightsRequest extends Message<QueryFreightsRequest> {
+  /**
+   * @generated from field: string project = 1;
+   */
+  project = "";
+
+  /**
+   * @generated from field: string stage = 2;
+   */
+  stage = "";
+
+  /**
+   * @generated from field: string group_by = 3;
+   */
+  groupBy = "";
+
+  /**
+   * @generated from field: string group = 4;
+   */
+  group = "";
+
+  /**
+   * @generated from field: string order_by = 5;
+   */
+  orderBy = "";
+
+  /**
+   * @generated from field: bool reverse = 6;
+   */
+  reverse = false;
+
+  constructor(data?: PartialMessage<QueryFreightsRequest>) {
+    super();
+    proto3.util.initPartial(data, this);
+  }
+
+  static readonly runtime: typeof proto3 = proto3;
+  static readonly typeName = "akuity.io.kargo.service.v1alpha1.QueryFreightsRequest";
+  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: "group_by", kind: "scalar", T: 9 /* ScalarType.STRING */ },
+    { no: 4, name: "group", kind: "scalar", T: 9 /* ScalarType.STRING */ },
+    { no: 5, name: "order_by", kind: "scalar", T: 9 /* ScalarType.STRING */ },
+    { no: 6, name: "reverse", kind: "scalar", T: 8 /* ScalarType.BOOL */ },
+  ]);
+
+  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): QueryFreightsRequest {
+    return new QueryFreightsRequest().fromBinary(bytes, options);
+  }
+
+  static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): QueryFreightsRequest {
+    return new QueryFreightsRequest().fromJson(jsonValue, options);
+  }
+
+  static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): QueryFreightsRequest {
+    return new QueryFreightsRequest().fromJsonString(jsonString, options);
+  }
+
+  static equals(a: QueryFreightsRequest | PlainMessage<QueryFreightsRequest> | undefined, b: QueryFreightsRequest | PlainMessage<QueryFreightsRequest> | undefined): boolean {
+    return proto3.util.equals(QueryFreightsRequest, a, b);
+  }
+}
+
+/**
+ * @generated from message akuity.io.kargo.service.v1alpha1.QueryFreightsResponse
+ */
+export class QueryFreightsResponse extends Message<QueryFreightsResponse> {
+  /**
+   * @generated from field: map<string, akuity.io.kargo.service.v1alpha1.FreightList> groups = 1;
+   */
+  groups: { [key: string]: FreightList } = {};
+
+  constructor(data?: PartialMessage<QueryFreightsResponse>) {
+    super();
+    proto3.util.initPartial(data, this);
+  }
+
+  static readonly runtime: typeof proto3 = proto3;
+  static readonly typeName = "akuity.io.kargo.service.v1alpha1.QueryFreightsResponse";
+  static readonly fields: FieldList = proto3.util.newFieldList(() => [
+    { no: 1, name: "groups", kind: "map", K: 9 /* ScalarType.STRING */, V: {kind: "message", T: FreightList} },
+  ]);
+
+  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): QueryFreightsResponse {
+    return new QueryFreightsResponse().fromBinary(bytes, options);
+  }
+
+  static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): QueryFreightsResponse {
+    return new QueryFreightsResponse().fromJson(jsonValue, options);
+  }
+
+  static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): QueryFreightsResponse {
+    return new QueryFreightsResponse().fromJsonString(jsonString, options);
+  }
+
+  static equals(a: QueryFreightsResponse | PlainMessage<QueryFreightsResponse> | undefined, b: QueryFreightsResponse | PlainMessage<QueryFreightsResponse> | undefined): boolean {
+    return proto3.util.equals(QueryFreightsResponse, a, b);
+  }
+}
+
+/**
+ * @generated from message akuity.io.kargo.service.v1alpha1.FreightList
+ */
+export class FreightList extends Message<FreightList> {
+  /**
+   * @generated from field: repeated github.com.akuity.kargo.pkg.api.v1alpha1.StageState freights = 1;
+   */
+  freights: StageState[] = [];
+
+  constructor(data?: PartialMessage<FreightList>) {
+    super();
+    proto3.util.initPartial(data, this);
+  }
+
+  static readonly runtime: typeof proto3 = proto3;
+  static readonly typeName = "akuity.io.kargo.service.v1alpha1.FreightList";
+  static readonly fields: FieldList = proto3.util.newFieldList(() => [
+    { no: 1, name: "freights", kind: "message", T: StageState, repeated: true },
+  ]);
+
+  static fromBinary(bytes: Uint8Array, options?: Partial<BinaryReadOptions>): FreightList {
+    return new FreightList().fromBinary(bytes, options);
+  }
+
+  static fromJson(jsonValue: JsonValue, options?: Partial<JsonReadOptions>): FreightList {
+    return new FreightList().fromJson(jsonValue, options);
+  }
+
+  static fromJsonString(jsonString: string, options?: Partial<JsonReadOptions>): FreightList {
+    return new FreightList().fromJsonString(jsonString, options);
+  }
+
+  static equals(a: FreightList | PlainMessage<FreightList> | undefined, b: FreightList | PlainMessage<FreightList> | undefined): boolean {
+    return proto3.util.equals(FreightList, a, b);
+  }
+}
+


Test patch:
diff --git a/internal/api/promote_subscribers_v1alpha1_test.go b/internal/api/promote_subscribers_v1alpha1_test.go
index 78e9c1d7..09b39d90 100644
--- a/internal/api/promote_subscribers_v1alpha1_test.go
+++ b/internal/api/promote_subscribers_v1alpha1_test.go
@@ -41,7 +41,7 @@ func TestPromoteSubscribers(t *testing.T) {
 				Stage:   "does-not-exist",
 				Freight: "c353927ca7af42b38c0cdcfa393b2c552740e547",
 			},
-			errMsg:       `stage "kargo-demo" not found`,
+			errMsg:       `stage "does-not-exist" not found`,
 			expectedCode: connect.CodeNotFound,
 		},
 		"existing Stage with non-existing freight": {
@@ -129,6 +129,7 @@ func TestPromoteSubscribers(t *testing.T) {
 				require.Equal(t, ts.expectedCode, connect.CodeOf(err))
 				return
 			}
+			require.NoError(t, err)
 			assert.Len(t, res.Msg.GetPromotions(), int(ts.expectedPromotions))
 			for _, p := range res.Msg.GetPromotions() {
 				parts := strings.Split(p.GetMetadata().GetName(), ".")
diff --git a/internal/api/query_freights_v1alpha1_test.go b/internal/api/query_freights_v1alpha1_test.go
new file mode 100644
index 00000000..ba5cb9ed
--- /dev/null
+++ b/internal/api/query_freights_v1alpha1_test.go
@@ -0,0 +1,223 @@
+package api
+
+import (
+	"context"
+	"testing"
+
+	"connectrpc.com/connect"
+	"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 TestQueryFreights(t *testing.T) {
+	testSets := map[string]struct {
+		req                *svcv1alpha1.QueryFreightsRequest
+		errMsg             string
+		expectedCode       connect.Code
+		expectedPromotions int32
+		assertions         func(res *svcv1alpha1.QueryFreightsResponse)
+	}{
+		"empty project": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "",
+			},
+			errMsg:       "project should not be empty",
+			expectedCode: connect.CodeInvalidArgument,
+		},
+		"non-existing Stage": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+				Stage:   "does-not-exist",
+			},
+			errMsg:       `stage "does-not-exist" not found`,
+			expectedCode: connect.CodeNotFound,
+		},
+		"invalid group by": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+				GroupBy: "notvalid",
+			},
+			errMsg:       `Invalid group by: notvalid`,
+			expectedCode: connect.CodeInvalidArgument,
+		},
+		"invalid order by": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+				OrderBy: "notvalid",
+			},
+			errMsg:       `Invalid order by: notvalid`,
+			expectedCode: connect.CodeInvalidArgument,
+		},
+		"invalid group filter": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+				Group:   "ghcr.io/akuity/guestbook",
+			},
+			errMsg:       "Cannot filter by group without group by",
+			expectedCode: connect.CodeInvalidArgument,
+		},
+		"invalid order by tag": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+				OrderBy: OrderByTag,
+			},
+			errMsg:       "Tag ordering only valid when grouping by: container_repo, helm_repo",
+			expectedCode: connect.CodeInvalidArgument,
+		},
+		"query all freight": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+			},
+			assertions: func(res *svcv1alpha1.QueryFreightsResponse) {
+				require.Len(t, res.GetGroups(), 1)
+				require.Len(t, res.GetGroups()[""].Freights, 4)
+				require.Equal(t, res.GetGroups()[""].Freights[0].Id, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+				require.Equal(t, res.GetGroups()[""].Freights[1].Id, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
+				require.Equal(t, res.GetGroups()[""].Freights[2].Id, "cccccccccccccccccccccccccccccccccccccccc")
+				require.Equal(t, res.GetGroups()[""].Freights[3].Id, "dddddddddddddddddddddddddddddddddddddddd")
+			},
+		},
+		"reverse sort": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+				Reverse: true,
+			},
+			assertions: func(res *svcv1alpha1.QueryFreightsResponse) {
+				require.Len(t, res.GetGroups(), 1)
+				require.Len(t, res.GetGroups()[""].Freights, 4)
+				require.Equal(t, res.GetGroups()[""].Freights[0].Id, "dddddddddddddddddddddddddddddddddddddddd")
+				require.Equal(t, res.GetGroups()[""].Freights[1].Id, "cccccccccccccccccccccccccccccccccccccccc")
+				require.Equal(t, res.GetGroups()[""].Freights[2].Id, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
+				require.Equal(t, res.GetGroups()[""].Freights[3].Id, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+			},
+		},
+		"query single stage": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+				Stage:   "query-freight-3",
+			},
+			assertions: func(res *svcv1alpha1.QueryFreightsResponse) {
+				require.Len(t, res.GetGroups(), 1)
+				require.Len(t, res.GetGroups()[""].Freights, 1)
+				require.Equal(t, res.GetGroups()[""].Freights[0].Id, "dddddddddddddddddddddddddddddddddddddddd")
+			},
+		},
+		"query group by container_repo": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+				GroupBy: GroupByContainerRepository,
+			},
+			assertions: func(res *svcv1alpha1.QueryFreightsResponse) {
+				require.Len(t, res.GetGroups(), 2)
+
+				gb1 := res.GetGroups()["ghcr.io/akuity/guestbook"]
+				require.Len(t, gb1.Freights, 3)
+				require.Equal(t, gb1.Freights[0].Id, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
+				require.Equal(t, gb1.Freights[1].Id, "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
+				require.Equal(t, gb1.Freights[2].Id, "cccccccccccccccccccccccccccccccccccccccc")
+
+				gb2 := res.GetGroups()["ghcr.io/akuity/guestbook2"]
+				require.Len(t, gb2.Freights, 1)
+				require.Equal(t, gb2.Freights[0].Id, "dddddddddddddddddddddddddddddddddddddddd")
+			},
+		},
+		"group by container, order by tag": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+				GroupBy: GroupByContainerRepository,
+				OrderBy: OrderByTag,
+			},
+			assertions: func(res *svcv1alpha1.QueryFreightsResponse) {
+				require.Len(t, res.GetGroups(), 2)
+
+				gb1 := res.GetGroups()["ghcr.io/akuity/guestbook"]
+				require.Len(t, gb1.Freights, 3)
+				require.Equal(t, gb1.Freights[0].Images[0].Tag, "v0.0.1")
+				require.Equal(t, gb1.Freights[1].Images[0].Tag, "v0.0.2")
+				require.Equal(t, gb1.Freights[2].Images[0].Tag, "v0.0.4")
+
+				gb2 := res.GetGroups()["ghcr.io/akuity/guestbook2"]
+				require.Len(t, gb2.Freights, 1)
+				require.Equal(t, gb2.Freights[0].Images[0].Tag, "v0.0.0")
+			},
+		},
+		"filter by group": {
+			req: &svcv1alpha1.QueryFreightsRequest{
+				Project: "kargo-demo",
+				GroupBy: GroupByContainerRepository,
+				Group:   "ghcr.io/akuity/guestbook2",
+			},
+			assertions: func(res *svcv1alpha1.QueryFreightsResponse) {
+				require.Len(t, res.GetGroups(), 1)
+				gb2 := res.GetGroups()["ghcr.io/akuity/guestbook2"]
+				require.Len(t, gb2.Freights, 1)
+				require.Equal(t, gb2.Freights[0].Id, "dddddddddddddddddddddddddddddddddddddddd")
+			},
+		},
+	}
+	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/query-freight-1.yaml"),
+									*mustNewObject[kubev1alpha1.Stage]("testdata/query-freight-2.yaml"),
+									*mustNewObject[kubev1alpha1.Stage]("testdata/query-freight-3.yaml"),
+								},
+							}).
+							Build(), nil
+					},
+				},
+			)
+			require.NoError(t, err)
+
+			res, err := (&server{
+				client: client,
+			}).QueryFreights(ctx, connect.NewRequest(ts.req))
+			if ts.errMsg != "" {
+				require.ErrorContains(t, err, ts.errMsg)
+				require.Equal(t, ts.expectedCode, connect.CodeOf(err))
+				return
+			}
+			require.NoError(t, err)
+			if ts.assertions != nil {
+				ts.assertions(res.Msg)
+			}
+
+		})
+	}
+}
diff --git a/internal/api/testdata/query-freight-1.yaml b/internal/api/testdata/query-freight-1.yaml
new file mode 100644
index 00000000..9085fb0d
--- /dev/null
+++ b/internal/api/testdata/query-freight-1.yaml
@@ -0,0 +1,52 @@
+apiVersion: kargo.akuity.io/v1alpha1
+kind: Stage
+metadata:
+  name: query-freight-1
+  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: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.1
+    provenance: dev
+  currentState:
+    firstSeen: "2023-08-28T15:18:57Z"
+    health:
+      status: Healthy
+    id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.1
+    provenance: dev
+  history:
+  - firstSeen: "2023-08-28T15:18:57Z"
+    health:
+      status: Healthy
+    id: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.1
+    provenance: dev
+  - firstSeen: "2023-08-28T15:17:57Z"
+    health:
+      status: Unhealthy
+    id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.2
+    provenance: dev
diff --git a/internal/api/testdata/query-freight-2.yaml b/internal/api/testdata/query-freight-2.yaml
new file mode 100644
index 00000000..6dec25f3
--- /dev/null
+++ b/internal/api/testdata/query-freight-2.yaml
@@ -0,0 +1,52 @@
+apiVersion: kargo.akuity.io/v1alpha1
+kind: Stage
+metadata:
+  name: query-freight-2
+  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: bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.1
+    provenance: dev
+  currentState:
+    firstSeen: "2023-08-29T15:18:57Z"
+    health:
+      status: Healthy
+    id: cccccccccccccccccccccccccccccccccccccccc
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.4
+    provenance: dev
+  history:
+  - firstSeen: "2023-08-29T15:18:57Z"
+    health:
+      status: Healthy
+    id: cccccccccccccccccccccccccccccccccccccccc
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.4
+    provenance: dev
+  - firstSeen: "2023-08-28T15:17:57Z"
+    health:
+      status: Unhealthy
+    id: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+    images:
+    - repoURL: ghcr.io/akuity/guestbook
+      tag: v0.0.2
+    provenance: dev
diff --git a/internal/api/testdata/query-freight-3.yaml b/internal/api/testdata/query-freight-3.yaml
new file mode 100644
index 00000000..51ec5aa5
--- /dev/null
+++ b/internal/api/testdata/query-freight-3.yaml
@@ -0,0 +1,35 @@
+apiVersion: kargo.akuity.io/v1alpha1
+kind: Stage
+metadata:
+  name: query-freight-3
+  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-30T15:18:57Z"
+    id: dddddddddddddddddddddddddddddddddddddddd
+    images:
+    - repoURL: ghcr.io/akuity/guestbook2
+      tag: v0.0.0
+    provenance: dev
+  currentState:
+    firstSeen: "2023-08-30T15:18:57Z"
+    health:
+      status: Healthy
+    id: dddddddddddddddddddddddddddddddddddddddd
+    images:
+    - repoURL: ghcr.io/akuity/guestbook2
+      tag: v0.0.0
+    provenance: dev