Repository: akuity/kargo

Issue:
UI: freight history improvements
# Checklist

* [x] I've searched the issue queue to verify this is not a duplicate feature request.
* [ ] I've pasted the output of `kargo version`, if applicable.
* [ ] I've pasted logs, if applicable.

# Proposed Feature

<img width="1440" alt="Image" src="https://github.com/user-attachments/assets/0a091c80-d0a2-4bcb-b95a-9a6c05fcd247" />

- ~[ ] Rename "Freight History" to "Deployments"~
- [ ] Add time of promotion of that freight and freight creation time
- [ ] Add "Promote" action button

# Motivation

Ease of use and less navigation

Hints from issue comments:
@krancour: I think this new view would be fine, except for:

> Rename "Freight History" to "Deployments"

We're trying really hard to not use the word "deployments" because it makes Kargo sound like a GitOps agent. We want to keep the focus on _promotions_ which is really about managing desired state. Deployment's left to Argo CD.

Other than that, this all seems fine.

Solution patch:
diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml
index 1db30933..5f908ca6 100644
--- a/.github/workflows/backport.yaml
+++ b/.github/workflows/backport.yaml
@@ -36,6 +36,8 @@ jobs:
         # Copy associated people to the backport PR
         copy_assignees: true
         copy_requested_reviewers: true
+        # Copy any milestone to the backport PR
+        copy_milestone: true
         # Skip any merge commits in the source PR
         merge_commits: 'skip'
         # Automatically detect "squash and merge" instead of copying all
diff --git a/Tiltfile b/Tiltfile
index cbd35d3c..3b56d3f4 100644
--- a/Tiltfile
+++ b/Tiltfile
@@ -55,7 +55,10 @@ k8s_yaml(
     name = 'kargo',
     namespace = 'kargo',
     values = 'hack/tilt/values.dev.yaml',
-    set = 'externalWebhooksServer.host=' + os.environ.get('KARGO_EXTERNAL_WEBHOOKS_SERVER_HOSTNAME', 'localhost:30083')
+    set = [
+      'externalWebhooksServer.host=' + os.environ.get('KARGO_EXTERNAL_WEBHOOKS_SERVER_HOSTNAME', 'localhost:30083'),
+      'externalWebhooksServer.tls.terminatedUpstream=' + os.environ.get('KARGO_EXTERNAL_WEBHOOKS_SERVER_TLS_TERMINATED_UPSTREAM', 'false')
+    ]
   )
 )
 # Normally the API server serves up the front end, but we want live updates
diff --git a/api/v1alpha1/freight_types.go b/api/v1alpha1/freight_types.go
index 3a9cae68..f7b4b384 100644
--- a/api/v1alpha1/freight_types.go
+++ b/api/v1alpha1/freight_types.go
@@ -1,8 +1,11 @@
 package v1alpha1
 
 import (
+	"encoding/json"
+	"fmt"
 	"time"
 
+	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -108,6 +111,10 @@ type FreightStatus struct {
 	// might wish to promote a piece of Freight to a given Stage without
 	// transiting the entire pipeline.
 	ApprovedFor map[string]ApprovedStage `json:"approvedFor,omitempty" protobuf:"bytes,2,rep,name=approvedFor" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
+	// Metadata is a map of arbitrary metadata associated with the Freight.
+	// This is useful for storing additional information about the Freight
+	// or Promotion that can be shared across steps or stages.
+	Metadata map[string]apiextensionsv1.JSON `json:"metadata,omitempty" protobuf:"bytes,4,rep,name=metadata" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
 }
 
 // IsCurrentlyIn returns whether the Freight is currently in the specified
@@ -225,6 +232,40 @@ func (f *FreightStatus) AddApprovedStage(stage string, approvedAt time.Time) {
 	}
 }
 
+// UpsertMetadata inserts or updates the given key in Freight status Metadata
+func (f *FreightStatus) UpsertMetadata(key string, data any) error {
+	if len(f.Metadata) == 0 {
+		f.Metadata = make(map[string]apiextensionsv1.JSON)
+	}
+
+	if key == "" {
+		return fmt.Errorf("key must not be empty")
+	}
+
+	dataBytes, err := json.Marshal(data)
+	if err != nil {
+		return err
+	}
+	f.Metadata[key] = apiextensionsv1.JSON{
+		Raw: dataBytes,
+	}
+	return nil
+}
+
+// GetMetadata retrieves the data associated with the given key from Freight status Metadata
+func (f *FreightStatus) GetMetadata(key string, data any) (bool, error) {
+	dataBytes, ok := f.Metadata[key]
+
+	if !ok {
+		return false, nil
+	}
+
+	if err := json.Unmarshal(dataBytes.Raw, data); err != nil {
+		return false, err
+	}
+	return true, nil
+}
+
 // CurrentStage reflects a Stage's current use of Freight.
 type CurrentStage struct {
 	// Since is the time at which the Stage most recently started using the
diff --git a/api/v1alpha1/generated.pb.go b/api/v1alpha1/generated.pb.go
index 64f9896f..72bdfb43 100644
--- a/api/v1alpha1/generated.pb.go
+++ b/api/v1alpha1/generated.pb.go
@@ -10,7 +10,7 @@ import (
 
 	proto "github.com/gogo/protobuf/proto"
 	github_com_gogo_protobuf_sortkeys "github.com/gogo/protobuf/sortkeys"
-	v12 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	v11 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	math "math"
@@ -2191,6 +2191,7 @@ func init() {
 	proto.RegisterType((*FreightStatus)(nil), "github.com.akuity.kargo.api.v1alpha1.FreightStatus")
 	proto.RegisterMapType((map[string]ApprovedStage)(nil), "github.com.akuity.kargo.api.v1alpha1.FreightStatus.ApprovedForEntry")
 	proto.RegisterMapType((map[string]CurrentStage)(nil), "github.com.akuity.kargo.api.v1alpha1.FreightStatus.CurrentlyInEntry")
+	proto.RegisterMapType((map[string]v11.JSON)(nil), "github.com.akuity.kargo.api.v1alpha1.FreightStatus.MetadataEntry")
 	proto.RegisterMapType((map[string]VerifiedStage)(nil), "github.com.akuity.kargo.api.v1alpha1.FreightStatus.VerifiedInEntry")
 	proto.RegisterType((*GitCommit)(nil), "github.com.akuity.kargo.api.v1alpha1.GitCommit")
 	proto.RegisterType((*GitDiscoveryResult)(nil), "github.com.akuity.kargo.api.v1alpha1.GitDiscoveryResult")
@@ -2250,294 +2251,296 @@ func init() {
 }
 
 var fileDescriptor_e26b7f7bbc391025 = []byte{
-	// 4578 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x7c, 0x4d, 0x8c, 0x24, 0x47,
-	0x56, 0xff, 0x64, 0x55, 0x7f, 0xbe, 0xea, 0xcf, 0x98, 0x6e, 0xbb, 0xb7, 0xf7, 0xef, 0xe9, 0xf9,
-	0xa7, 0x8d, 0x65, 0x63, 0xbb, 0x9a, 0x19, 0x7b, 0xec, 0xf9, 0xf0, 0x0e, 0xea, 0xea, 0x9e, 0x8f,
-	0xf6, 0xf6, 0x7a, 0x9a, 0xa8, 0xf9, 0x58, 0x8f, 0x6d, 0x0d, 0xd1, 0x55, 0xd1, 0x55, 0xb9, 0x5d,
-	0x95, 0x59, 0x8e, 0xc8, 0x6a, 0x4f, 0x03, 0x02, 0xf3, 0xa9, 0x95, 0x40, 0xc8, 0x87, 0x45, 0x5e,
-	0x21, 0x10, 0x88, 0x3d, 0xa1, 0x95, 0xe0, 0xc8, 0x81, 0x83, 0x0f, 0x5c, 0xbc, 0xb0, 0x20, 0xcb,
-	0x1c, 0x30, 0xd2, 0xaa, 0xc1, 0xbd, 0x12, 0x12, 0x37, 0x2e, 0x5c, 0x46, 0x42, 0x42, 0xf1, 0x91,
-	0x99, 0x91, 0x59, 0x59, 0xd3, 0x99, 0x35, 0xdd, 0xc3, 0xc0, 0xad, 0x2a, 0x5e, 0xc4, 0xef, 0x45,
-	0xbc, 0x88, 0xf7, 0xe2, 0xbd, 0x17, 0x11, 0x09, 0xaf, 0x35, 0x1c, 0xbf, 0xd9, 0xdd, 0x2a, 0xd7,
-	0xbc, 0xf6, 0x32, 0xd9, 0xe9, 0x3a, 0xfe, 0xde, 0xf2, 0x0e, 0x61, 0x0d, 0x6f, 0x99, 0x74, 0x9c,
-	0xe5, 0xdd, 0x33, 0xa4, 0xd5, 0x69, 0x92, 0x33, 0xcb, 0x0d, 0xea, 0x52, 0x46, 0x7c, 0x5a, 0x2f,
-	0x77, 0x98, 0xe7, 0x7b, 0xe8, 0xb9, 0xa8, 0x55, 0x59, 0xb5, 0x2a, 0xcb, 0x56, 0x65, 0xd2, 0x71,
-	0xca, 0x41, 0xab, 0xc5, 0x57, 0x0c, 0xec, 0x86, 0xd7, 0xf0, 0x96, 0x65, 0xe3, 0xad, 0xee, 0xb6,
-	0xfc, 0x27, 0xff, 0xc8, 0x5f, 0x0a, 0x74, 0xd1, 0xde, 0x39, 0xcf, 0xcb, 0x8e, 0xe2, 0x5c, 0xf3,
-	0x18, 0x5d, 0xde, 0xed, 0x61, 0xbc, 0x78, 0x3d, 0xaa, 0x43, 0xef, 0xfb, 0xd4, 0xe5, 0x8e, 0xe7,
-	0xf2, 0x57, 0x48, 0xc7, 0xe1, 0x94, 0xed, 0x52, 0xb6, 0xdc, 0xd9, 0x69, 0x08, 0x1a, 0x8f, 0x57,
-	0x48, 0x43, 0x7a, 0x2d, 0x42, 0x6a, 0x93, 0x5a, 0xd3, 0x71, 0x29, 0xdb, 0x8b, 0x9a, 0xb7, 0xa9,
-	0x4f, 0xd2, 0x5a, 0x2d, 0xf7, 0x6b, 0xc5, 0xba, 0xae, 0xef, 0xb4, 0x69, 0x4f, 0x83, 0xd7, 0x0f,
-	0x6b, 0xc0, 0x6b, 0x4d, 0xda, 0x26, 0xc9, 0x76, 0xf6, 0x7b, 0x70, 0x72, 0xc5, 0x25, 0xad, 0x3d,
-	0xee, 0x70, 0xdc, 0x75, 0x57, 0x58, 0xa3, 0xdb, 0xa6, 0xae, 0x8f, 0x4e, 0xc3, 0x90, 0x4b, 0xda,
-	0x74, 0xc1, 0x3a, 0x6d, 0xbd, 0x30, 0x5e, 0x99, 0xf8, 0x6c, 0x7f, 0xe9, 0xc4, 0xc1, 0xfe, 0xd2,
-	0xd0, 0xdb, 0xa4, 0x4d, 0xb1, 0xa4, 0xa0, 0x67, 0x61, 0x78, 0x97, 0xb4, 0xba, 0x74, 0xa1, 0x20,
-	0xab, 0x4c, 0xea, 0x2a, 0xc3, 0xb7, 0x45, 0x21, 0x56, 0x34, 0xfb, 0x37, 0x8b, 0x31, 0xf8, 0x6f,
-	0x51, 0x9f, 0xd4, 0x89, 0x4f, 0x50, 0x1b, 0x46, 0x5a, 0x64, 0x8b, 0xb6, 0xf8, 0x82, 0x75, 0xba,
-	0xf8, 0x42, 0xe9, 0xec, 0x95, 0x72, 0x96, 0x89, 0x2e, 0xa7, 0x40, 0x95, 0x37, 0x24, 0xce, 0x15,
-	0xd7, 0x67, 0x7b, 0x95, 0x29, 0xdd, 0x89, 0x11, 0x55, 0x88, 0x35, 0x13, 0xf4, 0xeb, 0x16, 0x94,
-	0x88, 0xeb, 0x7a, 0x3e, 0xf1, 0xc5, 0x34, 0x2d, 0x14, 0x24, 0xd3, 0xb7, 0x06, 0x67, 0xba, 0x12,
-	0x81, 0x29, 0xce, 0x27, 0x35, 0xe7, 0x92, 0x41, 0xc1, 0x26, 0xcf, 0xc5, 0x0b, 0x50, 0x32, 0xba,
-	0x8a, 0x66, 0xa0, 0xb8, 0x43, 0xf7, 0x94, 0x7c, 0xb1, 0xf8, 0x89, 0xe6, 0x62, 0x02, 0xd5, 0x12,
-	0xbc, 0x58, 0x38, 0x6f, 0x2d, 0x5e, 0x86, 0x99, 0x24, 0xc3, 0x3c, 0xed, 0xed, 0xdf, 0xb7, 0x60,
-	0xce, 0x18, 0x05, 0xa6, 0xdb, 0x94, 0x51, 0xb7, 0x46, 0xd1, 0x32, 0x8c, 0x8b, 0xb9, 0xe4, 0x1d,
-	0x52, 0x0b, 0xa6, 0x7a, 0x56, 0x0f, 0x64, 0xfc, 0xed, 0x80, 0x80, 0xa3, 0x3a, 0xe1, 0xb2, 0x28,
-	0x3c, 0x6c, 0x59, 0x74, 0x9a, 0x84, 0xd3, 0x85, 0x62, 0x7c, 0x59, 0x6c, 0x8a, 0x42, 0xac, 0x68,
-	0xf6, 0x3d, 0xf8, 0x5a, 0xd0, 0x9f, 0x9b, 0xb4, 0xdd, 0x69, 0x11, 0x9f, 0x46, 0x9d, 0x3a, 0x7c,
-	0xe9, 0x9d, 0x86, 0xa1, 0x1d, 0xc7, 0xad, 0x27, 0x7b, 0xf1, 0x4d, 0xc7, 0xad, 0x63, 0x49, 0xb1,
-	0x77, 0x60, 0x72, 0xa5, 0xd3, 0x61, 0xde, 0x2e, 0xad, 0x57, 0x7d, 0xd2, 0xa0, 0xe8, 0x2e, 0x00,
-	0xd1, 0x05, 0x2b, 0xbe, 0x84, 0x2e, 0x9d, 0xfd, 0xd9, 0xb2, 0xd2, 0x99, 0xb2, 0xa9, 0x33, 0xe5,
-	0xce, 0x4e, 0x43, 0x14, 0xf0, 0xb2, 0x50, 0xcd, 0xf2, 0xee, 0x99, 0xf2, 0x4d, 0xa7, 0x4d, 0x2b,
-	0x53, 0x07, 0xfb, 0x4b, 0xb0, 0x12, 0x22, 0x60, 0x03, 0xcd, 0xfe, 0x0d, 0x0b, 0xe6, 0x57, 0x58,
-	0xc3, 0x5b, 0x5d, 0x5b, 0xe9, 0x74, 0xae, 0x53, 0xd2, 0xf2, 0x9b, 0x55, 0x9f, 0xf8, 0x5d, 0x8e,
-	0x2e, 0xc3, 0x08, 0x97, 0xbf, 0xf4, 0x60, 0x9e, 0x0f, 0xd6, 0xa7, 0xa2, 0x3f, 0xd8, 0x5f, 0x9a,
-	0x4b, 0x69, 0x48, 0xb1, 0x6e, 0x85, 0x5e, 0x84, 0xd1, 0x36, 0xe5, 0x9c, 0x34, 0x02, 0x89, 0x4f,
-	0x6b, 0x80, 0xd1, 0x6f, 0xa9, 0x62, 0x1c, 0xd0, 0xed, 0xbf, 0x2d, 0xc0, 0x74, 0x88, 0xa5, 0xd9,
-	0x1f, 0xc3, 0xf4, 0x76, 0x61, 0xa2, 0x69, 0x8c, 0x50, 0xce, 0x72, 0xe9, 0xec, 0xa5, 0x8c, 0x9a,
-	0x94, 0x26, 0xa4, 0xca, 0x9c, 0x66, 0x33, 0x61, 0x96, 0xe2, 0x18, 0x1b, 0xd4, 0x06, 0xe0, 0x7b,
-	0x6e, 0x4d, 0x33, 0x1d, 0x92, 0x4c, 0x2f, 0xe4, 0x64, 0x5a, 0x0d, 0x01, 0x2a, 0x48, 0xb3, 0x84,
-	0xa8, 0x0c, 0x1b, 0x0c, 0xec, 0xbf, 0xb0, 0xe0, 0x64, 0x4a, 0x3b, 0xf4, 0x66, 0x62, 0x3e, 0x9f,
-	0xeb, 0x99, 0x4f, 0xd4, 0xd3, 0x2c, 0x9a, 0xcd, 0x97, 0x61, 0x8c, 0xd1, 0x5d, 0x47, 0xec, 0x14,
-	0x5a, 0xc2, 0x33, 0xba, 0xfd, 0x18, 0xd6, 0xe5, 0x38, 0xac, 0x81, 0x5e, 0x82, 0xf1, 0xe0, 0xb7,
-	0x10, 0x73, 0x51, 0x28, 0x93, 0x98, 0xb8, 0xa0, 0x2a, 0xc7, 0x11, 0xdd, 0xfe, 0x35, 0x18, 0x5e,
-	0x6d, 0x12, 0xe6, 0x8b, 0x15, 0xc3, 0x68, 0xc7, 0xbb, 0x85, 0x37, 0x74, 0x17, 0xc3, 0x15, 0x83,
-	0x55, 0x31, 0x0e, 0xe8, 0x19, 0x26, 0xfb, 0x45, 0x18, 0xdd, 0xa5, 0x4c, 0xf6, 0xb7, 0x18, 0x07,
-	0xbb, 0xad, 0x8a, 0x71, 0x40, 0xb7, 0xff, 0xd1, 0x82, 0x39, 0xd9, 0x83, 0x35, 0x87, 0xd7, 0xbc,
-	0x5d, 0xca, 0xf6, 0x30, 0xe5, 0xdd, 0xd6, 0x11, 0x77, 0x68, 0x0d, 0x66, 0x38, 0x6d, 0xef, 0x52,
-	0xb6, 0xea, 0xb9, 0xdc, 0x67, 0xc4, 0x71, 0x7d, 0xdd, 0xb3, 0x05, 0x5d, 0x7b, 0xa6, 0x9a, 0xa0,
-	0xe3, 0x9e, 0x16, 0xe8, 0x05, 0x18, 0xd3, 0xdd, 0x16, 0x4b, 0x49, 0x08, 0x76, 0x42, 0xcc, 0x81,
-	0x1e, 0x13, 0xc7, 0x21, 0xd5, 0xfe, 0x37, 0x0b, 0x66, 0xe5, 0xa8, 0xaa, 0xdd, 0x2d, 0x5e, 0x63,
-	0x4e, 0x47, 0x18, 0xe0, 0x27, 0x71, 0x48, 0x97, 0x61, 0xaa, 0x1e, 0x08, 0x7e, 0xc3, 0x69, 0x3b,
-	0xbe, 0xd4, 0x91, 0xe1, 0xca, 0x53, 0x1a, 0x63, 0x6a, 0x2d, 0x46, 0xc5, 0x89, 0xda, 0x6a, 0xfa,
-	0x5a, 0x5d, 0xee, 0x53, 0xb6, 0xc9, 0xbc, 0xb6, 0x27, 0xc6, 0x79, 0x93, 0xf0, 0x1d, 0xf4, 0x8b,
-	0x30, 0xd6, 0xd6, 0x9b, 0x9e, 0xb6, 0x9a, 0x3f, 0x97, 0xcd, 0x6a, 0xde, 0xd8, 0xfa, 0x0e, 0xad,
-	0xf9, 0x62, 0xc3, 0x8c, 0xb4, 0x2d, 0x2a, 0xc3, 0x21, 0x2a, 0x7a, 0x07, 0x86, 0x78, 0x87, 0xd6,
-	0xa4, 0x88, 0x4a, 0x67, 0xdf, 0xc8, 0xa6, 0xd4, 0xb1, 0x4e, 0x56, 0x3b, 0xb4, 0x16, 0xc9, 0x56,
-	0xfc, 0xc3, 0x12, 0xd2, 0xfe, 0x67, 0x0b, 0x16, 0xd2, 0x46, 0xb5, 0xe1, 0x70, 0x1f, 0xbd, 0xd7,
-	0x33, 0xb2, 0x72, 0xb6, 0x91, 0x89, 0xd6, 0x72, 0x5c, 0xa1, 0xf6, 0x06, 0x25, 0xc6, 0xa8, 0xee,
-	0xc1, 0xb0, 0xe3, 0xd3, 0x76, 0xe0, 0x6a, 0x5c, 0xcc, 0x36, 0xac, 0xb4, 0xce, 0x46, 0x5b, 0xe8,
-	0xba, 0x00, 0xc4, 0x0a, 0xd7, 0x7e, 0x17, 0x26, 0x56, 0xbb, 0x8c, 0x51, 0xd7, 0x57, 0x1b, 0xdc,
-	0x37, 0x61, 0x98, 0x3b, 0xae, 0xb6, 0xf3, 0xf9, 0xf6, 0xb6, 0x71, 0x01, 0x5e, 0x15, 0x8d, 0xb1,
-	0xc2, 0xb0, 0xff, 0xa8, 0x08, 0x27, 0x83, 0x15, 0x43, 0xeb, 0x2b, 0xcc, 0x77, 0xb6, 0x49, 0xcd,
-	0xe7, 0xa8, 0x0e, 0x13, 0xf5, 0xa8, 0xd8, 0xd7, 0x86, 0x38, 0x0f, 0xaf, 0xd0, 0xd8, 0x1b, 0xf0,
-	0x3e, 0x8e, 0xa1, 0xa2, 0x3b, 0x50, 0x6c, 0x38, 0xbe, 0xf6, 0x0c, 0xcf, 0x67, 0x93, 0xdc, 0x35,
-	0x27, 0x69, 0x79, 0x2a, 0x25, 0xcd, 0xaa, 0x78, 0xcd, 0xf1, 0xb1, 0x40, 0x44, 0x5b, 0x30, 0xe2,
-	0xb4, 0x49, 0x83, 0xe6, 0x9c, 0x95, 0x75, 0xd1, 0x26, 0x89, 0x1e, 0xba, 0x9a, 0x92, 0xca, 0xb1,
-	0x46, 0x16, 0x3c, 0x6a, 0xc2, 0x62, 0x28, 0x9b, 0x9d, 0x7d, 0xe6, 0x53, 0x6c, 0x67, 0xc4, 0x43,
-	0x52, 0x39, 0xd6, 0xc8, 0xf6, 0x97, 0x05, 0x98, 0x89, 0xe4, 0xb7, 0xea, 0xb5, 0xdb, 0x8e, 0x8f,
-	0x16, 0xa1, 0xe0, 0xd4, 0xb5, 0x41, 0x02, 0xdd, 0xb0, 0xb0, 0xbe, 0x86, 0x0b, 0x4e, 0x1d, 0x3d,
-	0x0f, 0x23, 0x5b, 0x8c, 0xb8, 0xb5, 0xa6, 0x36, 0x44, 0x21, 0x70, 0x45, 0x96, 0x62, 0x4d, 0x45,
-	0xcf, 0x40, 0xd1, 0x27, 0x0d, 0x6d, 0x7f, 0x42, 0xf9, 0xdd, 0x24, 0x0d, 0x2c, 0xca, 0x85, 0xe1,
-	0xe3, 0x5d, 0xa9, 0xc3, 0x72, 0xe6, 0x0d, 0xc3, 0x57, 0x55, 0xc5, 0x38, 0xa0, 0x0b, 0x8e, 0xa4,
-	0xeb, 0x37, 0x3d, 0xb6, 0x30, 0x1c, 0xe7, 0xb8, 0x22, 0x4b, 0xb1, 0xa6, 0x0a, 0x17, 0xa5, 0x26,
-	0xfb, 0xef, 0x53, 0xb6, 0x30, 0x12, 0x77, 0x51, 0x56, 0x03, 0x02, 0x8e, 0xea, 0xa0, 0xf7, 0xa1,
-	0x54, 0x63, 0x94, 0xf8, 0x1e, 0x5b, 0x23, 0x3e, 0x5d, 0x18, 0xcd, 0xbd, 0x02, 0xa7, 0x85, 0x97,
-	0xbe, 0x1a, 0x41, 0x60, 0x13, 0xcf, 0xfe, 0xab, 0x22, 0x2c, 0x44, 0xa2, 0x95, 0x73, 0x1b, 0x79,
-	0xa6, 0x5a, 0x3c, 0x56, 0x1f, 0xf1, 0x3c, 0x0f, 0x23, 0x75, 0xa7, 0x41, 0xb9, 0x9f, 0x94, 0xf2,
-	0x9a, 0x2c, 0xc5, 0x9a, 0x8a, 0x7e, 0x27, 0x11, 0x8d, 0x0c, 0xcb, 0x85, 0x72, 0x23, 0xdb, 0x42,
-	0xe9, 0xd7, 0xb9, 0x01, 0x42, 0x12, 0x74, 0x16, 0xa0, 0xe1, 0xf8, 0x7a, 0xd3, 0xd2, 0xb3, 0x1e,
-	0x1a, 0xeb, 0x6b, 0x21, 0x05, 0x1b, 0xb5, 0xd0, 0x1d, 0x18, 0x97, 0xf2, 0x1a, 0x50, 0xff, 0xa5,
-	0x0b, 0xb3, 0x1a, 0x00, 0xe0, 0x08, 0xeb, 0x91, 0x83, 0x9c, 0x77, 0x01, 0x5d, 0xb9, 0xdf, 0x61,
-	0x94, 0x8b, 0xad, 0xfb, 0x36, 0x61, 0x0e, 0xd9, 0x6a, 0xd1, 0xa3, 0x8a, 0x63, 0x3f, 0x1f, 0x82,
-	0xd1, 0xab, 0x8c, 0x3a, 0x8d, 0xa6, 0xff, 0x18, 0xb6, 0xc4, 0x67, 0x61, 0x98, 0xb4, 0x1c, 0xc2,
-	0xe5, 0xea, 0x36, 0xba, 0xb4, 0x22, 0x0a, 0xb1, 0xa2, 0xa1, 0x77, 0x61, 0xc4, 0x63, 0x4e, 0xc3,
-	0x71, 0x17, 0xc6, 0x65, 0x27, 0x5e, 0xcd, 0xb6, 0x7e, 0xf4, 0x28, 0x6e, 0xc8, 0xa6, 0xd1, 0x12,
-	0x55, 0xff, 0xb1, 0x86, 0x44, 0x77, 0x61, 0x54, 0xa9, 0x5c, 0x60, 0xc6, 0x96, 0x33, 0x9b, 0x61,
-	0xa5, 0xb5, 0x91, 0x69, 0x50, 0xff, 0x39, 0x0e, 0x00, 0x51, 0x35, 0xb4, 0xc2, 0x43, 0x12, 0xfa,
-	0xa5, 0x1c, 0x56, 0xb8, 0xaf, 0xd9, 0xad, 0x86, 0x66, 0x77, 0x38, 0x0f, 0xa8, 0x34, 0xac, 0xfd,
-	0xec, 0xac, 0x10, 0xb1, 0x76, 0xf7, 0x47, 0x06, 0x10, 0xb1, 0x8e, 0x35, 0xa6, 0xe2, 0x31, 0x42,
-	0x10, 0x0d, 0xd8, 0xdf, 0x2b, 0xc2, 0xac, 0xae, 0xb9, 0xea, 0xb5, 0x5a, 0xb4, 0x26, 0x7d, 0x4b,
-	0x65, 0xc5, 0x8b, 0xa9, 0x56, 0xdc, 0x09, 0x7c, 0x0a, 0xb5, 0x33, 0x56, 0x72, 0xf5, 0x26, 0xe2,
-	0x51, 0x96, 0x7e, 0x84, 0xb2, 0x11, 0xe1, 0x2c, 0xe9, 0x5a, 0xda, 0xbb, 0x40, 0xbf, 0x6d, 0xc1,
-	0xc9, 0x5d, 0xca, 0x9c, 0x6d, 0xa7, 0x26, 0xf5, 0xf1, 0xba, 0xc3, 0x7d, 0x8f, 0xed, 0xe9, 0x7d,
-	0xf3, 0xf5, 0x6c, 0x9c, 0x6f, 0x1b, 0x00, 0xeb, 0xee, 0xb6, 0x57, 0xf9, 0xba, 0xe6, 0x76, 0xf2,
-	0x76, 0x2f, 0x34, 0x4e, 0xe3, 0xb7, 0xd8, 0x01, 0x88, 0x7a, 0x9b, 0x62, 0x0e, 0x36, 0x4c, 0xe5,
-	0xcd, 0xdc, 0xb1, 0x60, 0xb0, 0x81, 0xed, 0x34, 0xcd, 0xc8, 0xa7, 0x16, 0x94, 0x34, 0xfd, 0x31,
-	0xb8, 0x89, 0x38, 0xee, 0x26, 0xbe, 0x92, 0xab, 0xff, 0x7d, 0x3c, 0x43, 0x06, 0x93, 0x31, 0x25,
-	0x47, 0xe7, 0x74, 0xba, 0x44, 0xd9, 0xc0, 0xff, 0x6f, 0xa6, 0x4b, 0x1e, 0xec, 0x2f, 0xcd, 0xc6,
-	0x2a, 0x47, 0x39, 0x94, 0xc3, 0x63, 0x97, 0x8b, 0x63, 0xdf, 0xff, 0xd3, 0xa5, 0x13, 0x1f, 0xfd,
-	0xe4, 0xf4, 0x09, 0xfb, 0x93, 0x22, 0xcc, 0x24, 0xa5, 0x9a, 0xc1, 0xf6, 0x46, 0x36, 0x6c, 0xec,
-	0x58, 0x6d, 0x58, 0xe1, 0xf8, 0x6c, 0x58, 0xf1, 0x38, 0x6c, 0xd8, 0xd0, 0x91, 0xd9, 0x30, 0xfb,
-	0x1f, 0x2c, 0x98, 0x0a, 0x67, 0xe6, 0x83, 0xae, 0xf0, 0x3f, 0x22, 0xa9, 0x5b, 0x47, 0x2f, 0xf5,
-	0x7b, 0x30, 0xca, 0xbd, 0x2e, 0xab, 0x49, 0x27, 0x5b, 0xa0, 0xbf, 0x96, 0xcf, 0x68, 0xaa, 0xb6,
-	0x86, 0x67, 0xa9, 0x0a, 0x70, 0x80, 0x6a, 0x7f, 0x5a, 0x08, 0x07, 0xa4, 0x69, 0xca, 0xf1, 0x62,
-	0xc2, 0x2d, 0x15, 0x03, 0x1a, 0x33, 0x1d, 0x2f, 0x51, 0x8a, 0x35, 0x15, 0xd9, 0xd2, 0x9e, 0x07,
-	0xfe, 0xff, 0x78, 0x05, 0xb4, 0x59, 0x96, 0x93, 0xa0, 0x28, 0xa8, 0x03, 0x33, 0x8c, 0x7e, 0xd0,
-	0x75, 0x18, 0xad, 0x57, 0x3d, 0xb2, 0x23, 0x9c, 0x16, 0x9d, 0xe4, 0xca, 0xa8, 0xf7, 0x6b, 0x5d,
-	0x26, 0x4d, 0x58, 0x65, 0x4e, 0xc4, 0xee, 0x38, 0x81, 0x85, 0x7b, 0xd0, 0x91, 0x07, 0x73, 0x64,
-	0x97, 0x38, 0x2d, 0xb2, 0xe5, 0xb4, 0x1c, 0x7f, 0xaf, 0xea, 0x33, 0xe2, 0xd3, 0xc6, 0x9e, 0x76,
-	0xb1, 0x2f, 0xe9, 0xb1, 0xcc, 0xad, 0xa4, 0xd4, 0x79, 0xb0, 0xbf, 0xf4, 0x75, 0x2d, 0x8b, 0x34,
-	0x32, 0x4e, 0x05, 0xb6, 0xff, 0x65, 0x38, 0xb4, 0x10, 0x3a, 0xaf, 0xf5, 0xcb, 0x50, 0xaa, 0xa9,
-	0x60, 0xb2, 0xb5, 0xb7, 0xee, 0xea, 0x35, 0xbd, 0x36, 0xc0, 0x6e, 0x57, 0x5e, 0x8d, 0x60, 0x12,
-	0x5e, 0xa8, 0x41, 0xc1, 0x26, 0x37, 0xf4, 0x21, 0x80, 0x32, 0xfd, 0xb4, 0xbe, 0xee, 0xea, 0xbd,
-	0x6d, 0x75, 0x10, 0xde, 0xb7, 0x43, 0x14, 0xc5, 0x3a, 0x74, 0xb2, 0x22, 0x02, 0x36, 0x58, 0x89,
-	0x51, 0x07, 0x59, 0xdc, 0xab, 0x1e, 0xd3, 0x46, 0x62, 0xa0, 0x51, 0xaf, 0x44, 0x30, 0x49, 0xdf,
-	0x3b, 0xa2, 0x60, 0x93, 0xdb, 0x22, 0x83, 0x99, 0xa4, 0xac, 0x52, 0xf6, 0xb7, 0xeb, 0xf1, 0xfd,
-	0xed, 0x6c, 0x46, 0x8b, 0x60, 0x24, 0x06, 0xcc, 0x73, 0x04, 0x06, 0xd3, 0x09, 0x19, 0xa5, 0xb0,
-	0x5c, 0x8f, 0xb3, 0x7c, 0x35, 0xcf, 0x5e, 0xaf, 0xb3, 0xed, 0x26, 0x4f, 0x0e, 0x33, 0x49, 0xe9,
-	0x1c, 0x19, 0xd3, 0x58, 0x8a, 0xdf, 0xdc, 0xc4, 0xff, 0xb8, 0x00, 0xe3, 0xa1, 0x19, 0xcf, 0x93,
-	0xaf, 0x53, 0xee, 0x57, 0xe1, 0x90, 0x20, 0xba, 0x98, 0x25, 0x88, 0x1e, 0xea, 0x1f, 0x44, 0x07,
-	0x39, 0xfd, 0x91, 0x87, 0xe7, 0xf4, 0x8d, 0x20, 0x7a, 0x34, 0x7b, 0x10, 0x3d, 0x76, 0x78, 0x10,
-	0x6d, 0xff, 0x99, 0x05, 0xa8, 0x37, 0x63, 0x92, 0x47, 0x50, 0x24, 0xb9, 0xb9, 0xbe, 0x9e, 0x37,
-	0x7c, 0x3d, 0x6c, 0x8f, 0xb5, 0x19, 0xcc, 0x5f, 0x73, 0xfc, 0xeb, 0xdd, 0xad, 0x3b, 0x74, 0xab,
-	0xe9, 0x79, 0x3b, 0x98, 0xd6, 0xa8, 0xb3, 0x4b, 0x19, 0x7a, 0x07, 0xc6, 0x39, 0xad, 0x31, 0x2a,
-	0x5c, 0x0d, 0xbd, 0x85, 0xbd, 0x60, 0xd8, 0xe6, 0x72, 0xcd, 0x63, 0x54, 0x7a, 0x60, 0x5e, 0x8d,
-	0xb4, 0x54, 0x80, 0x15, 0x3a, 0x25, 0x91, 0x60, 0xaa, 0x01, 0x04, 0x8e, 0xd0, 0xec, 0x4f, 0x87,
-	0x61, 0xfa, 0x9a, 0x33, 0x70, 0xba, 0xd7, 0x87, 0xa7, 0x55, 0xef, 0xab, 0x54, 0x3b, 0xdb, 0xa1,
-	0x35, 0x57, 0x6b, 0xea, 0xa2, 0x6e, 0xfa, 0xf4, 0x6a, 0x7a, 0xb5, 0x07, 0xfd, 0x49, 0xb8, 0x1f,
-	0x74, 0xe6, 0x85, 0x79, 0x09, 0x26, 0xb9, 0xcf, 0x9c, 0x9a, 0xaf, 0x12, 0xca, 0x7c, 0xa1, 0x24,
-	0x77, 0xcb, 0x79, 0x5d, 0x7d, 0xb2, 0x6a, 0x12, 0x71, 0xbc, 0x6e, 0x6a, 0x9e, 0x7a, 0x28, 0x77,
-	0x9e, 0x7a, 0x19, 0xc6, 0x49, 0xab, 0xe5, 0x7d, 0x78, 0x93, 0x34, 0xb8, 0xce, 0x0c, 0x85, 0x13,
-	0xb2, 0x12, 0x10, 0x70, 0x54, 0x07, 0x95, 0x01, 0x9c, 0x86, 0xeb, 0x31, 0x2a, 0x5b, 0x8c, 0xc8,
-	0x6d, 0x5b, 0x9e, 0xc5, 0xad, 0x87, 0xa5, 0xd8, 0xa8, 0x81, 0xaa, 0x30, 0xef, 0xb8, 0x9c, 0xd6,
-	0xba, 0x8c, 0x56, 0x77, 0x9c, 0xce, 0xcd, 0x8d, 0xaa, 0xb4, 0x4c, 0x7b, 0x52, 0x83, 0xc6, 0x2a,
-	0xcf, 0x68, 0x66, 0xf3, 0xeb, 0x69, 0x95, 0x70, 0x7a, 0x5b, 0xf4, 0x1a, 0x4c, 0x38, 0x6e, 0xad,
-	0xd5, 0xad, 0xd3, 0x4d, 0xe2, 0x37, 0xf9, 0xc2, 0x98, 0xec, 0xc6, 0xcc, 0xc1, 0xfe, 0xd2, 0xc4,
-	0xba, 0x51, 0x8e, 0x63, 0xb5, 0x44, 0x2b, 0x7a, 0xdf, 0x68, 0x35, 0x1e, 0xb5, 0xba, 0x72, 0xdf,
-	0x6c, 0x65, 0xd6, 0x4a, 0xc9, 0xe4, 0x43, 0xae, 0x4c, 0xfe, 0x0f, 0x0b, 0x30, 0xa2, 0x0e, 0xd2,
-	0xd0, 0xb9, 0xc4, 0x69, 0xd5, 0x33, 0x3d, 0xa7, 0x55, 0xa5, 0xb4, 0x43, 0x47, 0x1b, 0x46, 0x1c,
-	0xce, 0xbb, 0x71, 0x2f, 0x69, 0x5d, 0x96, 0x60, 0x4d, 0x91, 0x59, 0x4e, 0xcf, 0xdd, 0x76, 0x1a,
-	0x3a, 0x05, 0x74, 0xd9, 0xd0, 0xbf, 0xe8, 0x3a, 0xc4, 0xbd, 0xf0, 0xbe, 0x44, 0xe4, 0x26, 0xc5,
-	0x2a, 0x08, 0x2d, 0x7d, 0xab, 0x7a, 0xe3, 0x6d, 0xc5, 0x63, 0x55, 0x22, 0x62, 0x8d, 0x2c, 0x78,
-	0x78, 0x5d, 0xbf, 0xd3, 0xf5, 0xe5, 0x42, 0x39, 0x22, 0x1e, 0x37, 0x24, 0x22, 0xd6, 0xc8, 0xf6,
-	0x27, 0x16, 0x4c, 0x2b, 0x19, 0xac, 0x36, 0x69, 0x6d, 0xa7, 0xea, 0xd3, 0x8e, 0x08, 0x5b, 0xba,
-	0x9c, 0xf2, 0x64, 0xd8, 0x72, 0x8b, 0x53, 0x8e, 0x25, 0xc5, 0x18, 0x7d, 0xe1, 0xb8, 0x46, 0x6f,
-	0x9f, 0x07, 0x63, 0x72, 0xe4, 0x49, 0xb0, 0x3a, 0x10, 0x55, 0xdb, 0x66, 0x31, 0x32, 0x42, 0xaa,
-	0xd6, 0x1e, 0x0e, 0xe8, 0xf6, 0x41, 0x01, 0x86, 0x65, 0x64, 0x91, 0xc7, 0x72, 0xc5, 0x53, 0x81,
-	0x85, 0x4c, 0xa9, 0xc0, 0x43, 0xb2, 0xc5, 0x51, 0x3a, 0x74, 0xe8, 0xa1, 0xe9, 0x50, 0x9e, 0x96,
-	0x0d, 0x7d, 0x33, 0x47, 0x40, 0x35, 0xc8, 0x6d, 0x8c, 0x47, 0xcd, 0x36, 0xfe, 0xd4, 0x82, 0xb9,
-	0xb4, 0x73, 0x81, 0x3c, 0x32, 0x7f, 0x19, 0xc6, 0x3a, 0x2d, 0xe2, 0x6f, 0x7b, 0xac, 0x9d, 0x3c,
-	0x0f, 0xde, 0xd4, 0xe5, 0x38, 0xac, 0x81, 0x18, 0x00, 0x0b, 0x76, 0xb1, 0x20, 0xec, 0xbc, 0xfc,
-	0x68, 0x39, 0xe3, 0x68, 0x86, 0xc3, 0x22, 0x8e, 0x0d, 0x2e, 0xf6, 0xef, 0x0e, 0xc3, 0xac, 0x6c,
-	0x32, 0xe8, 0x86, 0x38, 0xc8, 0xb2, 0xea, 0xc0, 0x53, 0x32, 0x20, 0xee, 0xdd, 0x43, 0xd5, 0x4a,
-	0x3b, 0xaf, 0xdb, 0x3f, 0xb5, 0x9e, 0x5a, 0xeb, 0x41, 0x5f, 0x0a, 0xee, 0x83, 0xdb, 0xbb, 0x31,
-	0xc2, 0xff, 0xbd, 0x8d, 0xd1, 0x5c, 0x6c, 0xa3, 0x87, 0x2e, 0xb6, 0xbe, 0xdb, 0xe8, 0xd8, 0x23,
-	0x6c, 0xa3, 0xbd, 0x5b, 0xdb, 0x78, 0xae, 0xad, 0xed, 0x4f, 0x0a, 0x30, 0xba, 0xc9, 0x3c, 0x79,
-	0xbe, 0x74, 0xfc, 0x49, 0xf8, 0x5b, 0x03, 0x9e, 0x4b, 0x0b, 0x28, 0x65, 0xcb, 0xe5, 0xb9, 0xf4,
-	0x58, 0xfc, 0x4c, 0xda, 0xc8, 0x29, 0x17, 0xf3, 0x84, 0x3a, 0x1a, 0xf8, 0x90, 0x9c, 0xf2, 0x5f,
-	0x16, 0x60, 0x32, 0xd6, 0x85, 0x27, 0xf8, 0xfc, 0x3e, 0x21, 0xa7, 0x94, 0xf3, 0x7b, 0x44, 0x12,
-	0xb2, 0xba, 0x30, 0x08, 0xf8, 0xc3, 0x25, 0xf6, 0x77, 0x16, 0xcc, 0xc6, 0xea, 0x3f, 0x86, 0xa4,
-	0xef, 0xb7, 0xe3, 0x49, 0xdf, 0x57, 0x07, 0x18, 0x55, 0x9f, 0xd4, 0xef, 0x77, 0x0b, 0x89, 0xd1,
-	0x08, 0x61, 0xa2, 0x5f, 0x85, 0xd9, 0x4e, 0x70, 0xa3, 0x60, 0xd3, 0x6b, 0x39, 0x35, 0x87, 0x06,
-	0x67, 0x08, 0xe7, 0x72, 0x5e, 0xb7, 0x90, 0xcd, 0xf7, 0x2a, 0x5f, 0xd3, 0xdc, 0x67, 0x37, 0x93,
-	0xb8, 0xb8, 0x97, 0x15, 0xe2, 0x30, 0xce, 0x74, 0xec, 0x16, 0x8c, 0x39, 0xe3, 0x85, 0xb1, 0x44,
-	0xe4, 0xa7, 0xc7, 0x1e, 0xda, 0xd5, 0x04, 0x59, 0xde, 0x88, 0xd2, 0x3f, 0xed, 0x7f, 0xb7, 0xe0,
-	0x64, 0xca, 0x42, 0x40, 0x35, 0x80, 0x9a, 0xe7, 0xd6, 0x1d, 0xe5, 0x6c, 0x58, 0x3a, 0x31, 0x9c,
-	0x69, 0x72, 0x57, 0x83, 0x76, 0x91, 0x46, 0x84, 0x45, 0x1c, 0x1b, 0xb0, 0xa8, 0xdd, 0x3b, 0xe2,
-	0x73, 0x03, 0x8d, 0x38, 0xdb, 0x58, 0x3f, 0xb5, 0xa0, 0xa4, 0xc7, 0xfa, 0xc4, 0x9e, 0x59, 0xe8,
-	0xfe, 0xf5, 0x59, 0xb8, 0x5f, 0x58, 0x30, 0x61, 0x98, 0x38, 0x8e, 0x9a, 0x00, 0x1f, 0x12, 0x46,
-	0x9b, 0x5e, 0xe8, 0x8a, 0x67, 0xce, 0x24, 0xdf, 0x09, 0xda, 0x49, 0xa4, 0x68, 0xae, 0xc2, 0x72,
-	0x8e, 0x0d, 0x6c, 0xf4, 0x6d, 0x23, 0x29, 0xac, 0xec, 0x63, 0x26, 0x2e, 0x32, 0xe7, 0xa4, 0x38,
-	0x98, 0xb6, 0xc5, 0x48, 0x25, 0xdb, 0x3f, 0xb2, 0x42, 0x6b, 0x9c, 0xba, 0xf8, 0x8a, 0xc7, 0xb3,
-	0xf8, 0xaa, 0x30, 0x2c, 0x8c, 0x5b, 0x70, 0x4d, 0xf2, 0x6c, 0xee, 0x0d, 0x86, 0xeb, 0x1b, 0x41,
-	0xe2, 0x27, 0x56, 0x58, 0xf6, 0x0f, 0x0a, 0x30, 0x1e, 0x2a, 0xfb, 0x63, 0xdf, 0x7d, 0x5f, 0xcd,
-	0x69, 0xa6, 0xfa, 0xee, 0x28, 0xef, 0x27, 0x76, 0x94, 0xbc, 0xf6, 0xef, 0x90, 0xdd, 0xe4, 0x6f,
-	0xd4, 0x8c, 0xab, 0xba, 0x8f, 0x41, 0x15, 0x6f, 0xc6, 0x55, 0x71, 0x39, 0xe7, 0x68, 0xfa, 0x28,
-	0xe3, 0x47, 0x05, 0x98, 0x4e, 0x58, 0x7c, 0xf4, 0xac, 0x5c, 0x54, 0x8d, 0xe0, 0x30, 0x2f, 0x6c,
-	0xa8, 0x53, 0xaf, 0x92, 0x86, 0x76, 0x85, 0x1f, 0x1d, 0x7a, 0xd8, 0x1e, 0xd3, 0x42, 0xfe, 0xc6,
-	0x40, 0x9b, 0x4c, 0x00, 0x52, 0x99, 0x55, 0x2e, 0xb8, 0x81, 0x8b, 0xe3, 0x6c, 0xd0, 0x26, 0xcc,
-	0x91, 0xae, 0xef, 0x85, 0x00, 0x57, 0x5c, 0xb2, 0xd5, 0xa2, 0x2a, 0x8f, 0x3b, 0x56, 0xf9, 0x7f,
-	0xe1, 0x09, 0x4a, 0x4a, 0x1d, 0x9c, 0xda, 0xd2, 0xfe, 0x73, 0x0b, 0x9e, 0xee, 0xd3, 0x9f, 0x0c,
-	0xc7, 0x9a, 0x2d, 0x98, 0x94, 0x0f, 0x0f, 0x42, 0x39, 0x04, 0xab, 0x38, 0xdb, 0xcc, 0x9b, 0x4d,
-	0xd5, 0xe8, 0x63, 0x45, 0x38, 0x0e, 0x6e, 0xff, 0xb8, 0x00, 0x28, 0xec, 0x6b, 0x9e, 0xd3, 0xd7,
-	0xf7, 0x61, 0x74, 0x5b, 0x1d, 0x63, 0x3c, 0xda, 0xf1, 0x79, 0xa5, 0x64, 0xde, 0x20, 0x08, 0x30,
-	0xd1, 0x3b, 0x47, 0xa3, 0x6b, 0xd0, 0xab, 0x67, 0xe8, 0x2e, 0xc0, 0xb6, 0xe3, 0x3a, 0xbc, 0x39,
-	0xe0, 0x2d, 0x24, 0x19, 0x28, 0x5d, 0x0d, 0x11, 0xb0, 0x81, 0x66, 0xff, 0x41, 0xc1, 0xd0, 0x61,
-	0xe9, 0x3f, 0x65, 0x5a, 0xfb, 0x2f, 0xc6, 0x85, 0x39, 0xde, 0x7b, 0xb5, 0x22, 0x14, 0xcc, 0x5d,
-	0x18, 0xda, 0x25, 0x2c, 0x38, 0xe5, 0xcd, 0x78, 0xc1, 0xb1, 0xf7, 0x6e, 0x53, 0x34, 0xa7, 0xb7,
-	0x09, 0xe3, 0x58, 0x62, 0x0a, 0xdf, 0x92, 0xfb, 0xb4, 0x13, 0x6c, 0x2e, 0xb9, 0x0d, 0xa7, 0x4f,
-	0x3b, 0xe6, 0x00, 0x69, 0x47, 0xee, 0x00, 0xb4, 0xc3, 0xed, 0xef, 0x8d, 0x1a, 0x56, 0x41, 0xef,
-	0x67, 0x6f, 0x01, 0x6a, 0x11, 0xee, 0x5f, 0x27, 0x6e, 0x5d, 0xe8, 0x12, 0xdd, 0x66, 0x94, 0x37,
-	0x75, 0xf4, 0xbb, 0xa8, 0x51, 0xd0, 0x46, 0x4f, 0x0d, 0x9c, 0xd2, 0x0a, 0x9d, 0x0b, 0x1e, 0x8e,
-	0x28, 0x29, 0x2f, 0xc5, 0x1e, 0x8e, 0x3c, 0xd8, 0x5f, 0x9a, 0x8a, 0xf4, 0xd1, 0x78, 0x4a, 0x92,
-	0xe3, 0x89, 0x84, 0xb9, 0xde, 0x87, 0x8f, 0x61, 0xbd, 0xff, 0x0a, 0xcc, 0x6e, 0x27, 0xef, 0xda,
-	0xe8, 0xfb, 0x89, 0x6f, 0x0c, 0x78, 0x55, 0xa7, 0x32, 0x7f, 0x10, 0x5d, 0xd0, 0x88, 0x8a, 0x71,
-	0x2f, 0x23, 0xe4, 0x05, 0x0f, 0x33, 0x64, 0x22, 0x53, 0xe5, 0xa8, 0x33, 0xeb, 0x5c, 0x22, 0x05,
-	0x9a, 0x7c, 0x92, 0xa1, 0x20, 0x71, 0x8c, 0x41, 0x42, 0x07, 0x47, 0x8e, 0x52, 0x07, 0xd1, 0xb9,
-	0xf0, 0x3c, 0x5a, 0x74, 0x47, 0xa6, 0x09, 0x8a, 0x3d, 0x27, 0xc9, 0x82, 0x84, 0xcd, 0x7a, 0xe8,
-	0x63, 0x0b, 0xe6, 0xc5, 0x62, 0xbd, 0x72, 0x9f, 0xd6, 0xba, 0x42, 0x2a, 0xc1, 0x7b, 0xad, 0x85,
-	0x52, 0x9e, 0xa8, 0xa3, 0x9a, 0x06, 0x11, 0xe5, 0x3c, 0x52, 0xc9, 0x38, 0x9d, 0x31, 0xba, 0xa7,
-	0x9c, 0x31, 0x2a, 0x53, 0x4a, 0x8f, 0x9e, 0x29, 0x0e, 0x1d, 0x33, 0x65, 0x77, 0x7c, 0x6a, 0xff,
-	0x60, 0xc8, 0x34, 0x57, 0xd9, 0xf2, 0xd7, 0x77, 0x61, 0xc8, 0x27, 0x7c, 0x47, 0x6b, 0xc1, 0x9b,
-	0x03, 0x5c, 0xb9, 0x8f, 0x74, 0x41, 0xe6, 0x37, 0x64, 0x91, 0xc4, 0x44, 0x8b, 0x50, 0x20, 0x3c,
-	0x79, 0x82, 0xba, 0xc2, 0x71, 0x81, 0x70, 0x79, 0xba, 0xba, 0xad, 0xb3, 0x4f, 0xd1, 0xe9, 0xea,
-	0x36, 0x2e, 0x38, 0xdb, 0x68, 0x05, 0xa6, 0x6b, 0x9e, 0xeb, 0x3b, 0x6e, 0x97, 0xde, 0x70, 0xaf,
-	0x30, 0xe6, 0x31, 0x9d, 0x6b, 0x7a, 0x5a, 0x57, 0x9c, 0x5e, 0x8d, 0x93, 0x71, 0xb2, 0x3e, 0x7a,
-	0x07, 0x86, 0x19, 0xf5, 0xd9, 0x9e, 0xde, 0x10, 0xce, 0x0f, 0x60, 0xfb, 0xb0, 0x68, 0xaf, 0xa4,
-	0x2c, 0x7f, 0x62, 0x85, 0x18, 0x9a, 0xec, 0x91, 0x63, 0x30, 0xd9, 0xd1, 0x69, 0x42, 0xf1, 0xd8,
-	0x4e, 0x13, 0x7e, 0x68, 0x19, 0x3e, 0x42, 0x38, 0x50, 0x74, 0x0b, 0x46, 0x7d, 0xa7, 0x4d, 0xbd,
-	0xae, 0x9f, 0xcf, 0x39, 0x0d, 0xef, 0xb8, 0x48, 0x4b, 0x78, 0x53, 0x41, 0xe0, 0x00, 0x0b, 0x5d,
-	0x86, 0x29, 0x2a, 0x66, 0xe4, 0x66, 0x53, 0x58, 0x76, 0xaf, 0xa5, 0x3c, 0xb1, 0xc9, 0x28, 0xd1,
-	0x77, 0x25, 0x46, 0xc5, 0x89, 0xda, 0xf6, 0x8f, 0x4d, 0x37, 0xfa, 0x7f, 0xff, 0x33, 0x14, 0x9d,
-	0x63, 0x7a, 0xac, 0xef, 0x4f, 0x06, 0xce, 0x31, 0x1d, 0xfa, 0xf0, 0xe4, 0x3d, 0x78, 0x2a, 0xdd,
-	0x14, 0x1c, 0xc9, 0xc3, 0xcd, 0x1f, 0x25, 0x65, 0x25, 0x3d, 0xb0, 0x40, 0xfd, 0xac, 0xe3, 0xf4,
-	0x98, 0x0a, 0x47, 0xed, 0x31, 0x31, 0x73, 0x28, 0xfa, 0x99, 0x2b, 0x7a, 0x5f, 0xaf, 0x33, 0x2b,
-	0xcf, 0xc3, 0xc9, 0x1e, 0x98, 0xbe, 0x6b, 0xed, 0xef, 0x2d, 0x98, 0x4f, 0xad, 0x1d, 0xca, 0xb0,
-	0x70, 0x9c, 0x32, 0xb4, 0x8e, 0x5a, 0x86, 0xdf, 0x2f, 0xc0, 0x0c, 0xa6, 0x1d, 0x2f, 0x76, 0x00,
-	0xb5, 0x19, 0x3c, 0x10, 0xca, 0x11, 0x56, 0x24, 0x6e, 0x75, 0x54, 0x46, 0x63, 0x2f, 0x83, 0x84,
-	0xba, 0xb4, 0x03, 0x1f, 0x32, 0xb3, 0xfa, 0xf7, 0x1c, 0x8d, 0xa9, 0x9d, 0x43, 0x1d, 0xb2, 0x29,
-	0x40, 0x81, 0x2c, 0x6f, 0x62, 0x6a, 0xe3, 0xfe, 0x46, 0x8e, 0x3b, 0x9d, 0xbd, 0xc8, 0xb2, 0x18,
-	0x2b, 0x40, 0xfb, 0x93, 0x02, 0xa8, 0x10, 0xe4, 0x31, 0x58, 0xc7, 0x5f, 0x88, 0x59, 0xc7, 0xe5,
-	0x3c, 0x29, 0xb2, 0x7e, 0xa9, 0x98, 0x64, 0x78, 0x78, 0x26, 0x67, 0xde, 0xed, 0x21, 0x69, 0x98,
-	0xbf, 0xb6, 0x60, 0x5c, 0xd6, 0x7b, 0x0c, 0x86, 0x76, 0x33, 0x6e, 0x68, 0x5f, 0xca, 0x31, 0x8a,
-	0x3e, 0x06, 0xf6, 0x3f, 0x8a, 0xba, 0xf7, 0x61, 0xf0, 0xd9, 0x24, 0xac, 0xae, 0xa3, 0xaa, 0x48,
-	0x4b, 0x44, 0x21, 0x56, 0xb4, 0x50, 0xb7, 0x47, 0x8f, 0x41, 0xb7, 0x7f, 0x49, 0x5d, 0x88, 0xa5,
-	0xdc, 0xa7, 0xf5, 0xab, 0x61, 0xf8, 0x54, 0xcc, 0x7d, 0xb3, 0x57, 0xdf, 0x3e, 0x8e, 0x32, 0xda,
-	0x38, 0x81, 0x8a, 0x7b, 0xf8, 0x88, 0x90, 0xaa, 0x93, 0x34, 0x66, 0x3a, 0xd4, 0x78, 0x63, 0x40,
-	0xcb, 0xa9, 0x42, 0xaa, 0x9e, 0x62, 0xdc, 0xcb, 0x08, 0x35, 0x61, 0xc2, 0x7c, 0x93, 0xa0, 0xd7,
-	0xe9, 0xd9, 0xfc, 0x8f, 0x1f, 0xd4, 0xa5, 0x1f, 0xb3, 0x04, 0xc7, 0x90, 0xed, 0xdf, 0xb3, 0x00,
-	0xa2, 0x84, 0xb2, 0x98, 0xf3, 0x9a, 0xd7, 0x75, 0x55, 0x26, 0xa1, 0x18, 0xcd, 0xf9, 0xaa, 0x28,
-	0xc4, 0x8a, 0x26, 0xf4, 0x47, 0xc5, 0x63, 0x7a, 0x51, 0x9f, 0xc9, 0x13, 0xea, 0x25, 0x12, 0xd7,
-	0xaa, 0x10, 0x6b, 0x40, 0xfb, 0xa3, 0x11, 0x28, 0x19, 0x7a, 0x96, 0x48, 0x5b, 0x4f, 0x1e, 0x4f,
-	0xda, 0x3a, 0x3d, 0x97, 0x50, 0x1a, 0x28, 0x97, 0xc0, 0x61, 0x4a, 0x47, 0xc8, 0xc1, 0xc3, 0x15,
-	0x95, 0x6b, 0x19, 0x38, 0x0e, 0x47, 0xc2, 0x6b, 0xbd, 0x1a, 0x83, 0xc4, 0x09, 0x16, 0xc2, 0xeb,
-	0xd5, 0x25, 0xd5, 0x6e, 0xbb, 0x4d, 0xd8, 0xde, 0xc2, 0x84, 0xec, 0x7c, 0xe8, 0xf5, 0x5e, 0x8d,
-	0x51, 0x71, 0xa2, 0x36, 0xda, 0x0c, 0x27, 0x54, 0x3d, 0x86, 0x78, 0x39, 0xcf, 0x84, 0x2a, 0xaf,
-	0x3f, 0x3e, 0x8f, 0x42, 0xa4, 0xde, 0x96, 0x0c, 0x1a, 0xea, 0xd7, 0xd4, 0x67, 0x5f, 0xc4, 0x32,
-	0x1e, 0x91, 0x8b, 0x2a, 0x14, 0xe9, 0x8d, 0x9e, 0x1a, 0x38, 0xa5, 0x95, 0x30, 0x03, 0x3a, 0xd4,
-	0x0e, 0x75, 0x47, 0x27, 0x37, 0xf2, 0xc6, 0x59, 0x51, 0xec, 0x28, 0x6f, 0xc8, 0xaf, 0x26, 0x50,
-	0x71, 0x0f, 0x1f, 0xf4, 0x01, 0x4c, 0x8a, 0x49, 0x8e, 0x18, 0xc3, 0x23, 0x32, 0xd6, 0x49, 0x55,
-	0x03, 0x12, 0xc7, 0x39, 0xd8, 0x5f, 0x14, 0x21, 0x3d, 0xd0, 0x8f, 0x1e, 0xe7, 0x59, 0x0f, 0x79,
-	0x9c, 0x77, 0x07, 0xc6, 0xb9, 0x4f, 0x98, 0x7a, 0x25, 0x59, 0x18, 0xec, 0x95, 0x64, 0x35, 0x00,
-	0xc0, 0x11, 0x56, 0x22, 0xeb, 0x52, 0x3c, 0xd2, 0xac, 0xcb, 0x59, 0x00, 0x19, 0x88, 0x49, 0x33,
-	0x23, 0xf7, 0x9b, 0xc9, 0x48, 0x6b, 0xaf, 0x84, 0x14, 0x6c, 0xd4, 0x42, 0xdf, 0x08, 0x77, 0x71,
-	0x75, 0x69, 0xe5, 0x67, 0x7a, 0xee, 0x18, 0x9e, 0x8c, 0xb9, 0x79, 0x89, 0x44, 0x6e, 0x8e, 0xcb,
-	0xd0, 0x29, 0x09, 0x82, 0xd1, 0x7c, 0x09, 0x02, 0xfb, 0xbf, 0x0a, 0x10, 0xb3, 0xc2, 0xe8, 0xbb,
-	0x16, 0xcc, 0x92, 0xc4, 0x87, 0x68, 0x02, 0x27, 0xf6, 0xe7, 0xf3, 0x7d, 0x1d, 0xa8, 0xe7, 0x3b,
-	0x36, 0xd1, 0x21, 0x79, 0xb2, 0x0a, 0xc7, 0xbd, 0x4c, 0xd1, 0x6f, 0x59, 0x70, 0x92, 0xf4, 0x7e,
-	0x69, 0x48, 0x2f, 0x9e, 0x0b, 0x03, 0x7f, 0xaa, 0xa8, 0xf2, 0xf4, 0xc1, 0xfe, 0x52, 0xda, 0x37,
-	0x98, 0x70, 0x1a, 0x3b, 0xf4, 0x2e, 0x0c, 0x11, 0xd6, 0x08, 0xd2, 0xc7, 0xf9, 0xd9, 0x06, 0x1f,
-	0x90, 0x8a, 0x5c, 0x89, 0x15, 0xd6, 0xe0, 0x58, 0x82, 0xda, 0x3f, 0x29, 0xc2, 0x4c, 0xf2, 0x51,
-	0xa0, 0xbe, 0x72, 0x3f, 0x94, 0x7a, 0xe5, 0x5e, 0xe8, 0x9a, 0x3c, 0x40, 0x49, 0x3e, 0x84, 0x95,
-	0xe7, 0x20, 0x8a, 0x16, 0xea, 0x9a, 0x7c, 0xaa, 0x33, 0xfc, 0x08, 0xba, 0x26, 0xdf, 0xe7, 0x44,
-	0x58, 0xe8, 0x7c, 0x3c, 0x23, 0x6d, 0x27, 0x33, 0xd2, 0xb3, 0xe6, 0x58, 0x06, 0x4d, 0x4a, 0xb7,
-	0xa1, 0x64, 0xcc, 0x83, 0xd6, 0xe8, 0x8b, 0xb9, 0xe5, 0x1e, 0x2d, 0xbb, 0x69, 0x75, 0xef, 0x31,
-	0xa2, 0x98, 0xf8, 0x91, 0xfd, 0x90, 0xd2, 0x7a, 0xa4, 0xac, 0xad, 0x14, 0x97, 0x81, 0x66, 0xff,
-	0x93, 0x05, 0x93, 0xb1, 0x77, 0x20, 0x82, 0x5b, 0xf0, 0xde, 0x66, 0xf0, 0xaf, 0x2e, 0xdd, 0x0e,
-	0x11, 0xb0, 0x81, 0x86, 0xbe, 0x03, 0xa5, 0x96, 0xe7, 0x36, 0x28, 0xf7, 0xab, 0x1e, 0xd9, 0xd1,
-	0x7a, 0x92, 0x37, 0x7f, 0xb5, 0x70, 0xb0, 0xbf, 0x34, 0xb7, 0xa1, 0x60, 0x56, 0xbd, 0x76, 0xa7,
-	0x45, 0x7d, 0xf5, 0x32, 0x0b, 0x9b, 0xe0, 0xf2, 0xf4, 0x3b, 0xbc, 0x3e, 0xf0, 0xa4, 0x9e, 0x7e,
-	0x47, 0xf7, 0x1e, 0x8e, 0xf8, 0xf4, 0x3b, 0x76, 0xa1, 0xe2, 0x90, 0xd3, 0xef, 0xb0, 0xee, 0x13,
-	0x7b, 0xfa, 0x1d, 0xf6, 0xb0, 0x4f, 0xf8, 0xf5, 0x9f, 0x05, 0x63, 0x14, 0xf1, 0x10, 0xac, 0xf0,
-	0x90, 0x10, 0xec, 0x3d, 0x18, 0x73, 0x5c, 0x9f, 0xb2, 0x5d, 0xd2, 0xd2, 0xf9, 0xe7, 0xbc, 0x6b,
-	0x31, 0x1c, 0xea, 0xba, 0xc6, 0xc1, 0x21, 0x22, 0x6a, 0xc1, 0x7c, 0x70, 0xe4, 0xc3, 0x28, 0x89,
-	0x0e, 0xa5, 0xf5, 0x95, 0xd8, 0xd7, 0x83, 0xb3, 0x89, 0xab, 0x69, 0x95, 0x1e, 0xf4, 0x23, 0xe0,
-	0x74, 0x50, 0xc4, 0x61, 0x92, 0x1b, 0xb9, 0x87, 0x60, 0x47, 0xcc, 0x78, 0x5c, 0x96, 0x4c, 0xd7,
-	0x18, 0xf7, 0x68, 0x4d, 0x50, 0x1c, 0xe7, 0x61, 0x7f, 0x6c, 0xc1, 0x54, 0xfc, 0xea, 0xce, 0xff,
-	0x78, 0x1c, 0xf4, 0x45, 0x11, 0xa6, 0x13, 0x8b, 0x3f, 0x11, 0x0b, 0x8d, 0x3f, 0xce, 0x58, 0x68,
-	0x64, 0xa0, 0x58, 0x28, 0x3d, 0x08, 0x18, 0x1a, 0x28, 0x08, 0xb8, 0xa4, 0x1c, 0x71, 0xbd, 0x98,
-	0xd6, 0xd7, 0xf4, 0x63, 0xb3, 0x70, 0x82, 0x37, 0x4c, 0x22, 0x8e, 0xd7, 0x95, 0x1e, 0x4e, 0xbd,
-	0xf7, 0xa3, 0x42, 0x3a, 0x8a, 0xb8, 0x90, 0xf7, 0x2a, 0x7b, 0x08, 0xa0, 0x3c, 0x9c, 0x14, 0x02,
-	0x4e, 0x63, 0x67, 0xfb, 0x30, 0x9d, 0x7c, 0x50, 0x96, 0x29, 0x71, 0xdd, 0x21, 0x7e, 0xf0, 0xc0,
-	0x2a, 0xac, 0xb1, 0x49, 0xfc, 0x26, 0x96, 0x14, 0xf4, 0x0c, 0x14, 0xbb, 0xac, 0x95, 0x7c, 0xf5,
-	0x77, 0x0b, 0x6f, 0x60, 0x51, 0x6e, 0xff, 0xa1, 0x05, 0xf3, 0xa9, 0xb7, 0x19, 0x33, 0x30, 0xbf,
-	0x07, 0x23, 0x4a, 0x36, 0x7a, 0x3f, 0xb8, 0x94, 0x39, 0xe3, 0xd9, 0xfb, 0x78, 0x4e, 0xc5, 0x89,
-	0x8a, 0x84, 0x35, 0x6c, 0xe5, 0xad, 0xcf, 0xbe, 0x3a, 0x75, 0xe2, 0xf3, 0xaf, 0x4e, 0x9d, 0xf8,
-	0xf2, 0xab, 0x53, 0x27, 0x3e, 0x3a, 0x38, 0x65, 0x7d, 0x76, 0x70, 0xca, 0xfa, 0xfc, 0xe0, 0x94,
-	0xf5, 0xe5, 0xc1, 0x29, 0xeb, 0x5f, 0x0f, 0x4e, 0x59, 0x1f, 0xff, 0xf4, 0xd4, 0x89, 0xbb, 0xcf,
-	0x65, 0xf9, 0x82, 0xeb, 0x7f, 0x07, 0x00, 0x00, 0xff, 0xff, 0xe0, 0x89, 0x79, 0xdf, 0xe8, 0x55,
-	0x00, 0x00,
+	// 4614 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x5c, 0x4b, 0x6c, 0x1c, 0x47,
+	0x7a, 0x56, 0xcf, 0xf0, 0xf9, 0x0f, 0x9f, 0x25, 0xd2, 0xe6, 0x72, 0x63, 0x51, 0xe9, 0x75, 0x0c,
+	0x3b, 0xb6, 0x87, 0x91, 0x6c, 0xd9, 0x7a, 0x78, 0x15, 0x70, 0x86, 0x7a, 0xd0, 0xcb, 0xb5, 0x98,
+	0x1a, 0x4a, 0x5a, 0xcb, 0x36, 0x94, 0xe2, 0x4c, 0x71, 0xa6, 0x97, 0x33, 0xdd, 0xe3, 0xaa, 0x1e,
+	0x5a, 0xcc, 0x06, 0x89, 0xf3, 0xc4, 0x02, 0x09, 0x02, 0x1f, 0x36, 0xf0, 0x22, 0x48, 0x90, 0x20,
+	0x7b, 0x0a, 0x16, 0x48, 0x8e, 0x39, 0xe4, 0xe0, 0x43, 0x2e, 0xde, 0x64, 0x37, 0x30, 0x9c, 0x43,
+	0x1c, 0x60, 0x41, 0xc4, 0x5c, 0x20, 0x40, 0x6e, 0xb9, 0xe4, 0x22, 0x20, 0x40, 0x50, 0x8f, 0xee,
+	0xae, 0xee, 0xe9, 0x11, 0xa7, 0x87, 0xa4, 0xa2, 0xe4, 0x36, 0x53, 0x7f, 0xd5, 0xf7, 0x57, 0xfd,
+	0x55, 0xf5, 0xd7, 0xff, 0xa8, 0x6a, 0x78, 0xb5, 0xee, 0xf8, 0x8d, 0xce, 0x56, 0xb1, 0xea, 0xb5,
+	0x96, 0xc9, 0x4e, 0xc7, 0xf1, 0xf7, 0x96, 0x77, 0x08, 0xab, 0x7b, 0xcb, 0xa4, 0xed, 0x2c, 0xef,
+	0x9e, 0x23, 0xcd, 0x76, 0x83, 0x9c, 0x5b, 0xae, 0x53, 0x97, 0x32, 0xe2, 0xd3, 0x5a, 0xb1, 0xcd,
+	0x3c, 0xdf, 0x43, 0xcf, 0x46, 0xad, 0x8a, 0xaa, 0x55, 0x51, 0xb6, 0x2a, 0x92, 0xb6, 0x53, 0x0c,
+	0x5a, 0x2d, 0xbe, 0x6c, 0x60, 0xd7, 0xbd, 0xba, 0xb7, 0x2c, 0x1b, 0x6f, 0x75, 0xb6, 0xe5, 0x3f,
+	0xf9, 0x47, 0xfe, 0x52, 0xa0, 0x8b, 0xf6, 0xce, 0x45, 0x5e, 0x74, 0x14, 0xe7, 0xaa, 0xc7, 0xe8,
+	0xf2, 0x6e, 0x17, 0xe3, 0xc5, 0x9b, 0x51, 0x1d, 0xfa, 0xc0, 0xa7, 0x2e, 0x77, 0x3c, 0x97, 0xbf,
+	0x4c, 0xda, 0x0e, 0xa7, 0x6c, 0x97, 0xb2, 0xe5, 0xf6, 0x4e, 0x5d, 0xd0, 0x78, 0xbc, 0x42, 0x1a,
+	0xd2, 0xab, 0x11, 0x52, 0x8b, 0x54, 0x1b, 0x8e, 0x4b, 0xd9, 0x5e, 0xd4, 0xbc, 0x45, 0x7d, 0x92,
+	0xd6, 0x6a, 0xb9, 0x57, 0x2b, 0xd6, 0x71, 0x7d, 0xa7, 0x45, 0xbb, 0x1a, 0xbc, 0x76, 0x58, 0x03,
+	0x5e, 0x6d, 0xd0, 0x16, 0x49, 0xb6, 0xb3, 0xdf, 0x85, 0xd3, 0x2b, 0x2e, 0x69, 0xee, 0x71, 0x87,
+	0xe3, 0x8e, 0xbb, 0xc2, 0xea, 0x9d, 0x16, 0x75, 0x7d, 0x74, 0x16, 0x86, 0x5c, 0xd2, 0xa2, 0x0b,
+	0xd6, 0x59, 0xeb, 0xf9, 0xf1, 0xd2, 0xc4, 0xa7, 0xfb, 0x4b, 0xa7, 0x0e, 0xf6, 0x97, 0x86, 0xde,
+	0x22, 0x2d, 0x8a, 0x25, 0x05, 0x7d, 0x0d, 0x86, 0x77, 0x49, 0xb3, 0x43, 0x17, 0x72, 0xb2, 0xca,
+	0xa4, 0xae, 0x32, 0x7c, 0x47, 0x14, 0x62, 0x45, 0xb3, 0x7f, 0x27, 0x1f, 0x83, 0xff, 0x26, 0xf5,
+	0x49, 0x8d, 0xf8, 0x04, 0xb5, 0x60, 0xa4, 0x49, 0xb6, 0x68, 0x93, 0x2f, 0x58, 0x67, 0xf3, 0xcf,
+	0x17, 0xce, 0x5f, 0x2b, 0xf6, 0x33, 0xd1, 0xc5, 0x14, 0xa8, 0xe2, 0xba, 0xc4, 0xb9, 0xe6, 0xfa,
+	0x6c, 0xaf, 0x34, 0xa5, 0x3b, 0x31, 0xa2, 0x0a, 0xb1, 0x66, 0x82, 0x7e, 0xcb, 0x82, 0x02, 0x71,
+	0x5d, 0xcf, 0x27, 0xbe, 0x98, 0xa6, 0x85, 0x9c, 0x64, 0xfa, 0xe6, 0xe0, 0x4c, 0x57, 0x22, 0x30,
+	0xc5, 0xf9, 0xb4, 0xe6, 0x5c, 0x30, 0x28, 0xd8, 0xe4, 0xb9, 0x78, 0x09, 0x0a, 0x46, 0x57, 0xd1,
+	0x0c, 0xe4, 0x77, 0xe8, 0x9e, 0x92, 0x2f, 0x16, 0x3f, 0xd1, 0x5c, 0x4c, 0xa0, 0x5a, 0x82, 0x97,
+	0x73, 0x17, 0xad, 0xc5, 0xab, 0x30, 0x93, 0x64, 0x98, 0xa5, 0xbd, 0xfd, 0x47, 0x16, 0xcc, 0x19,
+	0xa3, 0xc0, 0x74, 0x9b, 0x32, 0xea, 0x56, 0x29, 0x5a, 0x86, 0x71, 0x31, 0x97, 0xbc, 0x4d, 0xaa,
+	0xc1, 0x54, 0xcf, 0xea, 0x81, 0x8c, 0xbf, 0x15, 0x10, 0x70, 0x54, 0x27, 0x5c, 0x16, 0xb9, 0x47,
+	0x2d, 0x8b, 0x76, 0x83, 0x70, 0xba, 0x90, 0x8f, 0x2f, 0x8b, 0x0d, 0x51, 0x88, 0x15, 0xcd, 0xbe,
+	0x0f, 0x5f, 0x09, 0xfa, 0xb3, 0x49, 0x5b, 0xed, 0x26, 0xf1, 0x69, 0xd4, 0xa9, 0xc3, 0x97, 0xde,
+	0x59, 0x18, 0xda, 0x71, 0xdc, 0x5a, 0xb2, 0x17, 0xdf, 0x70, 0xdc, 0x1a, 0x96, 0x14, 0x7b, 0x07,
+	0x26, 0x57, 0xda, 0x6d, 0xe6, 0xed, 0xd2, 0x5a, 0xc5, 0x27, 0x75, 0x8a, 0xee, 0x01, 0x10, 0x5d,
+	0xb0, 0xe2, 0x4b, 0xe8, 0xc2, 0xf9, 0x5f, 0x2c, 0xaa, 0x3d, 0x53, 0x34, 0xf7, 0x4c, 0xb1, 0xbd,
+	0x53, 0x17, 0x05, 0xbc, 0x28, 0xb6, 0x66, 0x71, 0xf7, 0x5c, 0x71, 0xd3, 0x69, 0xd1, 0xd2, 0xd4,
+	0xc1, 0xfe, 0x12, 0xac, 0x84, 0x08, 0xd8, 0x40, 0xb3, 0x7f, 0xdb, 0x82, 0xf9, 0x15, 0x56, 0xf7,
+	0xca, 0xab, 0x2b, 0xed, 0xf6, 0x4d, 0x4a, 0x9a, 0x7e, 0xa3, 0xe2, 0x13, 0xbf, 0xc3, 0xd1, 0x55,
+	0x18, 0xe1, 0xf2, 0x97, 0x1e, 0xcc, 0x73, 0xc1, 0xfa, 0x54, 0xf4, 0x87, 0xfb, 0x4b, 0x73, 0x29,
+	0x0d, 0x29, 0xd6, 0xad, 0xd0, 0x0b, 0x30, 0xda, 0xa2, 0x9c, 0x93, 0x7a, 0x20, 0xf1, 0x69, 0x0d,
+	0x30, 0xfa, 0x4d, 0x55, 0x8c, 0x03, 0xba, 0xfd, 0x0f, 0x39, 0x98, 0x0e, 0xb1, 0x34, 0xfb, 0x13,
+	0x98, 0xde, 0x0e, 0x4c, 0x34, 0x8c, 0x11, 0xca, 0x59, 0x2e, 0x9c, 0xbf, 0xd2, 0xe7, 0x4e, 0x4a,
+	0x13, 0x52, 0x69, 0x4e, 0xb3, 0x99, 0x30, 0x4b, 0x71, 0x8c, 0x0d, 0x6a, 0x01, 0xf0, 0x3d, 0xb7,
+	0xaa, 0x99, 0x0e, 0x49, 0xa6, 0x97, 0x32, 0x32, 0xad, 0x84, 0x00, 0x25, 0xa4, 0x59, 0x42, 0x54,
+	0x86, 0x0d, 0x06, 0xf6, 0x5f, 0x5b, 0x70, 0x3a, 0xa5, 0x1d, 0x7a, 0x23, 0x31, 0x9f, 0xcf, 0x76,
+	0xcd, 0x27, 0xea, 0x6a, 0x16, 0xcd, 0xe6, 0x4b, 0x30, 0xc6, 0xe8, 0xae, 0x23, 0x4e, 0x0a, 0x2d,
+	0xe1, 0x19, 0xdd, 0x7e, 0x0c, 0xeb, 0x72, 0x1c, 0xd6, 0x40, 0x2f, 0xc2, 0x78, 0xf0, 0x5b, 0x88,
+	0x39, 0x2f, 0x36, 0x93, 0x98, 0xb8, 0xa0, 0x2a, 0xc7, 0x11, 0xdd, 0xfe, 0x4d, 0x18, 0x2e, 0x37,
+	0x08, 0xf3, 0xc5, 0x8a, 0x61, 0xb4, 0xed, 0xdd, 0xc6, 0xeb, 0xba, 0x8b, 0xe1, 0x8a, 0xc1, 0xaa,
+	0x18, 0x07, 0xf4, 0x3e, 0x26, 0xfb, 0x05, 0x18, 0xdd, 0xa5, 0x4c, 0xf6, 0x37, 0x1f, 0x07, 0xbb,
+	0xa3, 0x8a, 0x71, 0x40, 0xb7, 0xff, 0xd9, 0x82, 0x39, 0xd9, 0x83, 0x55, 0x87, 0x57, 0xbd, 0x5d,
+	0xca, 0xf6, 0x30, 0xe5, 0x9d, 0xe6, 0x31, 0x77, 0x68, 0x15, 0x66, 0x38, 0x6d, 0xed, 0x52, 0x56,
+	0xf6, 0x5c, 0xee, 0x33, 0xe2, 0xb8, 0xbe, 0xee, 0xd9, 0x82, 0xae, 0x3d, 0x53, 0x49, 0xd0, 0x71,
+	0x57, 0x0b, 0xf4, 0x3c, 0x8c, 0xe9, 0x6e, 0x8b, 0xa5, 0x24, 0x04, 0x3b, 0x21, 0xe6, 0x40, 0x8f,
+	0x89, 0xe3, 0x90, 0x6a, 0xff, 0xbb, 0x05, 0xb3, 0x72, 0x54, 0x95, 0xce, 0x16, 0xaf, 0x32, 0xa7,
+	0x2d, 0x14, 0xf0, 0x93, 0x38, 0xa4, 0xab, 0x30, 0x55, 0x0b, 0x04, 0xbf, 0xee, 0xb4, 0x1c, 0x5f,
+	0xee, 0x91, 0xe1, 0xd2, 0x53, 0x1a, 0x63, 0x6a, 0x35, 0x46, 0xc5, 0x89, 0xda, 0x6a, 0xfa, 0x9a,
+	0x1d, 0xee, 0x53, 0xb6, 0xc1, 0xbc, 0x96, 0x27, 0xc6, 0xb9, 0x49, 0xf8, 0x0e, 0xfa, 0x55, 0x18,
+	0x6b, 0xe9, 0x43, 0x4f, 0x6b, 0xcd, 0x5f, 0xea, 0x4f, 0x6b, 0xde, 0xda, 0xfa, 0x36, 0xad, 0xfa,
+	0xe2, 0xc0, 0x8c, 0x76, 0x5b, 0x54, 0x86, 0x43, 0x54, 0xf4, 0x36, 0x0c, 0xf1, 0x36, 0xad, 0x4a,
+	0x11, 0x15, 0xce, 0xbf, 0xde, 0xdf, 0xa6, 0x8e, 0x75, 0xb2, 0xd2, 0xa6, 0xd5, 0x48, 0xb6, 0xe2,
+	0x1f, 0x96, 0x90, 0xf6, 0xbf, 0x5a, 0xb0, 0x90, 0x36, 0xaa, 0x75, 0x87, 0xfb, 0xe8, 0xdd, 0xae,
+	0x91, 0x15, 0xfb, 0x1b, 0x99, 0x68, 0x2d, 0xc7, 0x15, 0xee, 0xde, 0xa0, 0xc4, 0x18, 0xd5, 0x7d,
+	0x18, 0x76, 0x7c, 0xda, 0x0a, 0x4c, 0x8d, 0xcb, 0xfd, 0x0d, 0x2b, 0xad, 0xb3, 0xd1, 0x11, 0xba,
+	0x26, 0x00, 0xb1, 0xc2, 0xb5, 0xdf, 0x81, 0x89, 0x72, 0x87, 0x31, 0xea, 0xfa, 0xea, 0x80, 0xfb,
+	0x06, 0x0c, 0x73, 0xc7, 0xd5, 0x7a, 0x3e, 0xdb, 0xd9, 0x36, 0x2e, 0xc0, 0x2b, 0xa2, 0x31, 0x56,
+	0x18, 0xf6, 0x9f, 0xe6, 0xe1, 0x74, 0xb0, 0x62, 0x68, 0x6d, 0x85, 0xf9, 0xce, 0x36, 0xa9, 0xfa,
+	0x1c, 0xd5, 0x60, 0xa2, 0x16, 0x15, 0xfb, 0x5a, 0x11, 0x67, 0xe1, 0x15, 0x2a, 0x7b, 0x03, 0xde,
+	0xc7, 0x31, 0x54, 0x74, 0x17, 0xf2, 0x75, 0xc7, 0xd7, 0x96, 0xe1, 0xc5, 0xfe, 0x24, 0x77, 0xc3,
+	0x49, 0x6a, 0x9e, 0x52, 0x41, 0xb3, 0xca, 0xdf, 0x70, 0x7c, 0x2c, 0x10, 0xd1, 0x16, 0x8c, 0x38,
+	0x2d, 0x52, 0xa7, 0x19, 0x67, 0x65, 0x4d, 0xb4, 0x49, 0xa2, 0x87, 0xa6, 0xa6, 0xa4, 0x72, 0xac,
+	0x91, 0x05, 0x8f, 0xaa, 0xd0, 0x18, 0x4a, 0x67, 0xf7, 0x3f, 0xf3, 0x29, 0xba, 0x33, 0xe2, 0x21,
+	0xa9, 0x1c, 0x6b, 0x64, 0xfb, 0x8b, 0x1c, 0xcc, 0x44, 0xf2, 0x2b, 0x7b, 0xad, 0x96, 0xe3, 0xa3,
+	0x45, 0xc8, 0x39, 0x35, 0xad, 0x90, 0x40, 0x37, 0xcc, 0xad, 0xad, 0xe2, 0x9c, 0x53, 0x43, 0xcf,
+	0xc1, 0xc8, 0x16, 0x23, 0x6e, 0xb5, 0xa1, 0x15, 0x51, 0x08, 0x5c, 0x92, 0xa5, 0x58, 0x53, 0xd1,
+	0x33, 0x90, 0xf7, 0x49, 0x5d, 0xeb, 0x9f, 0x50, 0x7e, 0x9b, 0xa4, 0x8e, 0x45, 0xb9, 0x50, 0x7c,
+	0xbc, 0x23, 0xf7, 0xb0, 0x9c, 0x79, 0x43, 0xf1, 0x55, 0x54, 0x31, 0x0e, 0xe8, 0x82, 0x23, 0xe9,
+	0xf8, 0x0d, 0x8f, 0x2d, 0x0c, 0xc7, 0x39, 0xae, 0xc8, 0x52, 0xac, 0xa9, 0xc2, 0x44, 0xa9, 0xca,
+	0xfe, 0xfb, 0x94, 0x2d, 0x8c, 0xc4, 0x4d, 0x94, 0x72, 0x40, 0xc0, 0x51, 0x1d, 0xf4, 0x1e, 0x14,
+	0xaa, 0x8c, 0x12, 0xdf, 0x63, 0xab, 0xc4, 0xa7, 0x0b, 0xa3, 0x99, 0x57, 0xe0, 0xb4, 0xb0, 0xd2,
+	0xcb, 0x11, 0x04, 0x36, 0xf1, 0xec, 0xbf, 0xcd, 0xc3, 0x42, 0x24, 0x5a, 0x39, 0xb7, 0x91, 0x65,
+	0xaa, 0xc5, 0x63, 0xf5, 0x10, 0xcf, 0x73, 0x30, 0x52, 0x73, 0xea, 0x94, 0xfb, 0x49, 0x29, 0xaf,
+	0xca, 0x52, 0xac, 0xa9, 0xe8, 0xf7, 0x13, 0xde, 0xc8, 0xb0, 0x5c, 0x28, 0xb7, 0xfa, 0x5b, 0x28,
+	0xbd, 0x3a, 0x37, 0x80, 0x4b, 0x82, 0xce, 0x03, 0xd4, 0x1d, 0x5f, 0x1f, 0x5a, 0x7a, 0xd6, 0x43,
+	0x65, 0x7d, 0x23, 0xa4, 0x60, 0xa3, 0x16, 0xba, 0x0b, 0xe3, 0x52, 0x5e, 0x03, 0xee, 0x7f, 0x69,
+	0xc2, 0x94, 0x03, 0x00, 0x1c, 0x61, 0x1d, 0xd9, 0xc9, 0x79, 0x07, 0xd0, 0xb5, 0x07, 0x6d, 0x46,
+	0xb9, 0x38, 0xba, 0xef, 0x10, 0xe6, 0x90, 0xad, 0x26, 0x3d, 0x2e, 0x3f, 0xf6, 0xb3, 0x21, 0x18,
+	0xbd, 0xce, 0xa8, 0x53, 0x6f, 0xf8, 0x8f, 0xe1, 0x48, 0xfc, 0x1a, 0x0c, 0x93, 0xa6, 0x43, 0xb8,
+	0x5c, 0xdd, 0x46, 0x97, 0x56, 0x44, 0x21, 0x56, 0x34, 0xf4, 0x0e, 0x8c, 0x78, 0xcc, 0xa9, 0x3b,
+	0xee, 0xc2, 0xb8, 0xec, 0xc4, 0x2b, 0xfd, 0xad, 0x1f, 0x3d, 0x8a, 0x5b, 0xb2, 0x69, 0xb4, 0x44,
+	0xd5, 0x7f, 0xac, 0x21, 0xd1, 0x3d, 0x18, 0x55, 0x5b, 0x2e, 0x50, 0x63, 0xcb, 0x7d, 0xab, 0x61,
+	0xb5, 0x6b, 0x23, 0xd5, 0xa0, 0xfe, 0x73, 0x1c, 0x00, 0xa2, 0x4a, 0xa8, 0x85, 0x87, 0x24, 0xf4,
+	0x8b, 0x19, 0xb4, 0x70, 0x4f, 0xb5, 0x5b, 0x09, 0xd5, 0xee, 0x70, 0x16, 0x50, 0xa9, 0x58, 0x7b,
+	0xe9, 0x59, 0x21, 0x62, 0x6d, 0xee, 0x8f, 0x0c, 0x20, 0x62, 0xed, 0x6b, 0x4c, 0xc5, 0x7d, 0x84,
+	0xc0, 0x1b, 0xb0, 0xbf, 0x97, 0x87, 0x59, 0x5d, 0xb3, 0xec, 0x35, 0x9b, 0xb4, 0x2a, 0x6d, 0x4b,
+	0xa5, 0xc5, 0xf3, 0xa9, 0x5a, 0xdc, 0x09, 0x6c, 0x0a, 0x75, 0x32, 0x96, 0x32, 0xf5, 0x26, 0xe2,
+	0x51, 0x94, 0x76, 0x84, 0xd2, 0x11, 0xe1, 0x2c, 0xe9, 0x5a, 0xda, 0xba, 0x40, 0xbf, 0x67, 0xc1,
+	0xe9, 0x5d, 0xca, 0x9c, 0x6d, 0xa7, 0x2a, 0xf7, 0xe3, 0x4d, 0x87, 0xfb, 0x1e, 0xdb, 0xd3, 0xe7,
+	0xe6, 0x6b, 0xfd, 0x71, 0xbe, 0x63, 0x00, 0xac, 0xb9, 0xdb, 0x5e, 0xe9, 0xab, 0x9a, 0xdb, 0xe9,
+	0x3b, 0xdd, 0xd0, 0x38, 0x8d, 0xdf, 0x62, 0x1b, 0x20, 0xea, 0x6d, 0x8a, 0x3a, 0x58, 0x37, 0x37,
+	0x6f, 0xdf, 0x1d, 0x0b, 0x06, 0x1b, 0xe8, 0x4e, 0x53, 0x8d, 0x7c, 0x62, 0x41, 0x41, 0xd3, 0x1f,
+	0x83, 0x99, 0x88, 0xe3, 0x66, 0xe2, 0xcb, 0x99, 0xfa, 0xdf, 0xc3, 0x32, 0x64, 0x30, 0x19, 0xdb,
+	0xe4, 0xe8, 0x82, 0x0e, 0x97, 0x28, 0x1d, 0xf8, 0xf3, 0x66, 0xb8, 0xe4, 0xe1, 0xfe, 0xd2, 0x6c,
+	0xac, 0x72, 0x14, 0x43, 0x39, 0xdc, 0x77, 0xb9, 0x3c, 0xf6, 0xfd, 0xbf, 0x58, 0x3a, 0xf5, 0xe1,
+	0x4f, 0xcf, 0x9e, 0xb2, 0x3f, 0xce, 0xc3, 0x4c, 0x52, 0xaa, 0x7d, 0xe8, 0xde, 0x48, 0x87, 0x8d,
+	0x9d, 0xa8, 0x0e, 0xcb, 0x9d, 0x9c, 0x0e, 0xcb, 0x9f, 0x84, 0x0e, 0x1b, 0x3a, 0x36, 0x1d, 0x66,
+	0xff, 0x93, 0x05, 0x53, 0xe1, 0xcc, 0xbc, 0xdf, 0x11, 0xf6, 0x47, 0x24, 0x75, 0xeb, 0xf8, 0xa5,
+	0x7e, 0x1f, 0x46, 0xb9, 0xd7, 0x61, 0x55, 0x69, 0x64, 0x0b, 0xf4, 0x57, 0xb3, 0x29, 0x4d, 0xd5,
+	0xd6, 0xb0, 0x2c, 0x55, 0x01, 0x0e, 0x50, 0xed, 0x4f, 0x72, 0xe1, 0x80, 0x34, 0x4d, 0x19, 0x5e,
+	0x4c, 0x98, 0xa5, 0x62, 0x40, 0x63, 0xa6, 0xe1, 0x25, 0x4a, 0xb1, 0xa6, 0x22, 0x5b, 0xea, 0xf3,
+	0xc0, 0xfe, 0x1f, 0x2f, 0x81, 0x56, 0xcb, 0x72, 0x12, 0x14, 0x05, 0xb5, 0x61, 0x86, 0xd1, 0xf7,
+	0x3b, 0x0e, 0xa3, 0xb5, 0x8a, 0x47, 0x76, 0x84, 0xd1, 0xa2, 0x83, 0x5c, 0x7d, 0xee, 0xfb, 0xd5,
+	0x0e, 0x93, 0x2a, 0xac, 0x34, 0x27, 0x7c, 0x77, 0x9c, 0xc0, 0xc2, 0x5d, 0xe8, 0xc8, 0x83, 0x39,
+	0xb2, 0x4b, 0x9c, 0x26, 0xd9, 0x72, 0x9a, 0x8e, 0xbf, 0x57, 0xf1, 0x19, 0xf1, 0x69, 0x7d, 0x4f,
+	0x9b, 0xd8, 0x57, 0xf4, 0x58, 0xe6, 0x56, 0x52, 0xea, 0x3c, 0xdc, 0x5f, 0xfa, 0xaa, 0x96, 0x45,
+	0x1a, 0x19, 0xa7, 0x02, 0xdb, 0x3f, 0x19, 0x0d, 0x35, 0x84, 0x8e, 0x6b, 0x7d, 0x07, 0x0a, 0x55,
+	0xe5, 0x4c, 0x36, 0xf7, 0xd6, 0x5c, 0xbd, 0xa6, 0x57, 0x07, 0x38, 0xed, 0x8a, 0xe5, 0x08, 0x26,
+	0x61, 0x85, 0x1a, 0x14, 0x6c, 0x72, 0x43, 0x1f, 0x00, 0x28, 0xd5, 0x4f, 0x6b, 0x6b, 0xae, 0x3e,
+	0xdb, 0xca, 0x83, 0xf0, 0xbe, 0x13, 0xa2, 0x28, 0xd6, 0xa1, 0x91, 0x15, 0x11, 0xb0, 0xc1, 0x4a,
+	0x8c, 0x3a, 0x88, 0xe2, 0x5e, 0xf7, 0x98, 0x56, 0x12, 0x03, 0x8d, 0x7a, 0x25, 0x82, 0x49, 0xda,
+	0xde, 0x11, 0x05, 0x9b, 0xdc, 0x90, 0x67, 0x9c, 0x2b, 0x6a, 0xbb, 0xaf, 0x0c, 0xc2, 0x39, 0xc8,
+	0x48, 0x28, 0xb6, 0xe1, 0x51, 0x13, 0x14, 0x47, 0x47, 0xcd, 0x22, 0x83, 0x99, 0xe4, 0xe4, 0xa4,
+	0x1c, 0xa8, 0x37, 0xe3, 0x07, 0xea, 0xf9, 0x3e, 0x55, 0x90, 0x11, 0x89, 0x30, 0x13, 0x17, 0x0c,
+	0xa6, 0x13, 0x93, 0x92, 0xc2, 0x72, 0x2d, 0xce, 0xf2, 0x95, 0x2c, 0xc6, 0x85, 0x0e, 0xef, 0x9b,
+	0x3c, 0x39, 0xcc, 0x24, 0xa7, 0xe3, 0xd8, 0x98, 0xc6, 0x72, 0x0a, 0x26, 0xd3, 0xef, 0xc0, 0x64,
+	0x6c, 0x26, 0x52, 0x38, 0x6e, 0xc6, 0x39, 0x5e, 0x35, 0xb4, 0x49, 0x94, 0x40, 0xbc, 0x1f, 0x66,
+	0x18, 0x23, 0xc5, 0x12, 0xab, 0x20, 0x34, 0xcc, 0x9b, 0x95, 0x5b, 0x6f, 0x99, 0x26, 0xcb, 0x9f,
+	0xe5, 0x60, 0x3c, 0x3c, 0xb4, 0xb2, 0x44, 0x27, 0x95, 0xb1, 0x99, 0x3b, 0x24, 0x64, 0x90, 0xef,
+	0x27, 0x64, 0x30, 0xd4, 0x3b, 0x64, 0x10, 0x64, 0x30, 0x46, 0x1e, 0x9d, 0xc1, 0x30, 0x42, 0x06,
+	0xa3, 0xfd, 0x87, 0x0c, 0xc6, 0x0e, 0x0f, 0x19, 0xd8, 0x7f, 0x69, 0x01, 0xea, 0x8e, 0x0f, 0x65,
+	0x11, 0x14, 0x49, 0x9a, 0x12, 0xaf, 0x65, 0x75, 0xd6, 0x0f, 0xb3, 0x28, 0x6c, 0x06, 0xf3, 0x37,
+	0x1c, 0xff, 0x66, 0x67, 0xeb, 0x2e, 0xdd, 0x6a, 0x78, 0xde, 0x0e, 0xa6, 0x55, 0xea, 0xec, 0x52,
+	0x86, 0xde, 0x86, 0x71, 0x4e, 0xab, 0x8c, 0x0a, 0xc3, 0x4a, 0x1f, 0xd8, 0xcf, 0x1b, 0x6b, 0xa7,
+	0x58, 0xf5, 0x18, 0x95, 0xf6, 0xa6, 0x57, 0x25, 0x4d, 0xe5, 0x4e, 0x86, 0x26, 0x58, 0x24, 0x98,
+	0x4a, 0x00, 0x81, 0x23, 0x34, 0xfb, 0x93, 0x61, 0x98, 0xbe, 0xe1, 0x0c, 0x1c, 0xdc, 0xf6, 0xe1,
+	0x69, 0xd5, 0xfb, 0x0a, 0xd5, 0xae, 0x45, 0x78, 0x76, 0xa9, 0x35, 0x75, 0x59, 0x37, 0x7d, 0xba,
+	0x9c, 0x5e, 0xed, 0x61, 0x6f, 0x12, 0xee, 0x05, 0xdd, 0xf7, 0xc2, 0xbc, 0x02, 0x93, 0xdc, 0x67,
+	0x4e, 0xd5, 0x57, 0xe1, 0x73, 0xbe, 0x50, 0x90, 0xb6, 0xc1, 0xbc, 0xae, 0x3e, 0x59, 0x31, 0x89,
+	0x38, 0x5e, 0x37, 0x35, 0x2a, 0x3f, 0x94, 0x39, 0x2a, 0xbf, 0x0c, 0xe3, 0xa4, 0xd9, 0xf4, 0x3e,
+	0xd8, 0x24, 0x75, 0xae, 0xe3, 0x60, 0xe1, 0x84, 0xac, 0x04, 0x04, 0x1c, 0xd5, 0x41, 0x45, 0x00,
+	0xa7, 0xee, 0x7a, 0x8c, 0xca, 0x16, 0x23, 0xd2, 0x48, 0x91, 0x99, 0xc7, 0xb5, 0xb0, 0x14, 0x1b,
+	0x35, 0x50, 0x05, 0xe6, 0x1d, 0x97, 0xd3, 0x6a, 0x87, 0xd1, 0xca, 0x8e, 0xd3, 0xde, 0x5c, 0xaf,
+	0x48, 0xb5, 0xb8, 0x27, 0x77, 0xd0, 0x58, 0xe9, 0x19, 0xcd, 0x6c, 0x7e, 0x2d, 0xad, 0x12, 0x4e,
+	0x6f, 0x8b, 0x5e, 0x85, 0x09, 0xc7, 0xad, 0x36, 0x3b, 0x35, 0xba, 0x41, 0xfc, 0x06, 0x5f, 0x18,
+	0x93, 0xdd, 0x98, 0x39, 0xd8, 0x5f, 0x9a, 0x58, 0x33, 0xca, 0x71, 0xac, 0x96, 0x68, 0x45, 0x1f,
+	0x18, 0xad, 0xc6, 0xa3, 0x56, 0xd7, 0x1e, 0x98, 0xad, 0xcc, 0x5a, 0x29, 0x79, 0x0b, 0xc8, 0x94,
+	0xb7, 0xf8, 0x61, 0x0e, 0x46, 0x54, 0xda, 0x10, 0x5d, 0x48, 0xe4, 0xe6, 0x9e, 0xe9, 0xca, 0xcd,
+	0x15, 0xd2, 0x52, 0xac, 0x36, 0x8c, 0x38, 0x9c, 0x77, 0xe2, 0x36, 0xe1, 0x9a, 0x2c, 0xc1, 0x9a,
+	0x22, 0x63, 0xba, 0x9e, 0xbb, 0xed, 0xd4, 0x75, 0xc0, 0xeb, 0x88, 0xba, 0x5b, 0xf1, 0x28, 0x4b,
+	0x44, 0xac, 0x91, 0x05, 0x0f, 0xaf, 0xe3, 0xb7, 0x3b, 0xbe, 0x5c, 0x28, 0xc7, 0xc4, 0xe3, 0x96,
+	0x44, 0xc4, 0x1a, 0xd9, 0xfe, 0xd8, 0x82, 0x69, 0x25, 0x83, 0x72, 0x83, 0x56, 0x77, 0x2a, 0x3e,
+	0x6d, 0x0b, 0x27, 0xad, 0xc3, 0x29, 0x4f, 0x3a, 0x69, 0xb7, 0x39, 0xe5, 0x58, 0x52, 0x8c, 0xd1,
+	0xe7, 0x4e, 0x6a, 0xf4, 0xf6, 0x45, 0x30, 0x26, 0x47, 0xe6, 0xbd, 0x55, 0xfa, 0x57, 0x9d, 0xa0,
+	0xf9, 0x48, 0x09, 0xa9, 0x5a, 0x7b, 0x38, 0xa0, 0xdb, 0x07, 0x39, 0x18, 0x96, 0x7e, 0x54, 0x16,
+	0xcd, 0x15, 0x0f, 0x7c, 0xe6, 0xfa, 0x0a, 0x7c, 0x1e, 0x12, 0x1b, 0x8f, 0x82, 0xbf, 0x43, 0x8f,
+	0x0c, 0xfe, 0xf2, 0xb4, 0xd8, 0xef, 0x1b, 0x19, 0xdc, 0xc7, 0x41, 0xee, 0x9e, 0x1c, 0x35, 0xb6,
+	0xfa, 0x33, 0x0b, 0xe6, 0xd2, 0xb2, 0x20, 0x59, 0x64, 0xfe, 0x12, 0x8c, 0xb5, 0x9b, 0xc4, 0xdf,
+	0xf6, 0x58, 0x2b, 0x99, 0xfd, 0xde, 0xd0, 0xe5, 0x38, 0xac, 0x81, 0x18, 0x00, 0x0b, 0x4e, 0xb1,
+	0xc0, 0xc9, 0xbe, 0x7a, 0xb4, 0x08, 0x79, 0x34, 0xc3, 0x61, 0x11, 0xc7, 0x06, 0x17, 0xfb, 0x0f,
+	0x86, 0x61, 0x56, 0x36, 0x19, 0xf4, 0x40, 0x1c, 0x64, 0x59, 0xb5, 0xe1, 0x29, 0xe9, 0xfe, 0x77,
+	0x9f, 0xa1, 0x6a, 0xa5, 0x5d, 0xd4, 0xed, 0x9f, 0x5a, 0x4b, 0xad, 0xf5, 0xb0, 0x27, 0x05, 0xf7,
+	0xc0, 0xed, 0x3e, 0x18, 0xe1, 0xff, 0xdf, 0xc1, 0x68, 0x2e, 0xb6, 0xd1, 0x43, 0x17, 0x5b, 0xcf,
+	0x63, 0x74, 0xec, 0x08, 0xc7, 0x68, 0xf7, 0xd1, 0x36, 0x9e, 0xe9, 0x68, 0xfb, 0xf3, 0x1c, 0x8c,
+	0x6e, 0x30, 0x4f, 0x66, 0xd3, 0x4e, 0x3e, 0xe5, 0x70, 0x7b, 0xc0, 0x2c, 0xbc, 0x80, 0x52, 0xba,
+	0x5c, 0x66, 0xe1, 0xc7, 0xe2, 0x19, 0x78, 0x23, 0x82, 0x9e, 0xcf, 0xe2, 0x67, 0x69, 0xe0, 0x43,
+	0x22, 0xe8, 0x7f, 0x93, 0x83, 0xc9, 0x58, 0x17, 0x9e, 0xe0, 0xdb, 0x0a, 0x09, 0x39, 0xa5, 0xdc,
+	0x56, 0x40, 0x24, 0x21, 0xab, 0x4b, 0x83, 0x80, 0x3f, 0x5a, 0x62, 0xff, 0x68, 0xc1, 0x6c, 0xac,
+	0xfe, 0x63, 0x08, 0x71, 0x7f, 0x2b, 0x1e, 0xe2, 0x7e, 0x65, 0x80, 0x51, 0xf5, 0x08, 0x74, 0x7f,
+	0x37, 0x97, 0x18, 0x8d, 0x10, 0x26, 0xfa, 0x0d, 0x98, 0x6d, 0x07, 0xf7, 0x27, 0x36, 0xbc, 0xa6,
+	0x53, 0x75, 0x68, 0x90, 0x31, 0xb9, 0x90, 0xf1, 0x72, 0x89, 0x6c, 0xbe, 0x57, 0xfa, 0x8a, 0xe6,
+	0x3e, 0xbb, 0x91, 0xc4, 0xc5, 0xdd, 0xac, 0x10, 0x87, 0x71, 0xa6, 0x7d, 0xb7, 0x60, 0xcc, 0x7d,
+	0x5e, 0x8f, 0x4b, 0x78, 0x7e, 0x7a, 0xec, 0xa1, 0x5e, 0x4d, 0x90, 0xe5, 0xfd, 0x2f, 0xfd, 0xd3,
+	0xfe, 0x0f, 0x0b, 0x4e, 0xa7, 0x2c, 0x04, 0x54, 0x05, 0xa8, 0x7a, 0x6e, 0xcd, 0x51, 0xc6, 0x86,
+	0xa5, 0xc3, 0xe0, 0x7d, 0x4d, 0x6e, 0x39, 0x68, 0x17, 0xed, 0x88, 0xb0, 0x88, 0x63, 0x03, 0x16,
+	0xb5, 0xba, 0x47, 0x7c, 0x61, 0xa0, 0x11, 0xf7, 0x37, 0xd6, 0x4f, 0x2c, 0x28, 0xe8, 0xb1, 0x3e,
+	0xb1, 0x19, 0x1a, 0xdd, 0xbf, 0x1e, 0x0b, 0xf7, 0x73, 0x0b, 0x26, 0x0c, 0x15, 0xc7, 0x51, 0x03,
+	0xe0, 0x03, 0xc2, 0x68, 0xc3, 0x0b, 0x4d, 0xf1, 0xbe, 0xe3, 0xe6, 0x77, 0x83, 0x76, 0x12, 0x29,
+	0x9a, 0xab, 0xb0, 0x9c, 0x63, 0x03, 0x1b, 0x7d, 0xcb, 0x08, 0x81, 0x2b, 0xfd, 0xd8, 0x17, 0x17,
+	0x19, 0xf0, 0x52, 0x1c, 0x4c, 0xdd, 0x62, 0x04, 0xce, 0xed, 0x1f, 0x59, 0xa1, 0x36, 0x4e, 0x5d,
+	0x7c, 0xf9, 0x93, 0x59, 0x7c, 0x15, 0x18, 0x16, 0xca, 0x2d, 0xb8, 0x14, 0x7a, 0x3e, 0xf3, 0x01,
+	0xc3, 0xf5, 0xfd, 0x27, 0xf1, 0x13, 0x2b, 0x2c, 0xfb, 0x07, 0x39, 0x18, 0x0f, 0x37, 0xfb, 0x63,
+	0x3f, 0x7d, 0x5f, 0xc9, 0xa8, 0xa6, 0x7a, 0x9e, 0x28, 0xef, 0x25, 0x4e, 0x94, 0xac, 0xfa, 0xef,
+	0x90, 0xd3, 0xe4, 0xef, 0xd5, 0x8c, 0xab, 0xba, 0x8f, 0x61, 0x2b, 0x6e, 0xc6, 0xb7, 0xe2, 0x72,
+	0xc6, 0xd1, 0xf4, 0xd8, 0x8c, 0x1f, 0xe6, 0x60, 0x3a, 0xa1, 0xf1, 0xd1, 0xd7, 0xe4, 0xa2, 0xaa,
+	0x07, 0xa9, 0xcb, 0xb0, 0xa1, 0x8e, 0xfb, 0x4a, 0x1a, 0xda, 0x15, 0x76, 0x74, 0x68, 0x61, 0x7b,
+	0x4c, 0x0b, 0xf9, 0xeb, 0x03, 0x1d, 0x32, 0x01, 0x48, 0x69, 0x56, 0x99, 0xe0, 0x06, 0x2e, 0x8e,
+	0xb3, 0x41, 0x1b, 0x30, 0x47, 0x3a, 0xbe, 0x17, 0x02, 0x5c, 0x73, 0xc9, 0x56, 0x93, 0xaa, 0x38,
+	0xee, 0x58, 0xe9, 0xe7, 0xc2, 0x7c, 0x51, 0x4a, 0x1d, 0x9c, 0xda, 0xd2, 0xfe, 0x2b, 0x0b, 0x9e,
+	0xee, 0xd1, 0x9f, 0x3e, 0x92, 0xb8, 0x4d, 0x98, 0x94, 0xcf, 0x2c, 0x42, 0x39, 0x04, 0xab, 0xb8,
+	0xbf, 0x99, 0x37, 0x9b, 0xaa, 0xd1, 0xc7, 0x8a, 0x70, 0x1c, 0xdc, 0xfe, 0x71, 0x0e, 0x50, 0xd8,
+	0xd7, 0x2c, 0xb9, 0xe6, 0xf7, 0x60, 0x74, 0x5b, 0xa5, 0x4e, 0x8e, 0x76, 0x59, 0xa0, 0x54, 0x30,
+	0xef, 0x4b, 0x04, 0x98, 0xe8, 0xed, 0xe3, 0xd9, 0x6b, 0xd0, 0xbd, 0xcf, 0xd0, 0x3d, 0x80, 0x6d,
+	0xc7, 0x75, 0x78, 0x63, 0xc0, 0x3b, 0x57, 0xd2, 0x51, 0xba, 0x1e, 0x22, 0x60, 0x03, 0xcd, 0xfe,
+	0xe3, 0x9c, 0xb1, 0x87, 0xa5, 0xfd, 0xd4, 0xd7, 0xda, 0x7f, 0x21, 0x2e, 0xcc, 0xf1, 0xee, 0x8b,
+	0x24, 0xa1, 0x60, 0xee, 0xc1, 0xd0, 0x2e, 0x61, 0x41, 0x4e, 0xbb, 0xcf, 0xeb, 0x9c, 0xdd, 0x37,
+	0xb9, 0xa2, 0x39, 0xbd, 0x43, 0x18, 0xc7, 0x12, 0x53, 0xd8, 0x96, 0xdc, 0xa7, 0xed, 0xe0, 0x70,
+	0xc9, 0xac, 0x38, 0x7d, 0xda, 0x36, 0x07, 0x48, 0xdb, 0xf2, 0x04, 0xa0, 0x6d, 0x6e, 0x7f, 0x6f,
+	0xd4, 0xd0, 0x0a, 0xfa, 0x3c, 0x7b, 0x13, 0x50, 0x93, 0x70, 0xff, 0x26, 0x71, 0x6b, 0x62, 0x2f,
+	0xd1, 0x6d, 0x46, 0x79, 0x43, 0x7b, 0xbf, 0x8b, 0x1a, 0x05, 0xad, 0x77, 0xd5, 0xc0, 0x29, 0xad,
+	0xd0, 0x85, 0xe0, 0x99, 0x8c, 0x92, 0xf2, 0x52, 0xec, 0x99, 0xcc, 0xc3, 0xfd, 0xa5, 0xa9, 0x68,
+	0x3f, 0x1a, 0x0f, 0x67, 0x32, 0x3c, 0x08, 0x31, 0xd7, 0xfb, 0xf0, 0x09, 0xac, 0xf7, 0x5f, 0x87,
+	0xd9, 0xed, 0xe4, 0xcd, 0x22, 0x7d, 0x1b, 0xf3, 0xf5, 0x01, 0x2f, 0x26, 0x95, 0xe6, 0x0f, 0xa2,
+	0xeb, 0x28, 0x51, 0x31, 0xee, 0x66, 0x84, 0xbc, 0xe0, 0x19, 0x8a, 0x0c, 0x64, 0xaa, 0x18, 0x75,
+	0xdf, 0x7b, 0x2e, 0x11, 0x02, 0x4d, 0x3e, 0x40, 0x51, 0x90, 0x38, 0xc6, 0x20, 0xb1, 0x07, 0x47,
+	0x8e, 0x73, 0x0f, 0xa2, 0x0b, 0x61, 0xf6, 0x5d, 0x74, 0x47, 0x86, 0x09, 0xf2, 0x5d, 0x79, 0x73,
+	0x41, 0xc2, 0x66, 0x3d, 0xf4, 0x91, 0x05, 0xf3, 0x62, 0xb1, 0x5e, 0x7b, 0x40, 0xab, 0x1d, 0x21,
+	0x95, 0x20, 0x03, 0xb9, 0x50, 0xc8, 0xe2, 0x75, 0x54, 0xd2, 0x20, 0xa2, 0x98, 0x47, 0x2a, 0x19,
+	0xa7, 0x33, 0x46, 0xf7, 0x95, 0x31, 0x46, 0x65, 0x48, 0xe9, 0xe8, 0x91, 0xe2, 0xd0, 0x30, 0x53,
+	0x7a, 0xc7, 0xa7, 0xf6, 0x0f, 0x86, 0x4c, 0x75, 0xd5, 0x5f, 0xfc, 0xfa, 0x1e, 0x0c, 0xf9, 0x84,
+	0xef, 0xe8, 0x5d, 0xf0, 0xc6, 0x00, 0x0f, 0x0c, 0xa2, 0xbd, 0x20, 0xe3, 0x1b, 0xb2, 0x48, 0x62,
+	0xa2, 0x45, 0xc8, 0x11, 0x9e, 0xcc, 0xa0, 0xae, 0x70, 0x9c, 0x23, 0x5c, 0x66, 0x57, 0xb7, 0x75,
+	0xf4, 0x29, 0xca, 0xae, 0x6e, 0xe3, 0x9c, 0xb3, 0x8d, 0x56, 0x60, 0xba, 0xea, 0xb9, 0xbe, 0xe3,
+	0x76, 0xe8, 0x2d, 0xf7, 0x1a, 0x63, 0x1e, 0xd3, 0xb1, 0xa6, 0xa7, 0x75, 0xc5, 0xe9, 0x72, 0x9c,
+	0x8c, 0x93, 0xf5, 0xd1, 0xdb, 0x30, 0xcc, 0xa8, 0xcf, 0xf6, 0xf4, 0x81, 0x70, 0x71, 0x00, 0xdd,
+	0x87, 0x45, 0x7b, 0x25, 0x65, 0xf9, 0x13, 0x2b, 0xc4, 0x50, 0x65, 0x8f, 0x9c, 0x80, 0xca, 0x8e,
+	0xb2, 0x09, 0xf9, 0x13, 0xcb, 0x26, 0xfc, 0xd0, 0x32, 0x6c, 0x84, 0x70, 0xa0, 0xe8, 0x36, 0x8c,
+	0xfa, 0x4e, 0x8b, 0x7a, 0x1d, 0x3f, 0x9b, 0x71, 0x1a, 0xde, 0xe8, 0x91, 0x9a, 0x70, 0x53, 0x41,
+	0xe0, 0x00, 0x0b, 0x5d, 0x85, 0x29, 0x2a, 0x66, 0x64, 0xb3, 0x21, 0x34, 0xbb, 0xd7, 0x54, 0x96,
+	0xd8, 0x64, 0x14, 0xe8, 0xbb, 0x16, 0xa3, 0xe2, 0x44, 0x6d, 0xfb, 0xc7, 0xa6, 0x19, 0xfd, 0x7f,
+	0xff, 0xd1, 0x8d, 0x8e, 0x31, 0x3d, 0xd6, 0xd7, 0x36, 0x03, 0xc7, 0x98, 0x0e, 0x7d, 0x66, 0xf3,
+	0x2e, 0x3c, 0x95, 0xae, 0x0a, 0x8e, 0xe5, 0x99, 0xea, 0x8f, 0x92, 0xb2, 0x92, 0x16, 0x58, 0xb0,
+	0xfd, 0xac, 0x93, 0xb4, 0x98, 0x72, 0xc7, 0x6d, 0x31, 0x31, 0x73, 0x28, 0xfa, 0x51, 0x2f, 0x7a,
+	0x4f, 0xaf, 0x33, 0x2b, 0xcb, 0x33, 0xd1, 0x2e, 0x98, 0x9e, 0x6b, 0xed, 0x27, 0x16, 0xcc, 0xa7,
+	0xd6, 0x0e, 0x65, 0x98, 0x3b, 0x49, 0x19, 0x5a, 0xc7, 0x2d, 0xc3, 0xef, 0xe7, 0x60, 0x06, 0xd3,
+	0xb6, 0x17, 0x4b, 0x40, 0x6d, 0x04, 0xcf, 0xa1, 0x32, 0xb8, 0x15, 0x89, 0x5b, 0x1d, 0xa5, 0xd1,
+	0xd8, 0x3b, 0x28, 0xb1, 0x5d, 0x5a, 0x81, 0x0d, 0xd9, 0xf7, 0xf6, 0xef, 0x4a, 0x8d, 0xa9, 0x93,
+	0x43, 0x25, 0xd9, 0x14, 0xa0, 0x40, 0x96, 0xf7, 0x4e, 0xb5, 0x72, 0x7f, 0x3d, 0xc3, 0x0d, 0xd6,
+	0x6e, 0x64, 0x59, 0x8c, 0x15, 0xa0, 0xfd, 0x71, 0x0e, 0x94, 0x0b, 0xf2, 0x18, 0xb4, 0xe3, 0xaf,
+	0xc4, 0xb4, 0xe3, 0x72, 0x96, 0x10, 0x59, 0xaf, 0x50, 0x4c, 0xd2, 0x3d, 0x3c, 0x97, 0x31, 0xee,
+	0xf6, 0x88, 0x30, 0xcc, 0xdf, 0x59, 0x30, 0x2e, 0xeb, 0x3d, 0x06, 0x45, 0xbb, 0x11, 0x57, 0xb4,
+	0x2f, 0x66, 0x18, 0x45, 0x0f, 0x05, 0xfb, 0x9f, 0x79, 0xdd, 0xfb, 0xd0, 0xf9, 0x6c, 0x10, 0x56,
+	0xd3, 0x5e, 0x55, 0xb4, 0x4b, 0x44, 0x21, 0x56, 0xb4, 0x70, 0x6f, 0x8f, 0x9e, 0xc0, 0xde, 0xfe,
+	0x35, 0x75, 0xfd, 0x97, 0x72, 0x9f, 0xd6, 0xae, 0x87, 0xee, 0x53, 0x3e, 0xf3, 0x3d, 0x66, 0x7d,
+	0xd7, 0x3a, 0x8a, 0x68, 0xe3, 0x04, 0x2a, 0xee, 0xe2, 0x23, 0x5c, 0xaa, 0x76, 0x52, 0x99, 0x69,
+	0x57, 0xe3, 0xf5, 0x01, 0x35, 0xa7, 0x72, 0xa9, 0xba, 0x8a, 0x71, 0x37, 0x23, 0xd4, 0x80, 0x09,
+	0xf3, 0x05, 0x86, 0x5e, 0xa7, 0xe7, 0xb3, 0x3f, 0xf5, 0x50, 0x97, 0x7e, 0xcc, 0x12, 0x1c, 0x43,
+	0xb6, 0xff, 0xd0, 0x02, 0x88, 0x02, 0xca, 0x62, 0xce, 0xab, 0x5e, 0xc7, 0x55, 0x91, 0x84, 0x7c,
+	0x34, 0xe7, 0x65, 0x51, 0x88, 0x15, 0x4d, 0xec, 0x1f, 0xe5, 0x8f, 0xe9, 0x45, 0x7d, 0x2e, 0x8b,
+	0xab, 0x97, 0x08, 0x5c, 0xab, 0x42, 0xac, 0x01, 0xed, 0x0f, 0x47, 0xa0, 0x60, 0xec, 0xb3, 0x44,
+	0xd8, 0x7a, 0xf2, 0x64, 0xc2, 0xd6, 0xe9, 0xb1, 0x84, 0xc2, 0x40, 0xb1, 0x04, 0x0e, 0x53, 0xda,
+	0x43, 0x0e, 0x9e, 0xe9, 0xa8, 0x58, 0xcb, 0xc0, 0x7e, 0x38, 0x12, 0x56, 0xeb, 0xf5, 0x18, 0x24,
+	0x4e, 0xb0, 0x10, 0x56, 0xaf, 0x2e, 0xa9, 0x74, 0x5a, 0x2d, 0xc2, 0xf6, 0x16, 0x26, 0x64, 0xe7,
+	0x43, 0xab, 0xf7, 0x7a, 0x8c, 0x8a, 0x13, 0xb5, 0xd1, 0x46, 0x38, 0xa1, 0xea, 0xe9, 0xc7, 0x4b,
+	0x59, 0x26, 0x54, 0x59, 0xfd, 0xf1, 0x79, 0x14, 0x22, 0xf5, 0xb6, 0xa4, 0xd3, 0x50, 0xbb, 0xa1,
+	0x3e, 0x72, 0x23, 0x96, 0xf1, 0x88, 0x5c, 0x54, 0xa1, 0x48, 0x6f, 0x75, 0xd5, 0xc0, 0x29, 0xad,
+	0x84, 0x1a, 0xd0, 0xae, 0x76, 0xb8, 0x77, 0x74, 0x70, 0x23, 0xab, 0x9f, 0x15, 0xf9, 0x8e, 0xf2,
+	0x3d, 0x40, 0x39, 0x81, 0x8a, 0xbb, 0xf8, 0xa0, 0xf7, 0x61, 0x52, 0x4c, 0x72, 0xc4, 0x18, 0x8e,
+	0xc8, 0x58, 0x07, 0x55, 0x0d, 0x48, 0x1c, 0xe7, 0x60, 0x7f, 0x9e, 0x87, 0x74, 0x47, 0x3f, 0x7a,
+	0x8a, 0x68, 0x3d, 0xe2, 0x29, 0xe2, 0x5d, 0x18, 0xe7, 0x3e, 0x61, 0xea, 0x4d, 0x68, 0x6e, 0xb0,
+	0x37, 0xa1, 0x95, 0x00, 0x00, 0x47, 0x58, 0x89, 0xa8, 0x4b, 0xfe, 0x58, 0xa3, 0x2e, 0xe7, 0x01,
+	0xa4, 0x23, 0x26, 0xd5, 0x8c, 0x3c, 0x6f, 0x26, 0xa3, 0x5d, 0x7b, 0x2d, 0xa4, 0x60, 0xa3, 0x16,
+	0xfa, 0x7a, 0x78, 0x8a, 0xab, 0x4b, 0x2b, 0xbf, 0xd0, 0x75, 0xc7, 0xf0, 0x74, 0xcc, 0xcc, 0x4b,
+	0x04, 0x72, 0x33, 0x5c, 0x86, 0x4e, 0x09, 0x10, 0x8c, 0x66, 0x0b, 0x10, 0xd8, 0xff, 0x9d, 0x83,
+	0x98, 0x16, 0x46, 0xdf, 0xb5, 0x60, 0x96, 0x24, 0x3e, 0xbb, 0x13, 0x18, 0xb1, 0xbf, 0x9c, 0xed,
+	0x5b, 0x48, 0x5d, 0x5f, 0xed, 0x89, 0x92, 0xe4, 0xc9, 0x2a, 0x1c, 0x77, 0x33, 0x45, 0xbf, 0x6b,
+	0xc1, 0x69, 0xd2, 0xfd, 0x5d, 0x25, 0xbd, 0x78, 0x2e, 0x0d, 0xfc, 0x61, 0xa6, 0xd2, 0xd3, 0x07,
+	0xfb, 0x4b, 0x69, 0x5f, 0x9c, 0xc2, 0x69, 0xec, 0xd0, 0x3b, 0x30, 0x44, 0x58, 0x3d, 0x08, 0x1f,
+	0x67, 0x67, 0x1b, 0x7c, 0x2e, 0x2b, 0x32, 0x25, 0x56, 0x58, 0x9d, 0x63, 0x09, 0x6a, 0xff, 0x34,
+	0x0f, 0x33, 0xc9, 0x27, 0x90, 0xfa, 0xca, 0xfd, 0x50, 0xea, 0x95, 0x7b, 0xb1, 0xd7, 0x64, 0x02,
+	0x25, 0xf9, 0xec, 0x57, 0xe6, 0x41, 0x14, 0x2d, 0xdc, 0x6b, 0xf2, 0x61, 0xd2, 0xf0, 0x11, 0xf6,
+	0x9a, 0x7c, 0x8d, 0x14, 0x61, 0xa1, 0x8b, 0xf1, 0x88, 0xb4, 0x9d, 0x8c, 0x48, 0xcf, 0x9a, 0x63,
+	0x19, 0x34, 0x28, 0xdd, 0x82, 0x82, 0x31, 0x0f, 0x7a, 0x47, 0x5f, 0xce, 0x2c, 0xf7, 0x68, 0xd9,
+	0x4d, 0xab, 0x7b, 0x8f, 0x11, 0xc5, 0xc4, 0x8f, 0xf4, 0x87, 0x94, 0xd6, 0x91, 0xa2, 0xb6, 0x52,
+	0x5c, 0x06, 0x9a, 0xfd, 0x2f, 0x16, 0x4c, 0xc6, 0x1e, 0xa1, 0x08, 0x6e, 0xc1, 0xeb, 0xa2, 0xc1,
+	0xbf, 0x31, 0x75, 0x27, 0x44, 0xc0, 0x06, 0x1a, 0xfa, 0x36, 0x14, 0x9a, 0x9e, 0x5b, 0xa7, 0xdc,
+	0xaf, 0x78, 0x64, 0x47, 0xef, 0x93, 0xac, 0xf1, 0xab, 0x85, 0x83, 0xfd, 0xa5, 0xb9, 0x75, 0x05,
+	0x53, 0xf6, 0x5a, 0xed, 0x26, 0xf5, 0xd5, 0x3b, 0x34, 0x6c, 0x82, 0xcb, 0xec, 0x77, 0x78, 0x7d,
+	0xe0, 0x49, 0xcd, 0x7e, 0x47, 0xf7, 0x1e, 0x8e, 0x39, 0xfb, 0x1d, 0xbb, 0x50, 0x71, 0x48, 0xf6,
+	0x3b, 0xac, 0xfb, 0xc4, 0x66, 0xbf, 0xc3, 0x1e, 0xf6, 0x70, 0xbf, 0xfe, 0x2b, 0x67, 0x8c, 0x22,
+	0xee, 0x82, 0xe5, 0x1e, 0xe1, 0x82, 0xbd, 0x0b, 0x63, 0x8e, 0xeb, 0x53, 0xb6, 0x4b, 0x9a, 0x3a,
+	0xfe, 0x9c, 0x75, 0x2d, 0x86, 0x43, 0x5d, 0xd3, 0x38, 0x38, 0x44, 0x44, 0x4d, 0x98, 0x0f, 0x52,
+	0x3e, 0x8c, 0x92, 0x28, 0x29, 0xad, 0xaf, 0xc4, 0xbe, 0x16, 0xe4, 0x26, 0xae, 0xa7, 0x55, 0x7a,
+	0xd8, 0x8b, 0x80, 0xd3, 0x41, 0x11, 0x87, 0x49, 0x6e, 0xc4, 0x1e, 0x82, 0x13, 0xb1, 0xcf, 0x74,
+	0x59, 0x32, 0x5c, 0x63, 0xdc, 0xa3, 0x35, 0x41, 0x71, 0x9c, 0x87, 0xfd, 0x91, 0x05, 0x53, 0xf1,
+	0xab, 0x3b, 0xff, 0xeb, 0x7e, 0xd0, 0xe7, 0x79, 0x98, 0x4e, 0x2c, 0xfe, 0x84, 0x2f, 0x34, 0xfe,
+	0x38, 0x7d, 0xa1, 0x91, 0x81, 0x7c, 0xa1, 0x74, 0x27, 0x60, 0x68, 0x20, 0x27, 0xe0, 0x8a, 0x32,
+	0xc4, 0xf5, 0x62, 0x5a, 0x5b, 0xd5, 0x8f, 0xcd, 0xc2, 0x09, 0x5e, 0x37, 0x89, 0x38, 0x5e, 0x57,
+	0x5a, 0x38, 0xb5, 0xee, 0x4f, 0x28, 0x69, 0x2f, 0xe2, 0x52, 0xd6, 0xab, 0xec, 0x21, 0x80, 0xb2,
+	0x70, 0x52, 0x08, 0x38, 0x8d, 0x9d, 0xed, 0xc3, 0x74, 0xf2, 0x41, 0x59, 0x5f, 0x81, 0xeb, 0x36,
+	0xf1, 0x83, 0x07, 0x56, 0x61, 0x8d, 0x0d, 0xe2, 0x37, 0xb0, 0xa4, 0xa0, 0x67, 0x20, 0xdf, 0x61,
+	0xcd, 0xe4, 0xab, 0xbf, 0xdb, 0x78, 0x1d, 0x8b, 0x72, 0xfb, 0x4f, 0x2c, 0x98, 0x4f, 0xbd, 0xcd,
+	0xd8, 0x07, 0xf3, 0xfb, 0x30, 0xa2, 0x64, 0xa3, 0xcf, 0x83, 0x2b, 0x7d, 0x47, 0x3c, 0xbb, 0x1f,
+	0xcf, 0x29, 0x3f, 0x51, 0x91, 0xb0, 0x86, 0x2d, 0xbd, 0xf9, 0xe9, 0x97, 0x67, 0x4e, 0x7d, 0xf6,
+	0xe5, 0x99, 0x53, 0x5f, 0x7c, 0x79, 0xe6, 0xd4, 0x87, 0x07, 0x67, 0xac, 0x4f, 0x0f, 0xce, 0x58,
+	0x9f, 0x1d, 0x9c, 0xb1, 0xbe, 0x38, 0x38, 0x63, 0xfd, 0xdb, 0xc1, 0x19, 0xeb, 0xa3, 0x9f, 0x9d,
+	0x39, 0x75, 0xef, 0xd9, 0x7e, 0xbe, 0x57, 0xfb, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x7d, 0x82,
+	0x13, 0x96, 0xd6, 0x56, 0x00, 0x00,
 }
 
 func (m *AnalysisRunArgument) Marshal() (dAtA []byte, err error) {
@@ -3827,6 +3830,35 @@ func (m *FreightStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	_ = i
 	var l int
 	_ = l
+	if len(m.Metadata) > 0 {
+		keysForMetadata := make([]string, 0, len(m.Metadata))
+		for k := range m.Metadata {
+			keysForMetadata = append(keysForMetadata, string(k))
+		}
+		github_com_gogo_protobuf_sortkeys.Strings(keysForMetadata)
+		for iNdEx := len(keysForMetadata) - 1; iNdEx >= 0; iNdEx-- {
+			v := m.Metadata[string(keysForMetadata[iNdEx])]
+			baseI := i
+			{
+				size, err := (&v).MarshalToSizedBuffer(dAtA[:i])
+				if err != nil {
+					return 0, err
+				}
+				i -= size
+				i = encodeVarintGenerated(dAtA, i, uint64(size))
+			}
+			i--
+			dAtA[i] = 0x12
+			i -= len(keysForMetadata[iNdEx])
+			copy(dAtA[i:], keysForMetadata[iNdEx])
+			i = encodeVarintGenerated(dAtA, i, uint64(len(keysForMetadata[iNdEx])))
+			i--
+			dAtA[i] = 0xa
+			i = encodeVarintGenerated(dAtA, i, uint64(baseI-i))
+			i--
+			dAtA[i] = 0x22
+		}
+	}
 	if len(m.CurrentlyIn) > 0 {
 		keysForCurrentlyIn := make([]string, 0, len(m.CurrentlyIn))
 		for k := range m.CurrentlyIn {
@@ -7172,6 +7204,15 @@ func (m *FreightStatus) Size() (n int) {
 			n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize))
 		}
 	}
+	if len(m.Metadata) > 0 {
+		for k, v := range m.Metadata {
+			_ = k
+			_ = v
+			l = v.Size()
+			mapEntrySize := 1 + len(k) + sovGenerated(uint64(len(k))) + 1 + l + sovGenerated(uint64(l))
+			n += mapEntrySize + 1 + sovGenerated(uint64(mapEntrySize))
+		}
+	}
 	return n
 }
 
@@ -8610,10 +8651,21 @@ func (this *FreightStatus) String() string {
 		mapStringForCurrentlyIn += fmt.Sprintf("%v: %v,", k, this.CurrentlyIn[k])
 	}
 	mapStringForCurrentlyIn += "}"
+	keysForMetadata := make([]string, 0, len(this.Metadata))
+	for k := range this.Metadata {
+		keysForMetadata = append(keysForMetadata, k)
+	}
+	github_com_gogo_protobuf_sortkeys.Strings(keysForMetadata)
+	mapStringForMetadata := "map[string]v11.JSON{"
+	for _, k := range keysForMetadata {
+		mapStringForMetadata += fmt.Sprintf("%v: %v,", k, this.Metadata[k])
+	}
+	mapStringForMetadata += "}"
 	s := strings.Join([]string{`&FreightStatus{`,
 		`VerifiedIn:` + mapStringForVerifiedIn + `,`,
 		`ApprovedFor:` + mapStringForApprovedFor + `,`,
 		`CurrentlyIn:` + mapStringForCurrentlyIn + `,`,
+		`Metadata:` + mapStringForMetadata + `,`,
 		`}`,
 	}, "")
 	return s
@@ -8655,7 +8707,7 @@ func (this *GitHubWebhookReceiver) String() string {
 		return "nil"
 	}
 	s := strings.Join([]string{`&GitHubWebhookReceiver{`,
-		`SecretRef:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.SecretRef), "LocalObjectReference", "v11.LocalObjectReference", 1), `&`, ``, 1) + `,`,
+		`SecretRef:` + strings.Replace(strings.Replace(fmt.Sprintf("%v", this.SecretRef), "LocalObjectReference", "v12.LocalObjectReference", 1), `&`, ``, 1) + `,`,
 		`}`,
 	}, "")
 	return s
@@ -8687,8 +8739,8 @@ func (this *Health) String() string {
 	s := strings.Join([]string{`&Health{`,
 		`Status:` + fmt.Sprintf("%v", this.Status) + `,`,
 		`Issues:` + fmt.Sprintf("%v", this.Issues) + `,`,
-		`Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "JSON", "v12.JSON", 1) + `,`,
-		`Output:` + strings.Replace(fmt.Sprintf("%v", this.Output), "JSON", "v12.JSON", 1) + `,`,
+		`Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "JSON", "v11.JSON", 1) + `,`,
+		`Output:` + strings.Replace(fmt.Sprintf("%v", this.Output), "JSON", "v11.JSON", 1) + `,`,
 		`}`,
 	}, "")
 	return s
@@ -8699,7 +8751,7 @@ func (this *HealthCheckStep) String() string {
 	}
 	s := strings.Join([]string{`&HealthCheckStep{`,
 		`Uses:` + fmt.Sprintf("%v", this.Uses) + `,`,
-		`Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "JSON", "v12.JSON", 1) + `,`,
+		`Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "JSON", "v11.JSON", 1) + `,`,
 		`}`,
 	}, "")
 	return s
@@ -9009,7 +9061,7 @@ func (this *PromotionStatus) String() string {
 		`FreightCollection:` + strings.Replace(this.FreightCollection.String(), "FreightCollection", "FreightCollection", 1) + `,`,
 		`HealthChecks:` + repeatedStringForHealthChecks + `,`,
 		`CurrentStep:` + fmt.Sprintf("%v", this.CurrentStep) + `,`,
-		`State:` + strings.Replace(fmt.Sprintf("%v", this.State), "JSON", "v12.JSON", 1) + `,`,
+		`State:` + strings.Replace(fmt.Sprintf("%v", this.State), "JSON", "v11.JSON", 1) + `,`,
 		`StepExecutionMetadata:` + repeatedStringForStepExecutionMetadata + `,`,
 		`}`,
 	}, "")
@@ -9027,7 +9079,7 @@ func (this *PromotionStep) String() string {
 	s := strings.Join([]string{`&PromotionStep{`,
 		`Uses:` + fmt.Sprintf("%v", this.Uses) + `,`,
 		`As:` + fmt.Sprintf("%v", this.As) + `,`,
-		`Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "JSON", "v12.JSON", 1) + `,`,
+		`Config:` + strings.Replace(fmt.Sprintf("%v", this.Config), "JSON", "v11.JSON", 1) + `,`,
 		`Retry:` + strings.Replace(this.Retry.String(), "PromotionStepRetry", "PromotionStepRetry", 1) + `,`,
 		`Task:` + strings.Replace(this.Task.String(), "PromotionTaskReference", "PromotionTaskReference", 1) + `,`,
 		`Vars:` + repeatedStringForVars + `,`,
@@ -13988,6 +14040,135 @@ func (m *FreightStatus) Unmarshal(dAtA []byte) error {
 			}
 			m.CurrentlyIn[mapkey] = *mapvalue
 			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Metadata", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowGenerated
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthGenerated
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.Metadata == nil {
+				m.Metadata = make(map[string]v11.JSON)
+			}
+			var mapkey string
+			mapvalue := &v11.JSON{}
+			for iNdEx < postIndex {
+				entryPreIndex := iNdEx
+				var wire uint64
+				for shift := uint(0); ; shift += 7 {
+					if shift >= 64 {
+						return ErrIntOverflowGenerated
+					}
+					if iNdEx >= l {
+						return io.ErrUnexpectedEOF
+					}
+					b := dAtA[iNdEx]
+					iNdEx++
+					wire |= uint64(b&0x7F) << shift
+					if b < 0x80 {
+						break
+					}
+				}
+				fieldNum := int32(wire >> 3)
+				if fieldNum == 1 {
+					var stringLenmapkey uint64
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowGenerated
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						stringLenmapkey |= uint64(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					intStringLenmapkey := int(stringLenmapkey)
+					if intStringLenmapkey < 0 {
+						return ErrInvalidLengthGenerated
+					}
+					postStringIndexmapkey := iNdEx + intStringLenmapkey
+					if postStringIndexmapkey < 0 {
+						return ErrInvalidLengthGenerated
+					}
+					if postStringIndexmapkey > l {
+						return io.ErrUnexpectedEOF
+					}
+					mapkey = string(dAtA[iNdEx:postStringIndexmapkey])
+					iNdEx = postStringIndexmapkey
+				} else if fieldNum == 2 {
+					var mapmsglen int
+					for shift := uint(0); ; shift += 7 {
+						if shift >= 64 {
+							return ErrIntOverflowGenerated
+						}
+						if iNdEx >= l {
+							return io.ErrUnexpectedEOF
+						}
+						b := dAtA[iNdEx]
+						iNdEx++
+						mapmsglen |= int(b&0x7F) << shift
+						if b < 0x80 {
+							break
+						}
+					}
+					if mapmsglen < 0 {
+						return ErrInvalidLengthGenerated
+					}
+					postmsgIndex := iNdEx + mapmsglen
+					if postmsgIndex < 0 {
+						return ErrInvalidLengthGenerated
+					}
+					if postmsgIndex > l {
+						return io.ErrUnexpectedEOF
+					}
+					mapvalue = &v11.JSON{}
+					if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil {
+						return err
+					}
+					iNdEx = postmsgIndex
+				} else {
+					iNdEx = entryPreIndex
+					skippy, err := skipGenerated(dAtA[iNdEx:])
+					if err != nil {
+						return err
+					}
+					if (skippy < 0) || (iNdEx+skippy) < 0 {
+						return ErrInvalidLengthGenerated
+					}
+					if (iNdEx + skippy) > postIndex {
+						return io.ErrUnexpectedEOF
+					}
+					iNdEx += skippy
+				}
+			}
+			m.Metadata[mapkey] = *mapvalue
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipGenerated(dAtA[iNdEx:])
@@ -14970,7 +15151,7 @@ func (m *Health) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.Config == nil {
-				m.Config = &v12.JSON{}
+				m.Config = &v11.JSON{}
 			}
 			if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
@@ -15006,7 +15187,7 @@ func (m *Health) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.Output == nil {
-				m.Output = &v12.JSON{}
+				m.Output = &v11.JSON{}
 			}
 			if err := m.Output.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
@@ -15124,7 +15305,7 @@ func (m *HealthCheckStep) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.Config == nil {
-				m.Config = &v12.JSON{}
+				m.Config = &v11.JSON{}
 			}
 			if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
@@ -18223,7 +18404,7 @@ func (m *PromotionStatus) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.State == nil {
-				m.State = &v12.JSON{}
+				m.State = &v11.JSON{}
 			}
 			if err := m.State.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
@@ -18407,7 +18588,7 @@ func (m *PromotionStep) Unmarshal(dAtA []byte) error {
 				return io.ErrUnexpectedEOF
 			}
 			if m.Config == nil {
-				m.Config = &v12.JSON{}
+				m.Config = &v11.JSON{}
 			}
 			if err := m.Config.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
 				return err
diff --git a/api/v1alpha1/generated.proto b/api/v1alpha1/generated.proto
index ba94c531..53bc6007 100644
--- a/api/v1alpha1/generated.proto
+++ b/api/v1alpha1/generated.proto
@@ -494,6 +494,11 @@ message FreightStatus {
   // might wish to promote a piece of Freight to a given Stage without
   // transiting the entire pipeline.
   map<string, ApprovedStage> approvedFor = 2;
+
+  // Metadata is a map of arbitrary metadata associated with the Freight.
+  // This is useful for storing additional information about the Freight
+  // or Promotion that can be shared across steps or stages.
+  map<string, .k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.JSON> metadata = 4;
 }
 
 // GitCommit describes a specific commit from a specific Git repository.
@@ -938,9 +943,6 @@ message ProjectConfigSpec {
 
   // WebhookReceivers describes Project-specific webhook receivers used for
   // processing events from various external platforms
-  //
-  // +kubebuilder:validation:MaxItems=5
-  // +kubebuilder:validation:XValidation:message="WebhookReceiverConfig must have a unique name",rule="self.all(i, size(self.filter(j, i.name == j.name)) == 1)"
   repeated WebhookReceiverConfig receivers = 2;
 }
 
@@ -1513,6 +1515,8 @@ message Verification {
 // process.
 message VerificationInfo {
   // ID is the identifier of the Verification process.
+  //
+  // +kubebuilder:validation:Required
   optional string id = 4;
 
   // Actor is the name of the entity that initiated or aborted the
@@ -1520,12 +1524,16 @@ message VerificationInfo {
   optional string actor = 7;
 
   // StartTime is the time at which the Verification process was started.
+  //
+  // +kubebuilder:validation:Required
   optional .k8s.io.apimachinery.pkg.apis.meta.v1.Time startTime = 5;
 
   // Phase describes the current phase of the Verification process. Generally,
   // this will be a reflection of the underlying AnalysisRun's phase, however,
   // there are exceptions to this, such as in the case where an AnalysisRun
   // cannot be launched successfully.
+  //
+  // +kubebuilder:validation:Required
   optional string phase = 1;
 
   // Message may contain additional information about why the verification
diff --git a/api/v1alpha1/project_config_types.go b/api/v1alpha1/project_config_types.go
index 0f76077a..23f9b5cc 100644
--- a/api/v1alpha1/project_config_types.go
+++ b/api/v1alpha1/project_config_types.go
@@ -43,9 +43,6 @@ type ProjectConfigSpec struct {
 	PromotionPolicies []PromotionPolicy `json:"promotionPolicies,omitempty" protobuf:"bytes,1,rep,name=promotionPolicies"`
 	// WebhookReceivers describes Project-specific webhook receivers used for
 	// processing events from various external platforms
-	//
-	// +kubebuilder:validation:MaxItems=5
-	// +kubebuilder:validation:XValidation:message="WebhookReceiverConfig must have a unique name",rule="self.all(i, size(self.filter(j, i.name == j.name)) == 1)"
 	WebhookReceivers []WebhookReceiverConfig `json:"webhookReceivers,omitempty" protobuf:"bytes,2,rep,name=receivers"`
 }
 
diff --git a/api/v1alpha1/stage_types.go b/api/v1alpha1/stage_types.go
index 96979c52..5308f34d 100644
--- a/api/v1alpha1/stage_types.go
+++ b/api/v1alpha1/stage_types.go
@@ -704,17 +704,23 @@ type AnalysisRunArgument struct {
 // process.
 type VerificationInfo struct {
 	// ID is the identifier of the Verification process.
-	ID string `json:"id,omitempty" protobuf:"bytes,4,opt,name=id"`
+	//
+	// +kubebuilder:validation:Required
+	ID string `json:"id" protobuf:"bytes,4,opt,name=id"`
 	// Actor is the name of the entity that initiated or aborted the
 	// Verification process.
 	Actor string `json:"actor,omitempty" protobuf:"bytes,7,opt,name=actor"`
 	// StartTime is the time at which the Verification process was started.
-	StartTime *metav1.Time `json:"startTime,omitempty" protobuf:"bytes,5,opt,name=startTime"`
+	//
+	// +kubebuilder:validation:Required
+	StartTime *metav1.Time `json:"startTime" protobuf:"bytes,5,opt,name=startTime"`
 	// Phase describes the current phase of the Verification process. Generally,
 	// this will be a reflection of the underlying AnalysisRun's phase, however,
 	// there are exceptions to this, such as in the case where an AnalysisRun
 	// cannot be launched successfully.
-	Phase VerificationPhase `json:"phase,omitempty" protobuf:"bytes,1,opt,name=phase"`
+	//
+	// +kubebuilder:validation:Required
+	Phase VerificationPhase `json:"phase" protobuf:"bytes,1,opt,name=phase"`
 	// Message may contain additional information about why the verification
 	// process is in its current phase.
 	Message string `json:"message,omitempty" protobuf:"bytes,2,opt,name=message"`
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 94e33413..5de5710b 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -5,8 +5,8 @@
 package v1alpha1
 
 import (
-	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
-	"k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 )
 
@@ -584,7 +584,7 @@ func (in *FreightSources) DeepCopyInto(out *FreightSources) {
 	}
 	if in.RequiredSoakTime != nil {
 		in, out := &in.RequiredSoakTime, &out.RequiredSoakTime
-		*out = new(v1.Duration)
+		*out = new(metav1.Duration)
 		**out = **in
 	}
 }
@@ -623,6 +623,13 @@ func (in *FreightStatus) DeepCopyInto(out *FreightStatus) {
 			(*out)[key] = *val.DeepCopy()
 		}
 	}
+	if in.Metadata != nil {
+		in, out := &in.Metadata, &out.Metadata
+		*out = make(map[string]v1.JSON, len(*in))
+		for key, val := range *in {
+			(*out)[key] = *val.DeepCopy()
+		}
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FreightStatus.
@@ -728,12 +735,12 @@ func (in *Health) DeepCopyInto(out *Health) {
 	}
 	if in.Config != nil {
 		in, out := &in.Config, &out.Config
-		*out = new(apiextensionsv1.JSON)
+		*out = new(v1.JSON)
 		(*in).DeepCopyInto(*out)
 	}
 	if in.Output != nil {
 		in, out := &in.Output, &out.Output
-		*out = new(apiextensionsv1.JSON)
+		*out = new(v1.JSON)
 		(*in).DeepCopyInto(*out)
 	}
 }
@@ -753,7 +760,7 @@ func (in *HealthCheckStep) DeepCopyInto(out *HealthCheckStep) {
 	*out = *in
 	if in.Config != nil {
 		in, out := &in.Config, &out.Config
-		*out = new(apiextensionsv1.JSON)
+		*out = new(v1.JSON)
 		(*in).DeepCopyInto(*out)
 	}
 }
@@ -971,7 +978,7 @@ func (in *ProjectConfigStatus) DeepCopyInto(out *ProjectConfigStatus) {
 	*out = *in
 	if in.Conditions != nil {
 		in, out := &in.Conditions, &out.Conditions
-		*out = make([]v1.Condition, len(*in))
+		*out = make([]metav1.Condition, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
@@ -1047,7 +1054,7 @@ func (in *ProjectStatus) DeepCopyInto(out *ProjectStatus) {
 	*out = *in
 	if in.Conditions != nil {
 		in, out := &in.Conditions, &out.Conditions
-		*out = make([]v1.Condition, len(*in))
+		*out = make([]metav1.Condition, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
@@ -1153,7 +1160,7 @@ func (in *PromotionPolicySelector) DeepCopyInto(out *PromotionPolicySelector) {
 	*out = *in
 	if in.LabelSelector != nil {
 		in, out := &in.LabelSelector, &out.LabelSelector
-		*out = new(v1.LabelSelector)
+		*out = new(metav1.LabelSelector)
 		(*in).DeepCopyInto(*out)
 	}
 }
@@ -1257,7 +1264,7 @@ func (in *PromotionStatus) DeepCopyInto(out *PromotionStatus) {
 	}
 	if in.State != nil {
 		in, out := &in.State, &out.State
-		*out = new(apiextensionsv1.JSON)
+		*out = new(v1.JSON)
 		(*in).DeepCopyInto(*out)
 	}
 }
@@ -1292,7 +1299,7 @@ func (in *PromotionStep) DeepCopyInto(out *PromotionStep) {
 	}
 	if in.Config != nil {
 		in, out := &in.Config, &out.Config
-		*out = new(apiextensionsv1.JSON)
+		*out = new(v1.JSON)
 		(*in).DeepCopyInto(*out)
 	}
 }
@@ -1312,7 +1319,7 @@ func (in *PromotionStepRetry) DeepCopyInto(out *PromotionStepRetry) {
 	*out = *in
 	if in.Timeout != nil {
 		in, out := &in.Timeout, &out.Timeout
-		*out = new(v1.Duration)
+		*out = new(metav1.Duration)
 		**out = **in
 	}
 }
@@ -1617,7 +1624,7 @@ func (in *StageStatus) DeepCopyInto(out *StageStatus) {
 	*out = *in
 	if in.Conditions != nil {
 		in, out := &in.Conditions, &out.Conditions
-		*out = make([]v1.Condition, len(*in))
+		*out = make([]metav1.Condition, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
@@ -1792,7 +1799,7 @@ func (in *VerifiedStage) DeepCopyInto(out *VerifiedStage) {
 	}
 	if in.LongestCompletedSoak != nil {
 		in, out := &in.LongestCompletedSoak, &out.LongestCompletedSoak
-		*out = new(v1.Duration)
+		*out = new(metav1.Duration)
 		**out = **in
 	}
 }
@@ -1910,7 +1917,7 @@ func (in *WarehouseStatus) DeepCopyInto(out *WarehouseStatus) {
 	*out = *in
 	if in.Conditions != nil {
 		in, out := &in.Conditions, &out.Conditions
-		*out = make([]v1.Condition, len(*in))
+		*out = make([]metav1.Condition, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
diff --git a/charts/kargo/README.md b/charts/kargo/README.md
index 6b48d981..7ba4adf5 100644
--- a/charts/kargo/README.md
+++ b/charts/kargo/README.md
@@ -3,21 +3,6 @@
 
 ## Parameters
 
-### Global Parameters
-
-| Name                     | Description                                                                | Value |
-| ------------------------ | -------------------------------------------------------------------------- | ----- |
-| `global.env`             | Environment variables to add to all Kargo pods.                            | `[]`  |
-| `global.envFrom`         | Environment variables to add to all Kargo pods from ConfigMaps or Secrets. | `[]`  |
-| `global.nodeSelector`    | Default node selector for all Kargo pods.                                  | `{}`  |
-| `global.labels`          | Labels to add to all resources.                                            | `{}`  |
-| `global.annotations`     | Annotations to add to all resources.                                       | `{}`  |
-| `global.podLabels`       | Labels to add to all pods.                                                 | `{}`  |
-| `global.podAnnotations`  | Annotations to add to pods.                                                | `{}`  |
-| `global.tolerations`     | Default tolerations for all Kargo pods.                                    | `[]`  |
-| `global.affinity`        | Default affinity for all Kargo pods.                                       | `{}`  |
-| `global.securityContext` | Default security context for all Kargo pods.                               | `{}`  |
-
 ### Image Parameters
 
 | Name                | Description                                                                  | Value                  |
@@ -27,12 +12,22 @@
 | `image.pullPolicy`  | Image pull policy                                                            | `IfNotPresent`         |
 | `image.pullSecrets` | List of imagePullSecrets.                                                    | `[]`                   |
 
-### RBAC
+### Global Parameters
 
-| Name                              | Description                                             | Value  |
-| --------------------------------- | ------------------------------------------------------- | ------ |
-| `rbac.installClusterRoles`        | Indicates if `ClusterRoles` should be installed.        | `true` |
-| `rbac.installClusterRoleBindings` | Indicates if `ClusterRoleBindings` should be installed. | `true` |
+| Name                                | Description                                                                | Value |
+| ----------------------------------- | -------------------------------------------------------------------------- | ----- |
+| `global.labels`                     | Labels to add to all resources.                                            | `{}`  |
+| `global.annotations`                | Annotations to add to all resources.                                       | `{}`  |
+| `global.podLabels`                  | Labels to add to all pods.                                                 | `{}`  |
+| `global.podAnnotations`             | Annotations to add to pods.                                                | `{}`  |
+| `global.serviceAccount.labels`      | Global ServiceAccount labels.                                              | `{}`  |
+| `global.serviceAccount.annotations` | Global ServiceAccount annotations.                                         | `{}`  |
+| `global.env`                        | Environment variables to add to all Kargo pods.                            | `[]`  |
+| `global.envFrom`                    | Environment variables to add to all Kargo pods from ConfigMaps or Secrets. | `[]`  |
+| `global.nodeSelector`               | Default node selector for all Kargo pods.                                  | `{}`  |
+| `global.tolerations`                | Default tolerations for all Kargo pods.                                    | `[]`  |
+| `global.affinity`                   | Default affinity for all Kargo pods.                                       | `{}`  |
+| `global.securityContext`            | Default security context for all Kargo pods.                               | `{}`  |
 
 ### CRDs
 
@@ -41,6 +36,19 @@
 | `crds.install` | Indicates if Custom Resource Definitions should be installed and upgraded as part of the release. If set to `false`, the CRDs will only be installed if they do not already exist. | `true` |
 | `crds.keep`    | Indicates if Custom Resource Definitions should be kept when a release is uninstalled.                                                                                             | `true` |
 
+### RBAC
+
+| Name                              | Description                                             | Value  |
+| --------------------------------- | ------------------------------------------------------- | ------ |
+| `rbac.installClusterRoles`        | Indicates if `ClusterRoles` should be installed.        | `true` |
+| `rbac.installClusterRoleBindings` | Indicates if `ClusterRoleBindings` should be installed. | `true` |
+
+### Webhooks
+
+| Name                | Description                                                                                      | Value  |
+| ------------------- | ------------------------------------------------------------------------------------------------ | ------ |
+| `webhooks.register` | Whether to create `ValidatingWebhookConfiguration` and `MutatingWebhookConfiguration` resources. | `true` |
+
 ### KubeConfigs
 
 Optionally point to Kubernetes Secrets containing kubeconfig for:
@@ -73,34 +81,8 @@ the Kargo controller is running.
 | `api.replicas`                              | The number of API server pods.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `1`                      |
 | `api.host`                                  | The domain name where Kargo's API server will be accessible. When applicable, this is used for generation of an Ingress resource, certificates, and the OpenID Connect issuer and callback URLs. Note: The value in this field MAY include a port number and MUST NOT specify the protocol (http vs https), which is automatically inferred from other configuration options.                                                                                                                                                                                                                                                                                                       | `localhost`              |
 | `api.logLevel`                              | The log level for the API server.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | `INFO`                   |
-| `api.labels`                                | Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | `{}`                     |
-| `api.annotations`                           | Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `{}`                     |
-| `api.podLabels`                             | Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `{}`                     |
-| `api.podAnnotations`                        | Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `{}`                     |
 | `api.secretManagementEnabled`               | Specifies whether Secret management is enabled. This affects the API server's ability to manage repository credentials and other Project-level Secrets, such as those used by AnalysisRuns for verification purposes. If using GitOps to manage Kargo Projects declaratively, the API's Secret management capabilities are not needed and can be disabled to effectively reduce the API server's attackable surface.                                                                                                                                                                                                                                                                | `true`                   |
-| `api.resources`                             | Resources limits and requests for the api containers.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `{}`                     |
-| `api.nodeSelector`                          | Node selector for api pods. Defaults to `global.nodeSelector`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `{}`                     |
-| `api.tolerations`                           | Tolerations for api pods. Defaults to `global.tolerations`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | `[]`                     |
-| `api.affinity`                              | Specifies pod affinity for api pods. Defaults to `global.affinity`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `{}`                     |
-| `api.securityContext`                       | Security context for api pods. Defaults to `global.securityContext`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `{}`                     |
-| `api.cabundle.configMapName`                | Specifies the name of an optional ConfigMap containing CA certs that is managed "out of band." Values in the ConfigMap named here should each contain a single PEM-encoded CA cert. If secretName is also defined, it will take precedence over this field.                                                                                                                                                                                                                                                                                                                                                                                                                         | `""`                     |
-| `api.cabundle.secretName`                   | Specifies the name of an optional Secret containing CA certs that is managed "out of band." Values in the Secret named here should each contain a single PEM-encoded CA cert. If defined, the value of this field takes precedence over any in configMapName.                                                                                                                                                                                                                                                                                                                                                                                                                       | `""`                     |
-| `api.env`                                   | Environment variables to add to API server pods.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | `[]`                     |
-| `api.envFrom`                               | Environment variables to add to API server pods from ConfigMaps or Secrets.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | `[]`                     |
-| `api.probes.enabled`                        | Whether liveness and readiness probes should be included in the API server deployment. It is sometimes advantageous to disable these during local development.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `true`                   |
-| `api.tls.enabled`                           | Whether to enable TLS directly on the API server. This is helpful if you do not intend to use an ingress controller or if you require TLS end-to-end. All other settings in this section EXCEPT `terminatedUpstream` will be ignored when this is set to `false`.                                                                                                                                                                                                                                                                                                                                                                                                                   | `true`                   |
-| `api.tls.selfSignedCert`                    | Whether to generate a self-signed certificate for use by the API server. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-api-cert` **must** be provided in the same namespace as Kargo.                                                                                                                                                                                                                                                                                                                                                                              | `true`                   |
-| `api.tls.terminatedUpstream`                | Whether TLS is terminated upstream, i.e. a load balancer, reverse-proxy, or an Ingress controller using a single wildcard cert is terminating it. Setting this to `true` forces all API server URLs to use HTTPS even if the Ingress (if applicable) or API server itself are listening for plain HTTP requests.                                                                                                                                                                                                                                                                                                                                                                    | `false`                  |
 | `api.permissiveCORSPolicyEnabled`           | Whether to enable a permissive CORS (Cross Origin Resource Sharing) policy. This is sometimes advantageous during local development, but otherwise, should generally be left disabled.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | `false`                  |
-| `api.ingress.enabled`                       | Whether to enable ingress by creating an Ingress resource. By default, this is disabled. Enabling ingress is advanced usage.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | `false`                  |
-| `api.ingress.annotations`                   | Annotations specified by your ingress controller to customize the behavior of the Ingress resource.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `{}`                     |
-| `api.ingress.ingressClassName`              | If implemented by your ingress controller, specifies the ingress class. If your ingress controller does not support this, use the `kubernetes.io/ingress.class` annotation instead.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `nil`                    |
-| `api.ingress.tls.enabled`                   | Whether to associate a certificate with the Ingress resource.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | `true`                   |
-| `api.ingress.tls.selfSignedCert`            | Whether to generate a self-signed certificate for use with the API server's Ingress resource. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-api-ingress-cert` **must** be provided in the same namespace as Kargo. The value in this field has no effect if `api.ingress.tls.enabled` is `false`.                                                                                                                                                                                                                                                                  | `true`                   |
-| `api.ingress.pathType`                      | You may want to use `Prefix` for some controllers (like AWS LoadBalancer Ingress controller), which don't support `/` as wildcard path when pathType is set to `ImplementationSpecific`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | `ImplementationSpecific` |
-| `api.service.type`                          | If you're not going to use an ingress controller, you may want to change this value to `LoadBalancer` for production deployments. If running locally, you may want to change it to `NodePort` OR leave it as `ClusterIP` and use `kubectl port-forward` to map a port on the local network interface to the service.                                                                                                                                                                                                                                                                                                                                                                | `ClusterIP`              |
-| `api.service.nodePort`                      | Host port the `Service` will be mapped to when `type` is either `NodePort` or `LoadBalancer`. If not specified, Kubernetes chooses.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `undefined`              |
-| `api.service.annotations`                   | Annotations to add to the API server's service. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | `{}`                     |
 | `api.secret.name`                           | Specifies the name of an existing Secret which contains the `ADMIN_ACCOUNT_PASSWORD_HASH` and `ADMIN_ACCOUNT_TOKEN_SIGNING_KEY` values. By setting this, the Secret will **not** be generated by Helm.                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | `""`                     |
 | `api.adminAccount.enabled`                  | Whether to enable the admin account.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `true`                   |
 | `api.adminAccount.passwordHash`             | Bcrypt password hash for the admin account. A value **must** be provided for this field unless `api.secret.name` is specified.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `""`                     |
@@ -120,20 +102,22 @@ the Kargo controller is running.
 | `api.oidc.dex.image.tag`                    | Image tag for Dex.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | `v2.37.0`                |
 | `api.oidc.dex.image.pullPolicy`             | Image pull policy for Dex.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | `IfNotPresent`           |
 | `api.oidc.dex.image.pullSecrets`            | List of imagePullSecrets.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | `[]`                     |
+| `api.oidc.dex.skipApprovalScreen`           | Whether to skip Dex's own approval screen. Since upstream identity providers will already request user consent, this second approval screen from Dex can be both superfluous and confusing.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | `true`                   |
+| `api.oidc.dex.connectors`                   | Configure [Dex connectors](https://dexidp.io/docs/connectors/) to one or more upstream identity providers.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | `[]`                     |
+| `api.oidc.dex.serviceAccount.labels`        | Additional labels to add to the Dex server ServiceAccount.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | `{}`                     |
+| `api.oidc.dex.serviceAccount.annotations`   | Additional annotations to add to the Dex server ServiceAccount.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | `{}`                     |
 | `api.oidc.dex.env`                          | Environment variables to add to Dex server pods. This is convenient for cases where api.oidc.dex.connectors needs to reference environment variables from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.                                                                                                                                                                                                                                                                                                                                                                                                                          | `[]`                     |
 | `api.oidc.dex.envFrom`                      | Environment variables to add to Dex server pods from ConfigMaps or Secrets. This is especially convenient for cases where api.oidc.dex.connectors needs to reference environment variables from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.                                                                                                                                                                                                                                                                                                                                                                                    | `[]`                     |
 | `api.oidc.dex.volumes`                      | Add additional volumes to Dex pods. This is convenient for cases where api.oidc.dex.connectors needs to reference mounted data from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.                                                                                                                                                                                                                                                                                                                                                                                                                                                | `[]`                     |
 | `api.oidc.dex.volumeMounts`                 | Add additional volume mounts to Dex pods. This is convenient for cases where api.oidc.dex.connectors needs to reference mounted data from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.                                                                                                                                                                                                                                                                                                                                                                                                                                          | `nil`                    |
-| `api.oidc.dex.probes.enabled`               | Whether liveness and readiness probes should be included in the Dex server deployment. It is sometimes advantageous to disable these during local development.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `true`                   |
-| `api.oidc.dex.tls.selfSignedCert`           | Whether to generate a self-signed certificate for use with Dex. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-dex-server-cert` **must** be provided in the same namespace as Kargo. There is no provision for running Dex without TLS.                                                                                                                                                                                                                                                                                                                             | `true`                   |
-| `api.oidc.dex.skipApprovalScreen`           | Whether to skip Dex's own approval screen. Since upstream identity providers will already request user consent, this second approval screen from Dex can be both superfluous and confusing.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | `true`                   |
-| `api.oidc.dex.connectors`                   | Configure [Dex connectors](https://dexidp.io/docs/connectors/) to one or more upstream identity providers.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | `[]`                     |
 | `api.oidc.dex.resources`                    | Resources limits and requests for the Dex server containers.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | `{}`                     |
 | `api.oidc.dex.nodeSelector`                 | Node selector for Dex server pods. Defaults to `global.nodeSelector`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `{}`                     |
 | `api.oidc.dex.tolerations`                  | Tolerations for Dex server pods. Defaults to `global.tolerations`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | `[]`                     |
 | `api.oidc.dex.affinity`                     | Specifies pod affinity for the Dex server pods. Defaults to `global.affinity`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `{}`                     |
 | `api.oidc.dex.annotations`                  | Annotations to add to the Dex server pods. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | `{}`                     |
 | `api.oidc.dex.securityContext`              | Security context for Dex server pods. Defaults to `global.securityContext`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | `{}`                     |
+| `api.oidc.dex.probes.enabled`               | Whether liveness and readiness probes should be included in the Dex server deployment. It is sometimes advantageous to disable these during local development.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `true`                   |
+| `api.oidc.dex.tls.selfSignedCert`           | Whether to generate a self-signed certificate for use with Dex. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-dex-server-cert` **must** be provided in the same namespace as Kargo. There is no provision for running Dex without TLS.                                                                                                                                                                                                                                                                                                                             | `true`                   |
 | `api.argocd.urls`                           | Mapping of Argo CD shards names to URLs to support deep links to Argo CD URLs. If sharding is not used, map the empty string to the single Argo CD URL.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | `nil`                    |
 | `api.rollouts.integrationEnabled`           | Specifies whether Argo Rollouts integration is enabled. When not enabled, the API server will not be capable of creating/updating/applying AnalysesTemplate resources in the Kargo control plane. When enabled, the API server will perform a sanity check at startup. If Argo Rollouts CRDs are not found, the API server will proceed as if this integration had been explicitly disabled. Explicitly disabling is still preferable if this integration is not desired, as it will grant fewer permissions to the API server.                                                                                                                                                     | `true`                   |
 | `api.rollouts.logs.enabled`                 | Specifies whether support for streaming logs from AnalysisRuns using a JobMetric provider is enabled. This feature requires you to have forwarded and stored the logs yourself in a place where they can be retrieved with an HTTP GET.                                                                                                                                                                                                                                                                                                                                                                                                                                             | `false`                  |
@@ -141,19 +125,42 @@ the Kargo controller is running.
 | `api.rollouts.logs.tokenSecret.name`        | specifies the name of a Kubernetes Secret managed "out of band" that contains a token usable for accessing job metric logs.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | `nil`                    |
 | `api.rollouts.logs.tokenSecret.key`         | specifies the key in a Kubernetes Secret (named by name) that is managed "out of band" and contains a token usable for accessing job metric logs.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | `nil`                    |
 | `api.rollouts.logs.httpHeaders`             | Specifies HTTP headers to include in the HTTP GET request for log retrieval. These are typically used for authentication. The header values support expressions offset by ${{ }}, with the same variables documented for urlTemplate pre-defined and injected with values.                                                                                                                                                                                                                                                                                                                                                                                                          | `{}`                     |
+| `api.labels`                                | Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              | `{}`                     |
+| `api.annotations`                           | Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `{}`                     |
+| `api.podLabels`                             | Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `{}`                     |
+| `api.podAnnotations`                        | Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `{}`                     |
+| `api.serviceAccount.labels`                 | Additional labels to add to the API server ServiceAccount.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | `{}`                     |
+| `api.serviceAccount.annotations`            | Additional annotations to add to the API server ServiceAccount.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | `{}`                     |
+| `api.env`                                   | Environment variables to add to API server pods.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | `[]`                     |
+| `api.envFrom`                               | Environment variables to add to API server pods from ConfigMaps or Secrets.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | `[]`                     |
+| `api.resources`                             | Resources limits and requests for the api containers.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `{}`                     |
+| `api.nodeSelector`                          | Node selector for api pods. Defaults to `global.nodeSelector`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `{}`                     |
+| `api.tolerations`                           | Tolerations for api pods. Defaults to `global.tolerations`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | `[]`                     |
+| `api.affinity`                              | Specifies pod affinity for api pods. Defaults to `global.affinity`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `{}`                     |
+| `api.securityContext`                       | Security context for api pods. Defaults to `global.securityContext`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `{}`                     |
+| `api.cabundle.configMapName`                | Specifies the name of an optional ConfigMap containing CA certs that is managed "out of band." Values in the ConfigMap named here should each contain a single PEM-encoded CA cert. If secretName is also defined, it will take precedence over this field.                                                                                                                                                                                                                                                                                                                                                                                                                         | `""`                     |
+| `api.cabundle.secretName`                   | Specifies the name of an optional Secret containing CA certs that is managed "out of band." Values in the Secret named here should each contain a single PEM-encoded CA cert. If defined, the value of this field takes precedence over any in configMapName.                                                                                                                                                                                                                                                                                                                                                                                                                       | `""`                     |
+| `api.probes.enabled`                        | Whether liveness and readiness probes should be included in the API server deployment. It is sometimes advantageous to disable these during local development.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `true`                   |
+| `api.tls.enabled`                           | Whether to enable TLS directly on the API server. This is helpful if you do not intend to use an ingress controller or if you require TLS end-to-end. All other settings in this section EXCEPT `terminatedUpstream` will be ignored when this is set to `false`.                                                                                                                                                                                                                                                                                                                                                                                                                   | `true`                   |
+| `api.tls.selfSignedCert`                    | Whether to generate a self-signed certificate for use by the API server. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-api-cert` **must** be provided in the same namespace as Kargo.                                                                                                                                                                                                                                                                                                                                                                              | `true`                   |
+| `api.tls.terminatedUpstream`                | Whether TLS is terminated upstream, i.e. a load balancer, reverse-proxy, or an Ingress controller using a single wildcard cert is terminating it. Setting this to `true` forces all API server URLs to use HTTPS even if the Ingress (if applicable) or API server itself are listening for plain HTTP requests.                                                                                                                                                                                                                                                                                                                                                                    | `false`                  |
+| `api.ingress.enabled`                       | Whether to enable ingress by creating an Ingress resource. By default, this is disabled. Enabling ingress is advanced usage.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | `false`                  |
+| `api.ingress.annotations`                   | Annotations specified by your ingress controller to customize the behavior of the Ingress resource.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `{}`                     |
+| `api.ingress.ingressClassName`              | If implemented by your ingress controller, specifies the ingress class. If your ingress controller does not support this, use the `kubernetes.io/ingress.class` annotation instead.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `nil`                    |
+| `api.ingress.tls.enabled`                   | Whether to associate a certificate with the Ingress resource.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | `true`                   |
+| `api.ingress.tls.selfSignedCert`            | Whether to generate a self-signed certificate for use with the API server's Ingress resource. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-api-ingress-cert` **must** be provided in the same namespace as Kargo. The value in this field has no effect if `api.ingress.tls.enabled` is `false`.                                                                                                                                                                                                                                                                  | `true`                   |
+| `api.ingress.pathType`                      | You may want to use `Prefix` for some controllers (like AWS LoadBalancer Ingress controller), which don't support `/` as wildcard path when pathType is set to `ImplementationSpecific`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | `ImplementationSpecific` |
+| `api.service.type`                          | If you're not going to use an ingress controller, you may want to change this value to `LoadBalancer` for production deployments. If running locally, you may want to change it to `NodePort` OR leave it as `ClusterIP` and use `kubectl port-forward` to map a port on the local network interface to the service.                                                                                                                                                                                                                                                                                                                                                                | `ClusterIP`              |
+| `api.service.nodePort`                      | Host port the `Service` will be mapped to when `type` is either `NodePort` or `LoadBalancer`. If not specified, Kubernetes chooses.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `undefined`              |
+| `api.service.annotations`                   | Annotations to add to the API server's service. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | `{}`                     |
 
 ### Controller
 
 | Name                                                               | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Value               |
 | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- |
 | `controller.enabled`                                               | Whether the controller is enabled.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `true`              |
-| `controller.labels`                                                | Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | `{}`                |
-| `controller.annotations`                                           | Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | `{}`                |
-| `controller.podLabels`                                             | Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | `{}`                |
-| `controller.podAnnotations`                                        | Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | `{}`                |
-| `controller.initContainers`                                        | Optional init containers to add to the controller pods. This is rendered as the literal YAML.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | `[]`                |
-| `controller.serviceAccount.iamRole`                                | Specifies the ARN of an AWS IAM role to be used by the controller in an IRSA-enabled EKS cluster.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `""`                |
-| `controller.serviceAccount.clusterWideSecretReadingEnabled`        | Specifies whether the controller's ServiceAccount should be granted read permissions to Secrets CLUSTER-WIDE in the Kargo control plane's cluster. Enabling this is highly discouraged and you do so at your own peril. When this is NOT enabled, the Kargo management controller will dynamically expand and contract the controller's permissions to read Secrets on a Project-by-Project basis.                                                                                                                                                                                                                                                                                                                               | `false`             |
+| `controller.logLevel`                                              | The log level for the controller.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `INFO`              |
+| `controller.shardName`                                             | Set a shard name only if you are running multiple controllers backed by a single underlying control plane. Setting a shard name will cause this controller to operate **only** on resources with a matching shard name. Leaving the shard name undefined will designate this controller as the default controller that is responsible exclusively for resources that are **not** assigned to a specific shard. Leaving this undefined is the correct choice when you are not using sharding at all. It is also the correct setting if you are using sharding and want to designate a controller as the default for handling resources not assigned to a specific shard. In most cases, this setting should simply be left alone. | `undefined`         |
 | `controller.globalCredentials.namespaces`                          | List of namespaces to look for shared credentials. Note that as of v1.0.0, the Kargo controller does not have cluster-wide access to Secrets. The controller receives read-only permission for Secrets on a per-Project basis as Projects are created. If you designate some namespaces as homes for "global" credentials, you will need to manually grant the controller permission to read Secrets in those namespaces.                                                                                                                                                                                                                                                                                                        | `[]`                |
 | `controller.allowCredentialsOverHTTP`                              | Specifies whether the controller should allow credentials (for Git repositories, etc.) to be retrieved and used for operations over HTTP. This is generally discouraged, as it can expose sensitive information. When set to `false`, the controller will only allow credentials to be used over HTTPS (or other secure protocols).                                                                                                                                                                                                                                                                                                                                                                                              | `false`             |
 | `controller.reconcilers.maxConcurrentReconciles`                   | specifies the maximum number of resources EACH of the controller's reconcilers can reconcile concurrently. This setting may also be overridden on a per-reconciler basis.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | `4`                 |
@@ -165,24 +172,57 @@ the Kargo controller is running.
 | `controller.gitClient.email`                                       | Specifies the email of the Kargo controller (used when authoring Git commits).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | `no-reply@kargo.io` |
 | `controller.gitClient.signingKeySecret.name`                       | Specifies the name of an existing `Secret` which contains the Git user's signing key. The value should be accessible under `.data.signingKey` in the same namespace as Kargo. When the signing key is a GPG key, the GPG key's name and email address identity must match the values defined for `controller.gitClient.name` and `controller.gitClient.email`.                                                                                                                                                                                                                                                                                                                                                                   | `""`                |
 | `controller.gitClient.signingKeySecret.type`                       | Specifies the type of the signing key. The currently supported and default option is `gpg`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `""`                |
-| `controller.securityContext`                                       | Security context for controller pods. Defaults to `global.securityContext`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `{}`                |
-| `controller.cabundle.configMapName`                                | Specifies the name of an optional ConfigMap containing CA certs that is managed "out of band." Values in the ConfigMap named here should each contain a single PEM-encoded CA cert. If secretName is also defined, it will take precedence over this field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `""`                |
-| `controller.cabundle.secretName`                                   | Specifies the name of an optional Secret containing CA certs that is managed "out of band." Values in the Secret named here should each contain a single PEM-encoded CA cert. If defined, the value of this field takes precedence over any in configMapName.                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | `""`                |
-| `controller.shardName`                                             | Set a shard name only if you are running multiple controllers backed by a single underlying control plane. Setting a shard name will cause this controller to operate **only** on resources with a matching shard name. Leaving the shard name undefined will designate this controller as the default controller that is responsible exclusively for resources that are **not** assigned to a specific shard. Leaving this undefined is the correct choice when you are not using sharding at all. It is also the correct setting if you are using sharding and want to designate a controller as the default for handling resources not assigned to a specific shard. In most cases, this setting should simply be left alone. | `undefined`         |
 | `controller.argocd.integrationEnabled`                             | Specifies whether Argo CD integration is enabled. When not enabled, the controller will not watch Argo CD Application resources or factor Application health and sync state into determinations of Stage health. Argo CD-based promotion mechanisms will also fail. When enabled, the controller will perform a sanity check at startup. If Argo CD CRDs are not found, the controller will proceed as if this integration had been explicitly disabled. Explicitly disabling is still preferable if this integration is not desired, as it will grant fewer permissions to the controller.                                                                                                                                      | `true`              |
 | `controller.argocd.namespace`                                      | The namespace into which Argo CD is installed.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | `argocd`            |
 | `controller.argocd.watchArgocdNamespaceOnly`                       | Specifies whether the reconciler that watches Argo CD Applications for the sake of forcing related Stages to reconcile should only watch Argo CD Application resources residing in Argo CD's own namespace. Note: Older versions of Argo CD only supported Argo CD Application resources in Argo CD's own namespace, but newer versions support Argo CD Application resources in any namespace. This should usually be left as `false`.                                                                                                                                                                                                                                                                                          | `false`             |
 | `controller.rollouts.integrationEnabled`                           | Specifies whether Argo Rollouts integration is enabled. When not enabled, the controller will not reconcile Argo Rollouts AnalysisRun resources and attempts to verify Stages via Analysis will fail. When enabled, the controller will perform a sanity check at startup. If Argo Rollouts CRDs are not found, the controller will proceed as if this integration had been explicitly disabled. Explicitly disabling is still preferable if this integration is not desired, as it will grant fewer permissions to the controller.                                                                                                                                                                                              | `true`              |
 | `controller.rollouts.controllerInstanceID`                         | Specifies a cluster on which Jobs corresponding to an AnalysisRun (used for Freight/Stage verification purposes) will be executed. This is useful in cases where the cluster hosting the Kargo control plane is not a suitable environment for executing user-defined logic. Kargo will use this as the value of the rgo-rollouts.argoproj.io/controller-instance-id label when creating AnalysisRuns. When this is left empty/undefined, no such label will be added to AnalysisRuns.                                                                                                                                                                                                                                           | `""`                |
-| `controller.logLevel`                                              | The log level for the controller.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `INFO`              |
-| `controller.resources`                                             | Resources limits and requests for the controller containers.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | `{}`                |
+| `controller.labels`                                                | Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | `{}`                |
+| `controller.annotations`                                           | Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | `{}`                |
+| `controller.podLabels`                                             | Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | `{}`                |
+| `controller.podAnnotations`                                        | Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | `{}`                |
+| `controller.serviceAccount.iamRole`                                | Specifies the ARN of an AWS IAM role to be used by the controller in an IRSA-enabled EKS cluster.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | `""`                |
+| `controller.serviceAccount.labels`                                 | Additional labels to add to the controller ServiceAccount.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | `{}`                |
+| `controller.serviceAccount.annotations`                            | Additional annotations to add to the controller ServiceAccount.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  | `{}`                |
+| `controller.serviceAccount.clusterWideSecretReadingEnabled`        | Specifies whether the controller's ServiceAccount should be granted read permissions to Secrets CLUSTER-WIDE in the Kargo control plane's cluster. Enabling this is highly discouraged and you do so at your own peril. When this is NOT enabled, the Kargo management controller will dynamically expand and contract the controller's permissions to read Secrets on a Project-by-Project basis.                                                                                                                                                                                                                                                                                                                               | `false`             |
+| `controller.initContainers`                                        | Optional init containers to add to the controller pods. This is rendered as the literal YAML.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | `[]`                |
+| `controller.env`                                                   | Environment variables to add to controller pods.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `[]`                |
+| `controller.envFrom`                                               | Environment variables to add to controller pods from ConfigMaps or Secrets.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `[]`                |
 | `controller.volumes`                                               | Volumes for the controller pods.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `[]`                |
 | `controller.volumeMounts`                                          | Volume mounts for the controller pods.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           | `[]`                |
+| `controller.resources`                                             | Resources limits and requests for the controller containers.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | `{}`                |
 | `controller.nodeSelector`                                          | Node selector for controller pods. Defaults to `global.nodeSelector`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | `{}`                |
 | `controller.tolerations`                                           | Tolerations for controller pods. Defaults to `global.tolerations`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | `[]`                |
 | `controller.affinity`                                              | Specifies pod affinity for controller pods. Defaults to `global.affinity`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       | `{}`                |
-| `controller.env`                                                   | Environment variables to add to controller pods.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | `[]`                |
-| `controller.envFrom`                                               | Environment variables to add to controller pods from ConfigMaps or Secrets.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `[]`                |
+| `controller.securityContext`                                       | Security context for controller pods. Defaults to `global.securityContext`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `{}`                |
+| `controller.cabundle.configMapName`                                | Specifies the name of an optional ConfigMap containing CA certs that is managed "out of band." Values in the ConfigMap named here should each contain a single PEM-encoded CA cert. If secretName is also defined, it will take precedence over this field.                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | `""`                |
+| `controller.cabundle.secretName`                                   | Specifies the name of an optional Secret containing CA certs that is managed "out of band." Values in the Secret named here should each contain a single PEM-encoded CA cert. If defined, the value of this field takes precedence over any in configMapName.                                                                                                                                                                                                                                                                                                                                                                                                                                                                    | `""`                |
+
+### Garbage Collector
+
+| Name                                          | Description                                                                                                                                                                                                                                                                                                               | Value       |
+| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
+| `garbageCollector.enabled`                    | Whether the garbage collector is enabled.                                                                                                                                                                                                                                                                                 | `true`      |
+| `garbageCollector.logLevel`                   | The log level for the garbage collector.                                                                                                                                                                                                                                                                                  | `INFO`      |
+| `garbageCollector.schedule`                   | When to run the garbage collector.                                                                                                                                                                                                                                                                                        | `0 * * * *` |
+| `garbageCollector.workers`                    | The number of concurrent workers to run. Tuning this too low will result in slow garbage collection. Tuning this too high will result in too many API calls and may result in throttling.                                                                                                                                 | `3`         |
+| `garbageCollector.maxRetainedPromotions`      | The ideal maximum number of Promotions OLDER than the oldest Promotion in a non-terminal phase (for each Stage) that may be spared by the garbage collector. The ACTUAL number of older Promotions spared may exceed this ideal if some Promotions that would otherwise be deleted do not meet the minimum age criterion. | `20`        |
+| `garbageCollector.minPromotionDeletionAge`    | The minimum age a Promotion must be before considered eligible for garbage collection.                                                                                                                                                                                                                                    | `336h`      |
+| `garbageCollector.maxRetainedFreight`         | The ideal maximum number of Freight OLDER than the oldest still in use (from each Warehouse) that may be spared by the garbage collector. The ACTUAL number of older Freight spared may exceed this ideal if some Freight that would otherwise be deleted do not meet the minimum age criterion.                          | `20`        |
+| `garbageCollector.minFreightDeletionAge`      | The minimum age Freight must be before considered eligible for garbage collection.                                                                                                                                                                                                                                        | `336h`      |
+| `garbageCollector.labels`                     | Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.                                                                                                                                                                                                    | `{}`        |
+| `garbageCollector.annotations`                | Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                     | `{}`        |
+| `garbageCollector.podLabels`                  | Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                                                                                                                                                     | `{}`        |
+| `garbageCollector.podAnnotations`             | Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                      | `{}`        |
+| `garbageCollector.serviceAccount.labels`      | Additional labels to add to the managementController ServiceAccount.                                                                                                                                                                                                                                                      | `{}`        |
+| `garbageCollector.serviceAccount.annotations` | Additional annotations to add to the managementController ServiceAccount.                                                                                                                                                                                                                                                 | `{}`        |
+| `garbageCollector.env`                        | Environment variables to add to garbage collector pods.                                                                                                                                                                                                                                                                   | `[]`        |
+| `garbageCollector.envFrom`                    | Environment variables to add to garbage collector pods from ConfigMaps or Secrets.                                                                                                                                                                                                                                        | `[]`        |
+| `garbageCollector.resources`                  | Resources limits and requests for the garbage collector containers.                                                                                                                                                                                                                                                       | `{}`        |
+| `garbageCollector.nodeSelector`               | Node selector for the garbage collector pods. Defaults to `global.nodeSelector`.                                                                                                                                                                                                                                          | `{}`        |
+| `garbageCollector.tolerations`                | Tolerations for the garbage collector pods. Defaults to `global.tolerations`.                                                                                                                                                                                                                                             | `[]`        |
+| `garbageCollector.affinity`                   | Specifies pod affinity for the garbage collector pods. Defaults to `global.affinity`.                                                                                                                                                                                                                                     | `{}`        |
+| `garbageCollector.securityContext`            | Security context for garbage collector pods. Defaults to `global.securityContext`.                                                                                                                                                                                                                                        | `{}`        |
 
 ### External Webhooks Server
 
@@ -196,18 +236,20 @@ the Kargo controller is running.
 | `externalWebhooksServer.annotations`                | Annotations to add to the external webhook server resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                              | `{}`                     |
 | `externalWebhooksServer.podLabels`                  | Optional labels to add to the external webhook server pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                                                                                      | `{}`                     |
 | `externalWebhooksServer.podAnnotations`             | Optional annotations to add to the external webhook server pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                                                                                       | `{}`                     |
+| `externalWebhooksServer.serviceAccount.labels`      | Additional labels to add to the externalWebHooksServer ServiceAccount.                                                                                                                                                                                                                                                                                                                                                                                                 | `{}`                     |
+| `externalWebhooksServer.serviceAccount.annotations` | Additional annotations to add to the externalWebHooksServer ServiceAccount.                                                                                                                                                                                                                                                                                                                                                                                            | `{}`                     |
+| `externalWebhooksServer.env`                        | Environment variables to add to external webhook server pods.                                                                                                                                                                                                                                                                                                                                                                                                          | `[]`                     |
+| `externalWebhooksServer.envFrom`                    | Environment variables to add to external webhook server pods from ConfigMaps or Secrets.                                                                                                                                                                                                                                                                                                                                                                               | `[]`                     |
 | `externalWebhooksServer.resources`                  | Resources limits and requests for the external webhook server containers.                                                                                                                                                                                                                                                                                                                                                                                              | `{}`                     |
 | `externalWebhooksServer.nodeSelector`               | Node selector for external webhook server pods. Defaults to `global.nodeSelector`.                                                                                                                                                                                                                                                                                                                                                                                     | `{}`                     |
 | `externalWebhooksServer.tolerations`                | Tolerations for external webhook server pods. Defaults to `global.tolerations`.                                                                                                                                                                                                                                                                                                                                                                                        | `[]`                     |
 | `externalWebhooksServer.affinity`                   | Specifies pod affinity for external webhook server pods. Defaults to `global.affinity`.                                                                                                                                                                                                                                                                                                                                                                                | `{}`                     |
 | `externalWebhooksServer.securityContext`            | Security context for external webhook server pods. Defaults to `global.securityContext`.                                                                                                                                                                                                                                                                                                                                                                               | `{}`                     |
-| `externalWebhooksServer.env`                        | Environment variables to add to external webhook server pods.                                                                                                                                                                                                                                                                                                                                                                                                          | `[]`                     |
-| `externalWebhooksServer.envFrom`                    | Environment variables to add to external webhook server pods from ConfigMaps or Secrets.                                                                                                                                                                                                                                                                                                                                                                               | `[]`                     |
 | `externalWebhooksServer.probes.enabled`             | Whether liveness and readiness probes should be included in the external webhook server deployment. It is sometimes advantageous to disable these during local development.                                                                                                                                                                                                                                                                                            | `true`                   |
-| `externalWebhooksServer.tls.enabled`                | Whether to enable TLS directly on the external webhook server. This is helpful if you do not intend to use an ingress controller or if you require TLS end-to-end. All other settings in this section EXCEPT `terminatedUpstream` will be ignored when this is set to `false`.                                                                                                                                                                                         | `true`                   |
+| `externalWebhooksServer.tls.enabled`                | Whether to enable TLS directly on the external webhook server. This is helpful if you do not intend to use an ingress controller or if you require TLS end-to-end. All other settings in this section EXCEPT `terminatedUpstream` will be ignored when this is set to `false`.                                                                                                                                                                                         | `false`                  |
 | `externalWebhooksServer.tls.selfSignedCert`         | Whether to generate a self-signed certificate for use by the external webhooks server. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-external-webhook-server-cert` **must** be provided in the same namespace as Kargo.                                                                                                                               | `true`                   |
 | `externalWebhooksServer.tls.terminatedUpstream`     | Whether TLS is terminated upstream, i.e. a load balancer, reverse-proxy, or an Ingress controller using a single wildcard cert is terminating it. Setting this to `true` forces all external webhook server URLs to use HTTPS even if the Ingress (if applicable) or external webhook server itself are listening for plain HTTP requests.                                                                                                                             | `false`                  |
-| `externalWebhooksServer.ingress.enabled`            | Whether to enable ingress by creating an Ingress resource. By default, this is disabled. Enabling ingress is advanced usage.                                                                                                                                                                                                                                                                                                                                           | `false`                  |
+| `externalWebhooksServer.ingress.enabled`            | Whether to enable separate ingress for webhook by creating an Ingress resource. By default, this is disabled and webhook is exposed as part of kargo-api ingress. Enabling ingress is advanced usage.                                                                                                                                                                                                                                                                  | `false`                  |
 | `externalWebhooksServer.ingress.annotations`        | Annotations specified by your ingress controller to customize the behavior of the Ingress resource.                                                                                                                                                                                                                                                                                                                                                                    | `{}`                     |
 | `externalWebhooksServer.ingress.ingressClassName`   | If implemented by your ingress controller, specifies the ingress class. If your ingress controller does not support this, use the `kubernetes.io/ingress.class` annotation instead.                                                                                                                                                                                                                                                                                    | `nil`                    |
 | `externalWebhooksServer.ingress.tls.enabled`        | Whether to associate a certificate with the Ingress resource.                                                                                                                                                                                                                                                                                                                                                                                                          | `true`                   |
@@ -223,69 +265,43 @@ the Kargo controller is running.
 | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------ |
 | `managementController.enabled`                                             | Whether the management controller is enabled.                                                                                                                                        | `true` |
 | `managementController.logLevel`                                            | The log level for the management controller.                                                                                                                                         | `INFO` |
-| `managementController.labels`                                              | Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.                                                               | `{}`   |
-| `managementController.annotations`                                         | Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                | `{}`   |
-| `managementController.podLabels`                                           | Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                | `{}`   |
-| `managementController.podAnnotations`                                      | Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                 | `{}`   |
 | `managementController.reconcilers.maxConcurrentReconciles`                 | specifies the maximum number of resources EACH of the management controller's reconcilers can reconcile concurrently. This setting may also be overridden on a per-reconciler basis. | `4`    |
 | `managementController.reconcilers.namespaces.maxConcurrentReconciles`      | optionally overrides the maximum number of Namespace resources the management controller can reconcile concurrently.                                                                 | `nil`  |
 | `managementController.reconcilers.projects.maxConcurrentReconciles`        | optionally overrides the maximum number of Project resources the management controller can reconcile concurrently.                                                                   | `nil`  |
 | `managementController.reconcilers.serviceAccounts.maxConcurrentReconciles` | optionally overrides the maximum number of ServiceAccount resources the management controller can reconcile concurrently.                                                            | `nil`  |
-| `managementController.securityContext`                                     | Security context for management controller pods. Defaults to `global.securityContext`.                                                                                               | `{}`   |
+| `managementController.labels`                                              | Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.                                                               | `{}`   |
+| `managementController.annotations`                                         | Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                | `{}`   |
+| `managementController.podLabels`                                           | Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                | `{}`   |
+| `managementController.podAnnotations`                                      | Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                 | `{}`   |
+| `managementController.serviceAccount.labels`                               | Additional labels to add to the managementController ServiceAccount.                                                                                                                 | `{}`   |
+| `managementController.serviceAccount.annotations`                          | Additional annotations to add to the managementController ServiceAccount.                                                                                                            | `{}`   |
+| `managementController.env`                                                 | Environment variables to add to management controller pods.                                                                                                                          | `[]`   |
+| `managementController.envFrom`                                             | Environment variables to add to management controller pods from ConfigMaps or Secrets.                                                                                               | `[]`   |
 | `managementController.resources`                                           | Resources limits and requests for the management controller containers.                                                                                                              | `{}`   |
 | `managementController.nodeSelector`                                        | Node selector for management controller pods. Defaults to `global.nodeSelector`.                                                                                                     | `{}`   |
 | `managementController.tolerations`                                         | Tolerations for management controller pods. Defaults to `global.tolerations`.                                                                                                        | `[]`   |
 | `managementController.affinity`                                            | Specifies pod affinity for management controller pods. Defaults to `global.affinity`.                                                                                                | `{}`   |
-| `managementController.env`                                                 | Environment variables to add to management controller pods.                                                                                                                          | `[]`   |
-| `managementController.envFrom`                                             | Environment variables to add to management controller pods from ConfigMaps or Secrets.                                                                                               | `[]`   |
-
-### Webhooks
-
-| Name                | Description                                                                                      | Value  |
-| ------------------- | ------------------------------------------------------------------------------------------------ | ------ |
-| `webhooks.register` | Whether to create `ValidatingWebhookConfiguration` and `MutatingWebhookConfiguration` resources. | `true` |
+| `managementController.securityContext`                                     | Security context for management controller pods. Defaults to `global.securityContext`.                                                                                               | `{}`   |
 
 ### Webhooks Server
 
-| Name                                   | Description                                                                                                                                                                                                                                                                                                                                                                           | Value  |
-| -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
-| `webhooksServer.enabled`               | Whether the webhooks server is enabled.                                                                                                                                                                                                                                                                                                                                               | `true` |
-| `webhooksServer.replicas`              | The number of webhooks server pods.                                                                                                                                                                                                                                                                                                                                                   | `1`    |
-| `webhooksServer.logLevel`              | The log level for the webhooks server.                                                                                                                                                                                                                                                                                                                                                | `INFO` |
-| `webhooksServer.controlplaneUserRegex` | Regular expression for matching controlplane users.                                                                                                                                                                                                                                                                                                                                   | `""`   |
-| `webhooksServer.labels`                | Labels to add to the webhook server resources. Merges with `global.labels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                     | `{}`   |
-| `webhooksServer.annotations`           | Annotations to add to the webhook server resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                      | `{}`   |
-| `webhooksServer.podLabels`             | Optional labels to add to the webhook server pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                              | `{}`   |
-| `webhooksServer.podAnnotations`        | Optional annotations to add to the webhook server pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                               | `{}`   |
-| `webhooksServer.tls.selfSignedCert`    | Whether to generate a self-signed certificate for the controller's built-in webhook server. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-webhooks-server-cert` **must** be provided in the same namespace as Kargo. There is no provision for webhooks without TLS. | `true` |
-| `webhooksServer.resources`             | Resources limits and requests for the webhooks server containers.                                                                                                                                                                                                                                                                                                                     | `{}`   |
-| `webhooksServer.nodeSelector`          | Node selector for the webhooks server pods. Defaults to `global.nodeSelector`.                                                                                                                                                                                                                                                                                                        | `{}`   |
-| `webhooksServer.tolerations`           | Tolerations for the webhooks server pods. Defaults to `global.tolerations`.                                                                                                                                                                                                                                                                                                           | `[]`   |
-| `webhooksServer.affinity`              | Specifies pod affinity for the webhooks server pods. Defaults to `global.affinity`.                                                                                                                                                                                                                                                                                                   | `{}`   |
-| `webhooksServer.securityContext`       | Security context for webhooks server pods. Defaults to `global.securityContext`.                                                                                                                                                                                                                                                                                                      | `{}`   |
-| `webhooksServer.env`                   | Environment variables to add to webhook server pods.                                                                                                                                                                                                                                                                                                                                  | `[]`   |
-| `webhooksServer.envFrom`               | Environment variables to add to webhook server pods from ConfigMaps or Secrets.                                                                                                                                                                                                                                                                                                       | `[]`   |
-
-### Garbage Collector
-
-| Name                                       | Description                                                                                                                                                                                                                                                                                                               | Value       |
-| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
-| `garbageCollector.enabled`                 | Whether the garbage collector is enabled.                                                                                                                                                                                                                                                                                 | `true`      |
-| `garbageCollector.schedule`                | When to run the garbage collector.                                                                                                                                                                                                                                                                                        | `0 * * * *` |
-| `garbageCollector.workers`                 | The number of concurrent workers to run. Tuning this too low will result in slow garbage collection. Tuning this too high will result in too many API calls and may result in throttling.                                                                                                                                 | `3`         |
-| `garbageCollector.maxRetainedPromotions`   | The ideal maximum number of Promotions OLDER than the oldest Promotion in a non-terminal phase (for each Stage) that may be spared by the garbage collector. The ACTUAL number of older Promotions spared may exceed this ideal if some Promotions that would otherwise be deleted do not meet the minimum age criterion. | `20`        |
-| `garbageCollector.minPromotionDeletionAge` | The minimum age a Promotion must be before considered eligible for garbage collection.                                                                                                                                                                                                                                    | `336h`      |
-| `garbageCollector.maxRetainedFreight`      | The ideal maximum number of Freight OLDER than the oldest still in use (from each Warehouse) that may be spared by the garbage collector. The ACTUAL number of older Freight spared may exceed this ideal if some Freight that would otherwise be deleted do not meet the minimum age criterion.                          | `20`        |
-| `garbageCollector.minFreightDeletionAge`   | The minimum age Freight must be before considered eligible for garbage collection.                                                                                                                                                                                                                                        | `336h`      |
-| `garbageCollector.logLevel`                | The log level for the garbage collector.                                                                                                                                                                                                                                                                                  | `INFO`      |
-| `garbageCollector.labels`                  | Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.                                                                                                                                                                                                    | `{}`        |
-| `garbageCollector.annotations`             | Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                     | `{}`        |
-| `garbageCollector.podLabels`               | Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                                                                                                                                                     | `{}`        |
-| `garbageCollector.podAnnotations`          | Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                      | `{}`        |
-| `garbageCollector.resources`               | Resources limits and requests for the garbage collector containers.                                                                                                                                                                                                                                                       | `{}`        |
-| `garbageCollector.nodeSelector`            | Node selector for the garbage collector pods. Defaults to `global.nodeSelector`.                                                                                                                                                                                                                                          | `{}`        |
-| `garbageCollector.tolerations`             | Tolerations for the garbage collector pods. Defaults to `global.tolerations`.                                                                                                                                                                                                                                             | `[]`        |
-| `garbageCollector.affinity`                | Specifies pod affinity for the garbage collector pods. Defaults to `global.affinity`.                                                                                                                                                                                                                                     | `{}`        |
-| `garbageCollector.securityContext`         | Security context for garbage collector pods. Defaults to `global.securityContext`.                                                                                                                                                                                                                                        | `{}`        |
-| `garbageCollector.env`                     | Environment variables to add to garbage collector pods.                                                                                                                                                                                                                                                                   | `[]`        |
-| `garbageCollector.envFrom`                 | Environment variables to add to garbage collector pods from ConfigMaps or Secrets.                                                                                                                                                                                                                                        | `[]`        |
+| Name                                        | Description                                                                                                                                                                                                                                                                                                                                                                           | Value  |
+| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ |
+| `webhooksServer.enabled`                    | Whether the webhooks server is enabled.                                                                                                                                                                                                                                                                                                                                               | `true` |
+| `webhooksServer.replicas`                   | The number of webhooks server pods.                                                                                                                                                                                                                                                                                                                                                   | `1`    |
+| `webhooksServer.logLevel`                   | The log level for the webhooks server.                                                                                                                                                                                                                                                                                                                                                | `INFO` |
+| `webhooksServer.controlplaneUserRegex`      | Regular expression for matching controlplane users.                                                                                                                                                                                                                                                                                                                                   | `""`   |
+| `webhooksServer.labels`                     | Labels to add to the webhook server resources. Merges with `global.labels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                                     | `{}`   |
+| `webhooksServer.annotations`                | Annotations to add to the webhook server resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                                      | `{}`   |
+| `webhooksServer.podLabels`                  | Optional labels to add to the webhook server pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.                                                                                                                                                                                                                                              | `{}`   |
+| `webhooksServer.podAnnotations`             | Optional annotations to add to the webhook server pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.                                                                                                                                                                                                                               | `{}`   |
+| `webhooksServer.serviceAccount.labels`      | Additional labels to add to the webhooks server ServiceAccount.                                                                                                                                                                                                                                                                                                                       | `{}`   |
+| `webhooksServer.serviceAccount.annotations` | Additional annotations to add to the webhooks server ServiceAccount.                                                                                                                                                                                                                                                                                                                  | `{}`   |
+| `webhooksServer.env`                        | Environment variables to add to webhook server pods.                                                                                                                                                                                                                                                                                                                                  | `[]`   |
+| `webhooksServer.envFrom`                    | Environment variables to add to webhook server pods from ConfigMaps or Secrets.                                                                                                                                                                                                                                                                                                       | `[]`   |
+| `webhooksServer.resources`                  | Resources limits and requests for the webhooks server containers.                                                                                                                                                                                                                                                                                                                     | `{}`   |
+| `webhooksServer.nodeSelector`               | Node selector for the webhooks server pods. Defaults to `global.nodeSelector`.                                                                                                                                                                                                                                                                                                        | `{}`   |
+| `webhooksServer.tolerations`                | Tolerations for the webhooks server pods. Defaults to `global.tolerations`.                                                                                                                                                                                                                                                                                                           | `[]`   |
+| `webhooksServer.affinity`                   | Specifies pod affinity for the webhooks server pods. Defaults to `global.affinity`.                                                                                                                                                                                                                                                                                                   | `{}`   |
+| `webhooksServer.securityContext`            | Security context for webhooks server pods. Defaults to `global.securityContext`.                                                                                                                                                                                                                                                                                                      | `{}`   |
+| `webhooksServer.tls.selfSignedCert`         | Whether to generate a self-signed certificate for the controller's built-in webhook server. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-webhooks-server-cert` **must** be provided in the same namespace as Kargo. There is no provision for webhooks without TLS. | `true` |
diff --git a/charts/kargo/resources/crds/kargo.akuity.io_freights.yaml b/charts/kargo/resources/crds/kargo.akuity.io_freights.yaml
index 6e3080d8..f13f8a58 100644
--- a/charts/kargo/resources/crds/kargo.akuity.io_freights.yaml
+++ b/charts/kargo/resources/crds/kargo.akuity.io_freights.yaml
@@ -210,6 +210,14 @@ spec:
                 description: CurrentlyIn describes the Stages in which this Freight
                   is currently in use.
                 type: object
+              metadata:
+                additionalProperties:
+                  x-kubernetes-preserve-unknown-fields: true
+                description: |-
+                  Metadata is a map of arbitrary metadata associated with the Freight.
+                  This is useful for storing additional information about the Freight
+                  or Promotion that can be shared across steps or stages.
+                type: object
               verifiedIn:
                 additionalProperties:
                   description: VerifiedStage describes a Stage in which Freight has
diff --git a/charts/kargo/resources/crds/kargo.akuity.io_projectconfigs.yaml b/charts/kargo/resources/crds/kargo.akuity.io_projectconfigs.yaml
index 6a54a96e..8bb1c865 100644
--- a/charts/kargo/resources/crds/kargo.akuity.io_projectconfigs.yaml
+++ b/charts/kargo/resources/crds/kargo.akuity.io_projectconfigs.yaml
@@ -193,11 +193,7 @@ spec:
                   required:
                   - github
                   type: object
-                maxItems: 5
                 type: array
-                x-kubernetes-validations:
-                - message: WebhookReceiverConfig must have a unique name
-                  rule: self.all(i, size(self.filter(j, i.name == j.name)) == 1)
             type: object
           status:
             description: Status describes the current status of a ProjectConfig.
diff --git a/charts/kargo/resources/crds/kargo.akuity.io_projects.yaml b/charts/kargo/resources/crds/kargo.akuity.io_projects.yaml
index c2318b85..d3afcd18 100644
--- a/charts/kargo/resources/crds/kargo.akuity.io_projects.yaml
+++ b/charts/kargo/resources/crds/kargo.akuity.io_projects.yaml
@@ -198,11 +198,7 @@ spec:
                   required:
                   - github
                   type: object
-                maxItems: 5
                 type: array
-                x-kubernetes-validations:
-                - message: WebhookReceiverConfig must have a unique name
-                  rule: self.all(i, size(self.filter(j, i.name == j.name)) == 1)
             type: object
           status:
             description: Status describes the Project's current status.
diff --git a/charts/kargo/resources/crds/kargo.akuity.io_promotions.yaml b/charts/kargo/resources/crds/kargo.akuity.io_promotions.yaml
index 5aa49509..0fe2c89d 100644
--- a/charts/kargo/resources/crds/kargo.akuity.io_promotions.yaml
+++ b/charts/kargo/resources/crds/kargo.akuity.io_promotions.yaml
@@ -602,6 +602,10 @@ spec:
                             process was started.
                           format: date-time
                           type: string
+                      required:
+                      - id
+                      - phase
+                      - startTime
                       type: object
                     type: array
                 required:
diff --git a/charts/kargo/resources/crds/kargo.akuity.io_stages.yaml b/charts/kargo/resources/crds/kargo.akuity.io_stages.yaml
index eecbff79..9e761e1b 100644
--- a/charts/kargo/resources/crds/kargo.akuity.io_stages.yaml
+++ b/charts/kargo/resources/crds/kargo.akuity.io_stages.yaml
@@ -1007,6 +1007,10 @@ spec:
                                     Verification process was started.
                                   format: date-time
                                   type: string
+                              required:
+                              - id
+                              - phase
+                              - startTime
                               type: object
                             type: array
                         required:
@@ -1319,6 +1323,10 @@ spec:
                               process was started.
                             format: date-time
                             type: string
+                        required:
+                        - id
+                        - phase
+                        - startTime
                         type: object
                       type: array
                   required:
@@ -1863,6 +1871,10 @@ spec:
                                     Verification process was started.
                                   format: date-time
                                   type: string
+                              required:
+                              - id
+                              - phase
+                              - startTime
                               type: object
                             type: array
                         required:
diff --git a/charts/kargo/templates/api/ingress.yaml b/charts/kargo/templates/api/ingress.yaml
index 168340a0..1b7346f4 100644
--- a/charts/kargo/templates/api/ingress.yaml
+++ b/charts/kargo/templates/api/ingress.yaml
@@ -32,6 +32,19 @@ spec:
               {{- else }}
               number: 80
               {{- end }}
+      {{- if and .Values.externalWebhooksServer.enabled (not .Values.externalWebhooksServer.ingress.enabled) }}
+      - pathType: {{ .Values.api.ingress.pathType | default "ImplementationSpecific" }}
+        path: /webhook
+        backend:
+          service:
+            name: kargo-external-webhooks-server
+            port:
+              {{- if .Values.api.tls.enabled }}
+              number: 443
+              {{- else }}
+              number: 80
+              {{- end }}
+      {{- end }}
   {{- if .Values.api.ingress.tls.enabled }}
   tls:
   - hosts:
diff --git a/charts/kargo/templates/api/service-account.yaml b/charts/kargo/templates/api/service-account.yaml
index 81d065aa..48494427 100644
--- a/charts/kargo/templates/api/service-account.yaml
+++ b/charts/kargo/templates/api/service-account.yaml
@@ -7,4 +7,13 @@ metadata:
   labels:
     {{- include "kargo.labels" . | nindent 4 }}
     {{- include "kargo.api.labels" . | nindent 4 }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.labels) .Values.api.serviceAccount.labels) }}
+    {{- range $key, $value := . }}
+    {{ $key }}: {{ $value | quote }}
+    {{- end }}
+  {{- end }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.annotations) .Values.api.serviceAccount.annotations) }}
+  annotations:
+  {{- toYaml . | nindent 4 }}
+  {{- end }}
 {{- end }}
diff --git a/charts/kargo/templates/controller/service-account.yaml b/charts/kargo/templates/controller/service-account.yaml
index aa6659bf..3bb00878 100644
--- a/charts/kargo/templates/controller/service-account.yaml
+++ b/charts/kargo/templates/controller/service-account.yaml
@@ -7,8 +7,18 @@ metadata:
   labels:
     {{- include "kargo.labels" . | nindent 4 }}
     {{- include "kargo.controller.labels" . | nindent 4 }}
-  {{- if .Values.controller.serviceAccount.iamRole }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.labels) .Values.controller.serviceAccount.labels) }}
+    {{- range $key, $value := . }}
+    {{ $key }}: {{ $value | quote }}
+    {{- end }}
+  {{- end }}
+  {{- if (or .Values.global.serviceAccount.annotations .Values.controller.serviceAccount.annotations .Values.controller.serviceAccount.iamRole) }}  
   annotations:
+    {{- if .Values.controller.serviceAccount.iamRole }}
     eks.amazonaws.com/role-arn: {{ .Values.controller.serviceAccount.iamRole }}
+    {{- end }}
+    {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.annotations) .Values.controller.serviceAccount.annotations) }}
+    {{- toYaml . | nindent 4 }}
+    {{- end }}
   {{- end }}
 {{- end }}
diff --git a/charts/kargo/templates/dex-server/service-account.yaml b/charts/kargo/templates/dex-server/service-account.yaml
index b5fbe4b9..f0c0298d 100644
--- a/charts/kargo/templates/dex-server/service-account.yaml
+++ b/charts/kargo/templates/dex-server/service-account.yaml
@@ -7,4 +7,13 @@ metadata:
   labels:
     {{- include "kargo.labels" . | nindent 4 }}
     {{- include "kargo.dexServer.labels" . | nindent 4 }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.labels) .Values.api.oidc.dex.serviceAccount.labels) }}
+    {{- range $key, $value := . }}
+    {{ $key }}: {{ $value | quote }}
+    {{- end }}
+  {{- end }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.annotations) .Values.api.oidc.dex.serviceAccount.annotations) }}
+  annotations:
+  {{- toYaml . | nindent 4 }}
+  {{- end }}
 {{- end }}
diff --git a/charts/kargo/templates/external-webhooks-server/deployment.yaml b/charts/kargo/templates/external-webhooks-server/deployment.yaml
index 23c56a28..820f43f3 100644
--- a/charts/kargo/templates/external-webhooks-server/deployment.yaml
+++ b/charts/kargo/templates/external-webhooks-server/deployment.yaml
@@ -1,4 +1,4 @@
-{{- if .Values.webhooksServer.enabled }}
+{{- if .Values.externalWebhooksServer.enabled }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
diff --git a/charts/kargo/templates/external-webhooks-server/service-account.yaml b/charts/kargo/templates/external-webhooks-server/service-account.yaml
index b4c8cf9f..2ce5eaee 100644
--- a/charts/kargo/templates/external-webhooks-server/service-account.yaml
+++ b/charts/kargo/templates/external-webhooks-server/service-account.yaml
@@ -7,4 +7,13 @@ metadata:
   labels:
     {{- include "kargo.labels" . | nindent 4 }}
     {{- include "kargo.externalWebhooksServer.labels" . | nindent 4 }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.labels) .Values.externalWebhooksServer.serviceAccount.labels) }}
+    {{- range $key, $value := . }}
+    {{ $key }}: {{ $value | quote }}
+    {{- end }}
+  {{- end }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.annotations) .Values.externalWebhooksServer.serviceAccount.annotations) }}
+  annotations:
+  {{- toYaml . | nindent 4 }}
+  {{- end }}
 {{- end }}
diff --git a/charts/kargo/templates/external-webhooks-server/service.yaml b/charts/kargo/templates/external-webhooks-server/service.yaml
index 51ef89b9..566e6613 100644
--- a/charts/kargo/templates/external-webhooks-server/service.yaml
+++ b/charts/kargo/templates/external-webhooks-server/service.yaml
@@ -1,4 +1,4 @@
-{{- if .Values.webhooksServer.enabled }}
+{{- if .Values.externalWebhooksServer.enabled }}
 apiVersion: v1
 kind: Service
 metadata:
diff --git a/charts/kargo/templates/garbage-collector/service-account.yaml b/charts/kargo/templates/garbage-collector/service-account.yaml
index f8cceb44..b35f13c5 100644
--- a/charts/kargo/templates/garbage-collector/service-account.yaml
+++ b/charts/kargo/templates/garbage-collector/service-account.yaml
@@ -7,4 +7,13 @@ metadata:
   labels:
     {{- include "kargo.labels" . | nindent 4 }}
     {{- include "kargo.garbageCollector.labels" . | nindent 4 }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.labels) .Values.garbageCollector.serviceAccount.labels) }}
+    {{- range $key, $value := . }}
+    {{ $key }}: {{ $value | quote }}
+    {{- end }}
+  {{- end }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.annotations) .Values.garbageCollector.serviceAccount.annotations) }}
+  annotations:
+  {{- toYaml . | nindent 4 }}
+  {{- end }}
 {{- end }}
diff --git a/charts/kargo/templates/kubernetes-webhooks-server/service-account.yaml b/charts/kargo/templates/kubernetes-webhooks-server/service-account.yaml
index ed0dbcac..2d62a1f1 100644
--- a/charts/kargo/templates/kubernetes-webhooks-server/service-account.yaml
+++ b/charts/kargo/templates/kubernetes-webhooks-server/service-account.yaml
@@ -7,4 +7,13 @@ metadata:
   labels:
     {{- include "kargo.labels" . | nindent 4 }}
     {{- include "kargo.kubernetesWebhooksServer.labels" . | nindent 4 }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.labels) .Values.webhooksServer.serviceAccount.labels) }}
+    {{- range $key, $value := . }}
+    {{ $key }}: {{ $value | quote }}
+    {{- end }}
+  {{- end }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.annotations) .Values.webhooksServer.serviceAccount.annotations) }}
+  annotations:
+  {{- toYaml . | nindent 4 }}
+  {{- end }}
 {{- end }}
diff --git a/charts/kargo/templates/management-controller/service-account.yaml b/charts/kargo/templates/management-controller/service-account.yaml
index 258dd27d..6a529481 100644
--- a/charts/kargo/templates/management-controller/service-account.yaml
+++ b/charts/kargo/templates/management-controller/service-account.yaml
@@ -7,4 +7,13 @@ metadata:
   labels:
     {{- include "kargo.labels" . | nindent 4 }}
     {{- include "kargo.managementController.labels" . | nindent 4 }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.labels) .Values.managementController.serviceAccount.labels) }}
+    {{- range $key, $value := . }}
+    {{ $key }}: {{ $value | quote }}
+    {{- end }}
+  {{- end }}
+  {{- with (mergeOverwrite (deepCopy .Values.global.serviceAccount.annotations) .Values.managementController.serviceAccount.annotations) }}
+  annotations:
+  {{- toYaml . | nindent 4 }}
+  {{- end }}
 {{- end }}
diff --git a/charts/kargo/values.yaml b/charts/kargo/values.yaml
index 866fdff8..a78aeba3 100755
--- a/charts/kargo/values.yaml
+++ b/charts/kargo/values.yaml
@@ -3,8 +3,38 @@
 ## This is a YAML-formatted file.
 ## Declare variables to be passed into your templates.
 
+## @section Image Parameters
+image:
+  ## @param image.repository Image repository of Kargo
+  repository: ghcr.io/akuity/kargo
+  ## @param image.tag Overrides the image tag. The default tag is the value of `.Chart.AppVersion`
+  tag: ""
+  ## @param image.pullPolicy Image pull policy
+  pullPolicy: IfNotPresent
+  ## @param image.pullSecrets List of imagePullSecrets.
+  pullSecrets: []
+  # - name: regcred
+
 ## @section Global Parameters
 global:
+  ## @param global.labels Labels to add to all resources.
+  labels: {}
+  ## @param global.annotations Annotations to add to all resources.
+  annotations: {}
+  ## @param global.podLabels Labels to add to all pods.
+  podLabels: {}
+  ## @param global.podAnnotations Annotations to add to pods.
+  podAnnotations: {}
+
+  ## ServiceAccount global settings
+  serviceAccount:
+    ## @param global.serviceAccount.labels Global ServiceAccount labels.
+    labels: {}
+    ## @param global.serviceAccount.annotations Global ServiceAccount annotations.
+    annotations: {}
+    # foo: bar
+    # another: value
+
   ## @param global.env Environment variables to add to all Kargo pods.
   env: []
   #  - name: ENV_NAME
@@ -15,18 +45,9 @@ global:
   #      name: config-map-name
   #  - secretRef:
   #      name: secret-name
+  
   ## @param global.nodeSelector Default node selector for all Kargo pods.
   nodeSelector: {}
-
-  ## @param global.labels Labels to add to all resources.
-  labels: {}
-  ## @param global.annotations Annotations to add to all resources.
-  annotations: {}
-  ## @param global.podLabels Labels to add to all pods.
-  podLabels: {}
-  ## @param global.podAnnotations Annotations to add to pods.
-  podAnnotations: {}
-
   ## @param global.tolerations Default tolerations for all Kargo pods.
   tolerations: []
   ## @param global.affinity Default affinity for all Kargo pods.
@@ -34,17 +55,12 @@ global:
   ## @param global.securityContext Default security context for all Kargo pods.
   securityContext: {}
 
-## @section Image Parameters
-image:
-  ## @param image.repository Image repository of Kargo
-  repository: ghcr.io/akuity/kargo
-  ## @param image.tag Overrides the image tag. The default tag is the value of `.Chart.AppVersion`
-  tag: ""
-  ## @param image.pullPolicy Image pull policy
-  pullPolicy: IfNotPresent
-  ## @param image.pullSecrets List of imagePullSecrets.
-  pullSecrets: []
-  # - name: regcred
+## @section CRDs
+crds:
+  ## @param crds.install Indicates if Custom Resource Definitions should be installed and upgraded as part of the release. If set to `false`, the CRDs will only be installed if they do not already exist.
+  install: true
+  ## @param crds.keep Indicates if Custom Resource Definitions should be kept when a release is uninstalled.
+  keep: true
 
 ## @section RBAC
 rbac:
@@ -53,12 +69,10 @@ rbac:
   ## @param rbac.installClusterRoleBindings Indicates if `ClusterRoleBindings` should be installed.
   installClusterRoleBindings: true
 
-## @section CRDs
-crds:
-  ## @param crds.install Indicates if Custom Resource Definitions should be installed and upgraded as part of the release. If set to `false`, the CRDs will only be installed if they do not already exist.
-  install: true
-  ## @param crds.keep Indicates if Custom Resource Definitions should be kept when a release is uninstalled.
-  keep: true
+## @section Webhooks
+webhooks:
+  ## @param webhooks.register Whether to create `ValidatingWebhookConfiguration` and `MutatingWebhookConfiguration` resources.
+  register: true
 
 ## @section KubeConfigs
 ## @descriptionStart
@@ -90,96 +104,22 @@ kubeconfigSecrets: {}
 api:
   ## @param api.enabled Whether the API server is enabled.
   enabled: true
+
   ## @param api.replicas The number of API server pods.
   replicas: 1
+
   ## @param api.host The domain name where Kargo's API server will be accessible. When applicable, this is used for generation of an Ingress resource, certificates, and the OpenID Connect issuer and callback URLs. Note: The value in this field MAY include a port number and MUST NOT specify the protocol (http vs https), which is automatically inferred from other configuration options.
   host: localhost
+
   ## @param api.logLevel The log level for the API server.
   logLevel: INFO
 
-  ## @param api.labels Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.
-  labels: {}
-  ## @param api.annotations Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.
-  annotations: {}
-  ## @param api.podLabels Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.
-  podLabels: {}
-  ## @param api.podAnnotations Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.
-  podAnnotations: {}
-
   ## @param api.secretManagementEnabled Specifies whether Secret management is enabled. This affects the API server's ability to manage repository credentials and other Project-level Secrets, such as those used by AnalysisRuns for verification purposes. If using GitOps to manage Kargo Projects declaratively, the API's Secret management capabilities are not needed and can be disabled to effectively reduce the API server's attackable surface.
   secretManagementEnabled: true
 
-  ## @param api.resources Resources limits and requests for the api containers.
-  resources: {}
-    # limits:
-    #   cpu: 100m
-    #   memory: 128Mi
-    # requests:
-    #   cpu: 100m
-    #   memory: 128Mi
-  ## @param api.nodeSelector Node selector for api pods. Defaults to `global.nodeSelector`.
-  nodeSelector: {}
-  ## @param api.tolerations Tolerations for api pods. Defaults to `global.tolerations`.
-  tolerations: []
-  ## @param api.affinity Specifies pod affinity for api pods. Defaults to `global.affinity`.
-  affinity: {}
-  ## @param api.securityContext Security context for api pods. Defaults to `global.securityContext`.
-  securityContext: {}
-  cabundle:
-    ## @param api.cabundle.configMapName Specifies the name of an optional ConfigMap containing CA certs that is managed "out of band." Values in the ConfigMap named here should each contain a single PEM-encoded CA cert. If secretName is also defined, it will take precedence over this field.
-    configMapName: ""
-    ## @param api.cabundle.secretName Specifies the name of an optional Secret containing CA certs that is managed "out of band." Values in the Secret named here should each contain a single PEM-encoded CA cert. If defined, the value of this field takes precedence over any in configMapName.
-    secretName: ""
-  ## @param api.env Environment variables to add to API server pods.
-  env: []
-  #  - name: ENV_NAME
-  #    value: value
-  ## @param api.envFrom Environment variables to add to API server pods from ConfigMaps or Secrets.
-  envFrom: []
-  #  - configMapRef:
-  #      name: config-map-name
-  #  - secretRef:
-  #      name: secret-name
-
-  probes:
-    ## @param api.probes.enabled Whether liveness and readiness probes should be included in the API server deployment. It is sometimes advantageous to disable these during local development.
-    enabled: true
-
-  tls:
-    ## @param api.tls.enabled Whether to enable TLS directly on the API server. This is helpful if you do not intend to use an ingress controller or if you require TLS end-to-end. All other settings in this section EXCEPT `terminatedUpstream` will be ignored when this is set to `false`.
-    enabled: true
-    ## @param api.tls.selfSignedCert Whether to generate a self-signed certificate for use by the API server. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-api-cert` **must** be provided in the same namespace as Kargo.
-    selfSignedCert: true
-    ## @param api.tls.terminatedUpstream Whether TLS is terminated upstream, i.e. a load balancer, reverse-proxy, or an Ingress controller using a single wildcard cert is terminating it. Setting this to `true` forces all API server URLs to use HTTPS even if the Ingress (if applicable) or API server itself are listening for plain HTTP requests.
-    terminatedUpstream: false
-
   ## @param api.permissiveCORSPolicyEnabled Whether to enable a permissive CORS (Cross Origin Resource Sharing) policy. This is sometimes advantageous during local development, but otherwise, should generally be left disabled.
   permissiveCORSPolicyEnabled: false
 
-  ingress:
-    ## @param api.ingress.enabled Whether to enable ingress by creating an Ingress resource. By default, this is disabled. Enabling ingress is advanced usage.
-    enabled: false
-    ## @param api.ingress.annotations Annotations specified by your ingress controller to customize the behavior of the Ingress resource.
-    annotations: {}
-      # kubernetes.io/ingress.class: nginx
-    ## @param api.ingress.ingressClassName If implemented by your ingress controller, specifies the ingress class. If your ingress controller does not support this, use the `kubernetes.io/ingress.class` annotation instead.
-    ingressClassName:
-    tls:
-      ## @param api.ingress.tls.enabled Whether to associate a certificate with the Ingress resource.
-      enabled: true
-      ## @param api.ingress.tls.selfSignedCert Whether to generate a self-signed certificate for use with the API server's Ingress resource. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-api-ingress-cert` **must** be provided in the same namespace as Kargo. The value in this field has no effect if `api.ingress.tls.enabled` is `false`.
-      selfSignedCert: true
-    ## @param api.ingress.pathType You may want to use `Prefix` for some controllers (like AWS LoadBalancer Ingress controller), which don't support `/` as wildcard path when pathType is set to `ImplementationSpecific`
-    pathType: ImplementationSpecific
-
-  service:
-    ## @param api.service.type If you're not going to use an ingress controller, you may want to change this value to `LoadBalancer` for production deployments. If running locally, you may want to change it to `NodePort` OR leave it as `ClusterIP` and use `kubectl port-forward` to map a port on the local network interface to the service.
-    type: ClusterIP
-    ## @param api.service.nodePort [nullable] Host port the `Service` will be mapped to when `type` is either `NodePort` or `LoadBalancer`. If not specified, Kubernetes chooses.
-    # nodePort:
-    ## @param api.service.annotations Annotations to add to the API server's service. Merges with `global.annotations`, allowing you to override or add to the global annotations.
-    annotations: {}
-
   secret:
     ## @param api.secret.name Specifies the name of an existing Secret which contains the `ADMIN_ACCOUNT_PASSWORD_HASH` and `ADMIN_ACCOUNT_TOKEN_SIGNING_KEY` values. By setting this, the Secret will **not** be generated by Helm.
     name: ""
@@ -202,6 +142,7 @@ api:
     ## Note also: The PKCE code challenge used by Kargo is SHA256 hashed.
     ## For more information about PKCE, please visit: https://oauth.net/2/pkce/
     enabled: false
+
     ## @param api.oidc.issuerURL The issuer URL for the identity provider. If Dex is enabled, this value will be ignored and the issuer URL will be automatically configured. If Dex is not enabled, this should be set to the issuer URL provided to you by your identity provider.
     issuerURL:
     ## @param api.oidc.clientID The client ID for the OIDC client. If Dex is enabled, this value will be ignored and the client ID will be automatically configured. If Dex is not enabled, this should be set to the client ID provided to you by your identity provider.
@@ -211,6 +152,7 @@ api:
     ## @param api.oidc.additionalScopes The additional scopes to send to the OIDC provider. This should be set to the scopes you wish to be provided to your identity provider from clients of Kargo, the scopes openid, profile and email are always requested and don't need to be added, this value is intended for any additional ones you require.
     additionalScopes:
     - groups
+
     ## @param api.oidc.usernameClaim The claim to use as the username for the user.
     usernameClaim: email
 
@@ -257,42 +199,9 @@ api:
         pullSecrets: []
         # - name: regcred
 
-      ## @param api.oidc.dex.env Environment variables to add to Dex server pods. This is convenient for cases where api.oidc.dex.connectors needs to reference environment variables from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.
-      env: []
-      # - name: CLIENT_SECRET
-      #   valueFrom:
-      #     secretKeyRef:
-      #       name: github-dex
-      #       key: dex.github.clientSecret
-      ## @param api.oidc.dex.envFrom Environment variables to add to Dex server pods from ConfigMaps or Secrets. This is especially convenient for cases where api.oidc.dex.connectors needs to reference environment variables from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.
-      envFrom: []
-      #  - configMapRef:
-      #      name: config-map-name
-      #  - secretRef:
-      #      name: secret-name
-
-      ## @param api.oidc.dex.volumes Add additional volumes to Dex pods. This is convenient for cases where api.oidc.dex.connectors needs to reference mounted data from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.
-      volumes: []
-      # - name: google-json
-      #   secret:
-      #     defaultMode: 420
-      #     secretName: kargo-google-groups-json
-
-      ## @param api.oidc.dex.volumeMounts Add additional volume mounts to Dex pods. This is convenient for cases where api.oidc.dex.connectors needs to reference mounted data from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.
-      volumeMounts:
-      # - mountPath: /tmp/oidc
-      #   name: google-json
-      #   readOnly: true
-
-      probes:
-        ## @param api.oidc.dex.probes.enabled Whether liveness and readiness probes should be included in the Dex server deployment. It is sometimes advantageous to disable these during local development.
-        enabled: true
-
-      tls:
-        ## @param api.oidc.dex.tls.selfSignedCert Whether to generate a self-signed certificate for use with Dex. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-dex-server-cert` **must** be provided in the same namespace as Kargo. There is no provision for running Dex without TLS.
-        selfSignedCert: true
       ## @param api.oidc.dex.skipApprovalScreen Whether to skip Dex's own approval screen. Since upstream identity providers will already request user consent, this second approval screen from Dex can be both superfluous and confusing.
       skipApprovalScreen: true
+      
       ## @param api.oidc.dex.connectors Configure [Dex connectors](https://dexidp.io/docs/connectors/) to one or more upstream identity providers.
       connectors: []
       # - id: mock
@@ -324,6 +233,41 @@ api:
       #     redirectURI: <http(s)>://<api.host>/dex/callback
       #     tenant: <tenant ID>
 
+      ## ServiceAccount specific settings
+      serviceAccount:
+        ## @param api.oidc.dex.serviceAccount.labels Additional labels to add to the Dex server ServiceAccount.
+        labels: {}
+        ## @param api.oidc.dex.serviceAccount.annotations Additional annotations to add to the Dex server ServiceAccount.
+        annotations: {}
+        # foo: bar
+        # another: value
+
+      ## @param api.oidc.dex.env Environment variables to add to Dex server pods. This is convenient for cases where api.oidc.dex.connectors needs to reference environment variables from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.
+      env: []
+      # - name: CLIENT_SECRET
+      #   valueFrom:
+      #     secretKeyRef:
+      #       name: github-dex
+      #       key: dex.github.clientSecret
+      ## @param api.oidc.dex.envFrom Environment variables to add to Dex server pods from ConfigMaps or Secrets. This is especially convenient for cases where api.oidc.dex.connectors needs to reference environment variables from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.
+      envFrom: []
+      #  - configMapRef:
+      #      name: config-map-name
+      #  - secretRef:
+      #      name: secret-name
+
+      ## @param api.oidc.dex.volumes Add additional volumes to Dex pods. This is convenient for cases where api.oidc.dex.connectors needs to reference mounted data from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.
+      volumes: []
+      # - name: google-json
+      #   secret:
+      #     defaultMode: 420
+      #     secretName: kargo-google-groups-json
+      ## @param api.oidc.dex.volumeMounts Add additional volume mounts to Dex pods. This is convenient for cases where api.oidc.dex.connectors needs to reference mounted data from a Secret that is managed "out of band" with a secret management solution such as Sealed Secrets.
+      volumeMounts:
+      # - mountPath: /tmp/oidc
+      #   name: google-json
+      #   readOnly: true
+
       ## @param api.oidc.dex.resources Resources limits and requests for the Dex server containers.
       resources: {}
         # limits:
@@ -332,6 +276,7 @@ api:
         # requests:
         #   cpu: 100m
         #   memory: 128Mi
+
       ## @param api.oidc.dex.nodeSelector Node selector for Dex server pods. Defaults to `global.nodeSelector`.
       nodeSelector: {}
       ## @param api.oidc.dex.tolerations Tolerations for Dex server pods. Defaults to `global.tolerations`.
@@ -343,6 +288,14 @@ api:
       ## @param api.oidc.dex.securityContext Security context for Dex server pods. Defaults to `global.securityContext`.
       securityContext: {}
 
+      probes:
+        ## @param api.oidc.dex.probes.enabled Whether liveness and readiness probes should be included in the Dex server deployment. It is sometimes advantageous to disable these during local development.
+        enabled: true
+
+      tls:
+        ## @param api.oidc.dex.tls.selfSignedCert Whether to generate a self-signed certificate for use with Dex. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-dex-server-cert` **must** be provided in the same namespace as Kargo. There is no provision for running Dex without TLS.
+        selfSignedCert: true      
+
   argocd:
     ## @param api.argocd.urls Mapping of Argo CD shards names to URLs to support deep links to Argo CD URLs. If sharding is not used, map the empty string to the single Argo CD URL.
     urls:
@@ -367,44 +320,117 @@ api:
       ## @param api.rollouts.logs.httpHeaders Specifies HTTP headers to include in the HTTP GET request for log retrieval. These are typically used for authentication. The header values support expressions offset by ${{ }}, with the same variables documented for urlTemplate pre-defined and injected with values.
       httpHeaders: {}
 
-## @section Controller
-## All settings for the controller component
-controller:
-  ## @param controller.enabled Whether the controller is enabled.
-  enabled: true
 
-  ## @param controller.labels Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.
+  ## @param api.labels Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.
   labels: {}
-  ## @param controller.annotations Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.
+  ## @param api.annotations Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.
   annotations: {}
-  ## @param controller.podLabels Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.
+  ## @param api.podLabels Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.
   podLabels: {}
-  ## @param controller.podAnnotations Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.
+  ## @param api.podAnnotations Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.
   podAnnotations: {}
-  ## @param controller.initContainers Optional init containers to add to the controller pods. This is rendered as the literal YAML.
-  initContainers: []
-    # - name: download-tools
-    #   image: alpine:3.8
-    #   command: [ sh, -c ]
-    #   args:
-    #    - ls
 
-  ## All settings relating to the service account for the controller
+  ## ServiceAccount specific settings
   serviceAccount:
-    ## @param controller.serviceAccount.iamRole Specifies the ARN of an AWS IAM role to be used by the controller in an IRSA-enabled EKS cluster.
-    iamRole: ""
-    ## @param controller.serviceAccount.clusterWideSecretReadingEnabled Specifies whether the controller's ServiceAccount should be granted read permissions to Secrets CLUSTER-WIDE in the Kargo control plane's cluster. Enabling this is highly discouraged and you do so at your own peril. When this is NOT enabled, the Kargo management controller will dynamically expand and contract the controller's permissions to read Secrets on a Project-by-Project basis.
-    clusterWideSecretReadingEnabled: false
-
-  ## All settings relating to shared credentials (used across multiple kargo projects)
-  globalCredentials:
-    ## @param controller.globalCredentials.namespaces List of namespaces to look for shared credentials. Note that as of v1.0.0, the Kargo controller does not have cluster-wide access to Secrets. The controller receives read-only permission for Secrets on a per-Project basis as Projects are created. If you designate some namespaces as homes for "global" credentials, you will need to manually grant the controller permission to read Secrets in those namespaces.
-    namespaces: []
+    ## @param api.serviceAccount.labels Additional labels to add to the API server ServiceAccount.
+    labels: {}
+    ## @param api.serviceAccount.annotations Additional annotations to add to the API server ServiceAccount.
+    annotations: {}
+    # foo: bar
+    # another: value
 
-  ## @param controller.allowCredentialsOverHTTP Specifies whether the controller should allow credentials (for Git repositories, etc.) to be retrieved and used for operations over HTTP. This is generally discouraged, as it can expose sensitive information. When set to `false`, the controller will only allow credentials to be used over HTTPS (or other secure protocols).
-  allowCredentialsOverHTTP: false
+  ## @param api.env Environment variables to add to API server pods.
+  env: []
+  #  - name: ENV_NAME
+  #    value: value
+  ## @param api.envFrom Environment variables to add to API server pods from ConfigMaps or Secrets.
+  envFrom: []
+  #  - configMapRef:
+  #      name: config-map-name
+  #  - secretRef:
+  #      name: secret-name
 
-  ## Reconciler-specific settings
+  ## @param api.resources Resources limits and requests for the api containers.
+  resources: {}
+    # limits:
+    #   cpu: 100m
+    #   memory: 128Mi
+    # requests:
+    #   cpu: 100m
+    #   memory: 128Mi
+
+  ## @param api.nodeSelector Node selector for api pods. Defaults to `global.nodeSelector`.
+  nodeSelector: {}
+  ## @param api.tolerations Tolerations for api pods. Defaults to `global.tolerations`.
+  tolerations: []
+  ## @param api.affinity Specifies pod affinity for api pods. Defaults to `global.affinity`.
+  affinity: {}
+  ## @param api.securityContext Security context for api pods. Defaults to `global.securityContext`.
+  securityContext: {}
+
+  cabundle:
+    ## @param api.cabundle.configMapName Specifies the name of an optional ConfigMap containing CA certs that is managed "out of band." Values in the ConfigMap named here should each contain a single PEM-encoded CA cert. If secretName is also defined, it will take precedence over this field.
+    configMapName: ""
+    ## @param api.cabundle.secretName Specifies the name of an optional Secret containing CA certs that is managed "out of band." Values in the Secret named here should each contain a single PEM-encoded CA cert. If defined, the value of this field takes precedence over any in configMapName.
+    secretName: ""
+
+  probes:
+    ## @param api.probes.enabled Whether liveness and readiness probes should be included in the API server deployment. It is sometimes advantageous to disable these during local development.
+    enabled: true
+
+  tls:
+    ## @param api.tls.enabled Whether to enable TLS directly on the API server. This is helpful if you do not intend to use an ingress controller or if you require TLS end-to-end. All other settings in this section EXCEPT `terminatedUpstream` will be ignored when this is set to `false`.
+    enabled: true
+    ## @param api.tls.selfSignedCert Whether to generate a self-signed certificate for use by the API server. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-api-cert` **must** be provided in the same namespace as Kargo.
+    selfSignedCert: true
+    ## @param api.tls.terminatedUpstream Whether TLS is terminated upstream, i.e. a load balancer, reverse-proxy, or an Ingress controller using a single wildcard cert is terminating it. Setting this to `true` forces all API server URLs to use HTTPS even if the Ingress (if applicable) or API server itself are listening for plain HTTP requests.
+    terminatedUpstream: false
+
+  ingress:
+    ## @param api.ingress.enabled Whether to enable ingress by creating an Ingress resource. By default, this is disabled. Enabling ingress is advanced usage.
+    enabled: false
+    ## @param api.ingress.annotations Annotations specified by your ingress controller to customize the behavior of the Ingress resource.
+    annotations: {}
+      # kubernetes.io/ingress.class: nginx
+    ## @param api.ingress.ingressClassName If implemented by your ingress controller, specifies the ingress class. If your ingress controller does not support this, use the `kubernetes.io/ingress.class` annotation instead.
+    ingressClassName:
+    tls:
+      ## @param api.ingress.tls.enabled Whether to associate a certificate with the Ingress resource.
+      enabled: true
+      ## @param api.ingress.tls.selfSignedCert Whether to generate a self-signed certificate for use with the API server's Ingress resource. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-api-ingress-cert` **must** be provided in the same namespace as Kargo. The value in this field has no effect if `api.ingress.tls.enabled` is `false`.
+      selfSignedCert: true
+    ## @param api.ingress.pathType You may want to use `Prefix` for some controllers (like AWS LoadBalancer Ingress controller), which don't support `/` as wildcard path when pathType is set to `ImplementationSpecific`
+    pathType: ImplementationSpecific
+
+  service:
+    ## @param api.service.type If you're not going to use an ingress controller, you may want to change this value to `LoadBalancer` for production deployments. If running locally, you may want to change it to `NodePort` OR leave it as `ClusterIP` and use `kubectl port-forward` to map a port on the local network interface to the service.
+    type: ClusterIP
+    ## @param api.service.nodePort [nullable] Host port the `Service` will be mapped to when `type` is either `NodePort` or `LoadBalancer`. If not specified, Kubernetes chooses.
+    # nodePort:
+    ## @param api.service.annotations Annotations to add to the API server's service. Merges with `global.annotations`, allowing you to override or add to the global annotations.
+    annotations: {}
+
+## @section Controller
+## All settings for the controller component
+controller:
+  ## @param controller.enabled Whether the controller is enabled.
+  enabled: true
+
+  ## @param controller.logLevel The log level for the controller.
+  logLevel: INFO
+
+  ## @param controller.shardName [nullable] Set a shard name only if you are running multiple controllers backed by a single underlying control plane. Setting a shard name will cause this controller to operate **only** on resources with a matching shard name. Leaving the shard name undefined will designate this controller as the default controller that is responsible exclusively for resources that are **not** assigned to a specific shard. Leaving this undefined is the correct choice when you are not using sharding at all. It is also the correct setting if you are using sharding and want to designate a controller as the default for handling resources not assigned to a specific shard. In most cases, this setting should simply be left alone.
+  # shardName:
+
+  ## All settings relating to shared credentials (used across multiple kargo projects)
+  globalCredentials:
+    ## @param controller.globalCredentials.namespaces List of namespaces to look for shared credentials. Note that as of v1.0.0, the Kargo controller does not have cluster-wide access to Secrets. The controller receives read-only permission for Secrets on a per-Project basis as Projects are created. If you designate some namespaces as homes for "global" credentials, you will need to manually grant the controller permission to read Secrets in those namespaces.
+    namespaces: []
+
+  ## @param controller.allowCredentialsOverHTTP Specifies whether the controller should allow credentials (for Git repositories, etc.) to be retrieved and used for operations over HTTP. This is generally discouraged, as it can expose sensitive information. When set to `false`, the controller will only allow credentials to be used over HTTPS (or other secure protocols).
+  allowCredentialsOverHTTP: false
+
+  ## Reconciler-specific settings
   reconcilers:
     ## @param controller.reconcilers.maxConcurrentReconciles specifies the maximum number of resources EACH of the controller's reconcilers can reconcile concurrently. This setting may also be overridden on a per-reconciler basis.
     maxConcurrentReconciles: 4
@@ -433,18 +459,6 @@ controller:
       ## @param controller.gitClient.signingKeySecret.type Specifies the type of the signing key. The currently supported and default option is `gpg`.
       type: ""
 
-  ## @param controller.securityContext Security context for controller pods. Defaults to `global.securityContext`.
-  securityContext: {}
-
-  cabundle:
-    ## @param controller.cabundle.configMapName Specifies the name of an optional ConfigMap containing CA certs that is managed "out of band." Values in the ConfigMap named here should each contain a single PEM-encoded CA cert. If secretName is also defined, it will take precedence over this field.
-    configMapName: ""
-    ## @param controller.cabundle.secretName Specifies the name of an optional Secret containing CA certs that is managed "out of band." Values in the Secret named here should each contain a single PEM-encoded CA cert. If defined, the value of this field takes precedence over any in configMapName.
-    secretName: ""
-
-  ## @param controller.shardName [nullable] Set a shard name only if you are running multiple controllers backed by a single underlying control plane. Setting a shard name will cause this controller to operate **only** on resources with a matching shard name. Leaving the shard name undefined will designate this controller as the default controller that is responsible exclusively for resources that are **not** assigned to a specific shard. Leaving this undefined is the correct choice when you are not using sharding at all. It is also the correct setting if you are using sharding and want to designate a controller as the default for handling resources not assigned to a specific shard. In most cases, this setting should simply be left alone.
-  # shardName:
-
   ## All settings relating to the Argo CD control plane this controller might
   ## integrate with.
   argocd:
@@ -463,8 +477,51 @@ controller:
     ## @param controller.rollouts.controllerInstanceID Specifies a cluster on which Jobs corresponding to an AnalysisRun (used for Freight/Stage verification purposes) will be executed. This is useful in cases where the cluster hosting the Kargo control plane is not a suitable environment for executing user-defined logic. Kargo will use this as the value of the rgo-rollouts.argoproj.io/controller-instance-id label when creating AnalysisRuns. When this is left empty/undefined, no such label will be added to AnalysisRuns.
     controllerInstanceID: ""
 
-  ## @param controller.logLevel The log level for the controller.
-  logLevel: INFO
+  ## @param controller.labels Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.
+  labels: {}
+  ## @param controller.annotations Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.
+  annotations: {}
+  ## @param controller.podLabels Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.
+  podLabels: {}
+  ## @param controller.podAnnotations Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.
+  podAnnotations: {}
+
+  ## All settings relating to the service account for the controller
+  serviceAccount:
+    ## @param controller.serviceAccount.iamRole Specifies the ARN of an AWS IAM role to be used by the controller in an IRSA-enabled EKS cluster.
+    iamRole: ""
+    ## @param controller.serviceAccount.labels Additional labels to add to the controller ServiceAccount.
+    labels: {}
+    ## @param controller.serviceAccount.annotations Additional annotations to add to the controller ServiceAccount.
+    annotations: {}
+    # foo: bar
+    # another: value
+    ## @param controller.serviceAccount.clusterWideSecretReadingEnabled Specifies whether the controller's ServiceAccount should be granted read permissions to Secrets CLUSTER-WIDE in the Kargo control plane's cluster. Enabling this is highly discouraged and you do so at your own peril. When this is NOT enabled, the Kargo management controller will dynamically expand and contract the controller's permissions to read Secrets on a Project-by-Project basis.
+    clusterWideSecretReadingEnabled: false
+
+  ## @param controller.initContainers Optional init containers to add to the controller pods. This is rendered as the literal YAML.
+  initContainers: []
+    # - name: download-tools
+    #   image: alpine:3.8
+    #   command: [ sh, -c ]
+    #   args:
+    #    - ls
+
+  ## @param controller.env Environment variables to add to controller pods.
+  env: []
+  #  - name: ENV_NAME
+  #    value: value
+  ## @param controller.envFrom Environment variables to add to controller pods from ConfigMaps or Secrets.
+  envFrom: []
+  #  - configMapRef:
+  #      name: config-map-name
+  #  - secretRef:
+  #      name: secret-name
+
+  ## @param controller.volumes Volumes for the controller pods.
+  volumes: []
+  ## @param controller.volumeMounts Volume mounts for the controller pods.
+  volumeMounts: []
 
   ## @param controller.resources Resources limits and requests for the controller containers.
   resources: {}
@@ -474,35 +531,101 @@ controller:
     # requests:
     #   cpu: 100m
     #   memory: 128Mi
-  ## @param controller.volumes Volumes for the controller pods.
-  volumes: []
-  ## @param controller.volumeMounts Volume mounts for the controller pods.
-  volumeMounts: []
+
   ## @param controller.nodeSelector Node selector for controller pods. Defaults to `global.nodeSelector`.
   nodeSelector: {}
   ## @param controller.tolerations Tolerations for controller pods. Defaults to `global.tolerations`.
   tolerations: []
   ## @param controller.affinity Specifies pod affinity for controller pods. Defaults to `global.affinity`.
   affinity: {}
-  ## @param controller.env Environment variables to add to controller pods.
+  ## @param controller.securityContext Security context for controller pods. Defaults to `global.securityContext`.
+  securityContext: {}
+
+  cabundle:
+    ## @param controller.cabundle.configMapName Specifies the name of an optional ConfigMap containing CA certs that is managed "out of band." Values in the ConfigMap named here should each contain a single PEM-encoded CA cert. If secretName is also defined, it will take precedence over this field.
+    configMapName: ""
+    ## @param controller.cabundle.secretName Specifies the name of an optional Secret containing CA certs that is managed "out of band." Values in the Secret named here should each contain a single PEM-encoded CA cert. If defined, the value of this field takes precedence over any in configMapName.
+    secretName: ""
+
+## @section Garbage Collector
+garbageCollector:
+  ## @param garbageCollector.enabled Whether the garbage collector is enabled.
+  enabled: true
+
+  ## @param garbageCollector.logLevel The log level for the garbage collector.
+  logLevel: INFO
+
+  ## @param garbageCollector.schedule When to run the garbage collector.
+  schedule: "0 * * * *"
+  ## @param garbageCollector.workers The number of concurrent workers to run. Tuning this too low will result in slow garbage collection. Tuning this too high will result in too many API calls and may result in throttling.
+  workers: 3
+  ## @param garbageCollector.maxRetainedPromotions The ideal maximum number of Promotions OLDER than the oldest Promotion in a non-terminal phase (for each Stage) that may be spared by the garbage collector. The ACTUAL number of older Promotions spared may exceed this ideal if some Promotions that would otherwise be deleted do not meet the minimum age criterion.
+  maxRetainedPromotions: 20
+  ## @param garbageCollector.minPromotionDeletionAge The minimum age a Promotion must be before considered eligible for garbage collection.
+  minPromotionDeletionAge: 336h # Two weeks
+  ## @param garbageCollector.maxRetainedFreight The ideal maximum number of Freight OLDER than the oldest still in use (from each Warehouse) that may be spared by the garbage collector. The ACTUAL number of older Freight spared may exceed this ideal if some Freight that would otherwise be deleted do not meet the minimum age criterion.
+  maxRetainedFreight: 20
+  ## @param garbageCollector.minFreightDeletionAge The minimum age Freight must be before considered eligible for garbage collection.
+  minFreightDeletionAge: 336h # Two weeks
+
+  ## @param garbageCollector.labels Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.
+  labels: {}
+  ## @param garbageCollector.annotations Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.
+  annotations: {}
+  ## @param garbageCollector.podLabels Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.
+  podLabels: {}
+  ## @param garbageCollector.podAnnotations Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.
+  podAnnotations: {}
+
+  ## ServiceAccount specific settings
+  serviceAccount:
+    ## @param garbageCollector.serviceAccount.labels Additional labels to add to the managementController ServiceAccount.
+    labels: {}
+    ## @param garbageCollector.serviceAccount.annotations Additional annotations to add to the managementController ServiceAccount.
+    annotations: {}
+    # foo: bar
+    # another: value
+
+  ## @param garbageCollector.env Environment variables to add to garbage collector pods.
   env: []
   #  - name: ENV_NAME
   #    value: value
-  ## @param controller.envFrom Environment variables to add to controller pods from ConfigMaps or Secrets.
+  ## @param garbageCollector.envFrom Environment variables to add to garbage collector pods from ConfigMaps or Secrets.
   envFrom: []
   #  - configMapRef:
   #      name: config-map-name
   #  - secretRef:
   #      name: secret-name
 
+  ## @param garbageCollector.resources Resources limits and requests for the garbage collector containers.
+  resources: {}
+    # limits:
+    #   cpu: 100m
+    #   memory: 128Mi
+    # requests:
+    #   cpu: 100m
+    #   memory: 128Mi
+
+  ## @param garbageCollector.nodeSelector Node selector for the garbage collector pods. Defaults to `global.nodeSelector`.
+  nodeSelector: {}
+  ## @param garbageCollector.tolerations Tolerations for the garbage collector pods. Defaults to `global.tolerations`.
+  tolerations: []
+  ## @param garbageCollector.affinity Specifies pod affinity for the garbage collector pods. Defaults to `global.affinity`.
+  affinity: {}
+  ## @param garbageCollector.securityContext Security context for garbage collector pods. Defaults to `global.securityContext`.
+  securityContext: {}
+
 ## @section External Webhooks Server
 externalWebhooksServer:
   ## @param externalWebhooksServer.enabled Whether the external webhooks server is enabled.
   enabled: true
+
   ## @param externalWebhooksServer.replicas The number of external webhooks server pods.
   replicas: 1
+
   ## @param externalWebhooksServer.host The domain name where Kargo's external webhooks server will be accessible. When applicable, this is used for generation of an Ingress resource and certificates. Note: The value in this field MAY include a port number and MUST NOT specify the protocol (http vs https), which is automatically inferred from other configuration options.
   host: localhost
+
   ## @param externalWebhooksServer.logLevel The log level for the external webhooks server.
   logLevel: INFO
 
@@ -515,6 +638,26 @@ externalWebhooksServer:
   ## @param externalWebhooksServer.podAnnotations Optional annotations to add to the external webhook server pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.
   podAnnotations: {}
 
+  ## ServiceAccount specific settings
+  serviceAccount:
+    ## @param externalWebhooksServer.serviceAccount.labels Additional labels to add to the externalWebHooksServer ServiceAccount.
+    labels: {}
+    ## @param externalWebhooksServer.serviceAccount.annotations Additional annotations to add to the externalWebHooksServer ServiceAccount.
+    annotations: {}
+    # foo: bar
+    # another: value
+
+  ## @param externalWebhooksServer.env Environment variables to add to external webhook server pods.
+  env: []
+  #  - name: ENV_NAME
+  #    value: value
+  ## @param externalWebhooksServer.envFrom Environment variables to add to external webhook server pods from ConfigMaps or Secrets.
+  envFrom: []
+  #  - configMapRef:
+  #      name: config-map-name
+  #  - secretRef:
+  #      name: secret-name
+
   ## @param externalWebhooksServer.resources Resources limits and requests for the external webhook server containers.
   resources: {}
     # limits:
@@ -530,20 +673,8 @@ externalWebhooksServer:
   tolerations: []
   ## @param externalWebhooksServer.affinity Specifies pod affinity for external webhook server pods. Defaults to `global.affinity`.
   affinity: {}
-  
   ## @param externalWebhooksServer.securityContext Security context for external webhook server pods. Defaults to `global.securityContext`.
   securityContext: {}
-  
-  ## @param externalWebhooksServer.env Environment variables to add to external webhook server pods.
-  env: []
-  #  - name: ENV_NAME
-  #    value: value
-  ## @param externalWebhooksServer.envFrom Environment variables to add to external webhook server pods from ConfigMaps or Secrets.
-  envFrom: []
-  #  - configMapRef:
-  #      name: config-map-name
-  #  - secretRef:
-  #      name: secret-name
 
   probes:
     ## @param externalWebhooksServer.probes.enabled Whether liveness and readiness probes should be included in the external webhook server deployment. It is sometimes advantageous to disable these during local development.
@@ -551,14 +682,14 @@ externalWebhooksServer:
 
   tls:
     ## @param externalWebhooksServer.tls.enabled Whether to enable TLS directly on the external webhook server. This is helpful if you do not intend to use an ingress controller or if you require TLS end-to-end. All other settings in this section EXCEPT `terminatedUpstream` will be ignored when this is set to `false`.
-    enabled: true
+    enabled: false
     ## @param externalWebhooksServer.tls.selfSignedCert Whether to generate a self-signed certificate for use by the external webhooks server. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-external-webhook-server-cert` **must** be provided in the same namespace as Kargo.
     selfSignedCert: true
     ## @param externalWebhooksServer.tls.terminatedUpstream Whether TLS is terminated upstream, i.e. a load balancer, reverse-proxy, or an Ingress controller using a single wildcard cert is terminating it. Setting this to `true` forces all external webhook server URLs to use HTTPS even if the Ingress (if applicable) or external webhook server itself are listening for plain HTTP requests.
     terminatedUpstream: false
 
   ingress:
-    ## @param externalWebhooksServer.ingress.enabled Whether to enable ingress by creating an Ingress resource. By default, this is disabled. Enabling ingress is advanced usage.
+    ## @param externalWebhooksServer.ingress.enabled Whether to enable separate ingress for webhook by creating an Ingress resource. By default, this is disabled and webhook is exposed as part of kargo-api ingress. Enabling ingress is advanced usage.
     enabled: false
     ## @param externalWebhooksServer.ingress.annotations Annotations specified by your ingress controller to customize the behavior of the Ingress resource.
     annotations: {}
@@ -590,15 +721,6 @@ managementController:
   ## @param managementController.logLevel The log level for the management controller.
   logLevel: INFO
 
-  ## @param managementController.labels Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.
-  labels: {}
-  ## @param managementController.annotations Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.
-  annotations: {}
-  ## @param managementController.podLabels Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.
-  podLabels: {}
-  ## @param managementController.podAnnotations Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.
-  podAnnotations: {}
-
   ## Reconciler-specific settings
   reconcilers:
     ## @param managementController.reconcilers.maxConcurrentReconciles specifies the maximum number of resources EACH of the management controller's reconcilers can reconcile concurrently. This setting may also be overridden on a per-reconciler basis.
@@ -613,8 +735,34 @@ managementController:
       ## @param managementController.reconcilers.serviceAccounts.maxConcurrentReconciles optionally overrides the maximum number of ServiceAccount resources the management controller can reconcile concurrently.
       maxConcurrentReconciles:
 
-  ## @param managementController.securityContext Security context for management controller pods. Defaults to `global.securityContext`.
-  securityContext: {}
+  ## @param managementController.labels Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.
+  labels: {}
+  ## @param managementController.annotations Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.
+  annotations: {}
+  ## @param managementController.podLabels Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.
+  podLabels: {}
+  ## @param managementController.podAnnotations Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.
+  podAnnotations: {}
+
+  ## ServiceAccount specific settings
+  serviceAccount:
+    ## @param managementController.serviceAccount.labels Additional labels to add to the managementController ServiceAccount.
+    labels: {}
+    ## @param managementController.serviceAccount.annotations Additional annotations to add to the managementController ServiceAccount.
+    annotations: {}
+    # foo: bar
+    # another: value
+
+  ## @param managementController.env Environment variables to add to management controller pods.
+  env: []
+  #  - name: ENV_NAME
+  #    value: value
+  ## @param managementController.envFrom Environment variables to add to management controller pods from ConfigMaps or Secrets.
+  envFrom: []
+  #  - configMapRef:
+  #      name: config-map-name
+  #  - secretRef:
+  #      name: secret-name
 
   ## @param managementController.resources Resources limits and requests for the management controller containers.
   resources: {}
@@ -624,36 +772,27 @@ managementController:
     # requests:
     #   cpu: 100m
     #   memory: 128Mi
+
   ## @param managementController.nodeSelector Node selector for management controller pods. Defaults to `global.nodeSelector`.
   nodeSelector: {}
   ## @param managementController.tolerations Tolerations for management controller pods. Defaults to `global.tolerations`.
   tolerations: []
   ## @param managementController.affinity Specifies pod affinity for management controller pods. Defaults to `global.affinity`.
   affinity: {}
-  ## @param managementController.env Environment variables to add to management controller pods.
-  env: []
-  #  - name: ENV_NAME
-  #    value: value
-  ## @param managementController.envFrom Environment variables to add to management controller pods from ConfigMaps or Secrets.
-  envFrom: []
-  #  - configMapRef:
-  #      name: config-map-name
-  #  - secretRef:
-  #      name: secret-name
-
-## @section Webhooks
-webhooks:
-  ## @param webhooks.register Whether to create `ValidatingWebhookConfiguration` and `MutatingWebhookConfiguration` resources.
-  register: true
+  ## @param managementController.securityContext Security context for management controller pods. Defaults to `global.securityContext`.
+  securityContext: {}
 
 ## @section Webhooks Server
 webhooksServer:
   ## @param webhooksServer.enabled Whether the webhooks server is enabled.
   enabled: true
+
   ## @param webhooksServer.replicas The number of webhooks server pods.
   replicas: 1
+  
   ## @param webhooksServer.logLevel The log level for the webhooks server.
   logLevel: INFO
+  
   ## @param webhooksServer.controlplaneUserRegex Regular expression for matching controlplane users.
   controlplaneUserRegex: "" # ^system:serviceaccount:kargo:[a-z0-9]([-a-z0-9]*[a-z0-9])?$
 
@@ -666,26 +805,15 @@ webhooksServer:
   ## @param webhooksServer.podAnnotations Optional annotations to add to the webhook server pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.
   podAnnotations: {}
 
-  tls:
-    ## @param webhooksServer.tls.selfSignedCert  Whether to generate a self-signed certificate for the controller's built-in webhook server. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-webhooks-server-cert` **must** be provided in the same namespace as Kargo. There is no provision for webhooks without TLS.
-    selfSignedCert: true
+  ## ServiceAccount specific settings
+  serviceAccount:
+    ## @param webhooksServer.serviceAccount.labels Additional labels to add to the webhooks server ServiceAccount.
+    labels: {}
+    ## @param webhooksServer.serviceAccount.annotations Additional annotations to add to the webhooks server ServiceAccount.
+    annotations: {}
+    # foo: bar
+    # another: value
 
-  ## @param webhooksServer.resources Resources limits and requests for the webhooks server containers.
-  resources: {}
-    # limits:
-    #   cpu: 100m
-    #   memory: 128Mi
-    # requests:
-    #   cpu: 100m
-    #   memory: 128Mi
-  ## @param webhooksServer.nodeSelector Node selector for the webhooks server pods. Defaults to `global.nodeSelector`.
-  nodeSelector: {}
-  ## @param webhooksServer.tolerations Tolerations for the webhooks server pods. Defaults to `global.tolerations`.
-  tolerations: []
-  ## @param webhooksServer.affinity Specifies pod affinity for the webhooks server pods. Defaults to `global.affinity`.
-  affinity: {}
-  ## @param webhooksServer.securityContext Security context for webhooks server pods. Defaults to `global.securityContext`.
-  securityContext: {}
   ## @param webhooksServer.env Environment variables to add to webhook server pods.
   env: []
   #  - name: ENV_NAME
@@ -697,35 +825,7 @@ webhooksServer:
   #  - secretRef:
   #      name: secret-name
 
-## @section Garbage Collector
-garbageCollector:
-  ## @param garbageCollector.enabled Whether the garbage collector is enabled.
-  enabled: true
-  ## @param garbageCollector.schedule When to run the garbage collector.
-  schedule: "0 * * * *"
-  ## @param garbageCollector.workers The number of concurrent workers to run. Tuning this too low will result in slow garbage collection. Tuning this too high will result in too many API calls and may result in throttling.
-  workers: 3
-  ## @param garbageCollector.maxRetainedPromotions The ideal maximum number of Promotions OLDER than the oldest Promotion in a non-terminal phase (for each Stage) that may be spared by the garbage collector. The ACTUAL number of older Promotions spared may exceed this ideal if some Promotions that would otherwise be deleted do not meet the minimum age criterion.
-  maxRetainedPromotions: 20
-  ## @param garbageCollector.minPromotionDeletionAge The minimum age a Promotion must be before considered eligible for garbage collection.
-  minPromotionDeletionAge: 336h # Two weeks
-  ## @param garbageCollector.maxRetainedFreight The ideal maximum number of Freight OLDER than the oldest still in use (from each Warehouse) that may be spared by the garbage collector. The ACTUAL number of older Freight spared may exceed this ideal if some Freight that would otherwise be deleted do not meet the minimum age criterion.
-  maxRetainedFreight: 20
-  ## @param garbageCollector.minFreightDeletionAge The minimum age Freight must be before considered eligible for garbage collection.
-  minFreightDeletionAge: 336h # Two weeks
-  ## @param garbageCollector.logLevel The log level for the garbage collector.
-  logLevel: INFO
-
-  ## @param garbageCollector.labels Labels to add to the api resources. Merges with `global.labels`, allowing you to override or add to the global labels.
-  labels: {}
-  ## @param garbageCollector.annotations Annotations to add to the api resources. Merges with `global.annotations`, allowing you to override or add to the global annotations.
-  annotations: {}
-  ## @param garbageCollector.podLabels Optional labels to add to pods. Merges with `global.podLabels`, allowing you to override or add to the global labels.
-  podLabels: {}
-  ## @param garbageCollector.podAnnotations Optional annotations to add to pods. Merges with `global.podAnnotations`, allowing you to override or add to the global annotations.
-  podAnnotations: {}
-
-  ## @param garbageCollector.resources Resources limits and requests for the garbage collector containers.
+  ## @param webhooksServer.resources Resources limits and requests for the webhooks server containers.
   resources: {}
     # limits:
     #   cpu: 100m
@@ -733,21 +833,16 @@ garbageCollector:
     # requests:
     #   cpu: 100m
     #   memory: 128Mi
-  ## @param garbageCollector.nodeSelector Node selector for the garbage collector pods. Defaults to `global.nodeSelector`.
+
+  ## @param webhooksServer.nodeSelector Node selector for the webhooks server pods. Defaults to `global.nodeSelector`.
   nodeSelector: {}
-  ## @param garbageCollector.tolerations Tolerations for the garbage collector pods. Defaults to `global.tolerations`.
+  ## @param webhooksServer.tolerations Tolerations for the webhooks server pods. Defaults to `global.tolerations`.
   tolerations: []
-  ## @param garbageCollector.affinity Specifies pod affinity for the garbage collector pods. Defaults to `global.affinity`.
+  ## @param webhooksServer.affinity Specifies pod affinity for the webhooks server pods. Defaults to `global.affinity`.
   affinity: {}
-  ## @param garbageCollector.securityContext Security context for garbage collector pods. Defaults to `global.securityContext`.
+  ## @param webhooksServer.securityContext Security context for webhooks server pods. Defaults to `global.securityContext`.
   securityContext: {}
-  ## @param garbageCollector.env Environment variables to add to garbage collector pods.
-  env: []
-  #  - name: ENV_NAME
-  #    value: value
-  ## @param garbageCollector.envFrom Environment variables to add to garbage collector pods from ConfigMaps or Secrets.
-  envFrom: []
-  #  - configMapRef:
-  #      name: config-map-name
-  #  - secretRef:
-  #      name: secret-name
+
+  tls:
+    ## @param webhooksServer.tls.selfSignedCert  Whether to generate a self-signed certificate for the controller's built-in webhook server. If `true`, `cert-manager` CRDs **must** be present in the cluster. Kargo will create and use its own namespaced issuer. If `false`, a cert secret named `kargo-webhooks-server-cert` **must** be provided in the same namespace as Kargo. There is no provision for webhooks without TLS.
+    selfSignedCert: true
diff --git a/docs/docs/100-roadmap.md b/docs/docs/100-roadmap.md
index 99faf16b..c358e386 100644
--- a/docs/docs/100-roadmap.md
+++ b/docs/docs/100-roadmap.md
@@ -17,28 +17,38 @@ Project](https://github.com/akuity/kargo/milestones)
 
 ## In Progress
 
-### v1.3.0
+### v1.6.0
 
 __Expected:__ TBD
 
-v1.3.0 will focus on isolating user-defined promotion processes (or possibly
-individual steps thereof) in their own ephemeral execution environments. This
-is intended to pave the way for eventual support for custom/third-party
-promotion steps.
+The anchor feature of v1.6.0 is support for triggering applicable Warehouses to
+execute their artifact discovery process in response to inbound webhooks from
+source code and container image repositories. Initial support will favor the
+most popular platforms, such as GitHub, GitLab, Docker Hub and Quay.io, although
+support for many others will be phased in over time.
 
-## Upcoming
+## Completed
 
-### v1.4.0 and Beyond
+### v1.5.0
 
-__Expected:__ TBD
+This release focused on UI improvement and streamlining `Project` configuration
+through the introduction of a new `ProjectConfig` resource type. Conditional
+execution of promotion steps was also improved such that the success or failure
+of previous steps can be taken into account. This permits users to define
+more robust promotion processes by defining steps that conditionally execute
+error recovery or cleanup.
 
-v1.4.0 will leverage the improved isolation of user-defined promotion processes
-to begin supporting custom/third-party promotion steps. This work will include
-establishing a formal specification for developers wishing to implement their
-own promotion steps, as well as building the mechanisms whereby operators may
-install and users may leverage such promotion steps.
+### v1.4.0
 
-## Completed
+This release focused on small QOL improvements, notably access to logs generated
+by `Job` metrics as part of verification processes and support for enriching
+`Freight` with additional metadata by reading OIDC annotations from container
+images.
+
+### v1.3.0
+
+This release focused primarily on introducing support for conditional execution
+of promotion steps as determined by user-defined expressions.
 
 ### v1.2.0
 
diff --git a/docs/docs/50-user-guide/60-reference-docs/30-promotion-steps/git-clone.md b/docs/docs/50-user-guide/60-reference-docs/30-promotion-steps/git-clone.md
index 477367e1..1f5fa530 100644
--- a/docs/docs/50-user-guide/60-reference-docs/30-promotion-steps/git-clone.md
+++ b/docs/docs/50-user-guide/60-reference-docs/30-promotion-steps/git-clone.md
@@ -29,11 +29,18 @@ multiple working trees.
 | `author.email` | `string` | Y | The committer's email address. |
 | `author.signingKey` | `string` | N | The GPG signing key for the author. This field is optional. |
 | `checkout` | `[]object` | Y | The commits, branches, or tags to check out from the repository and the paths where they should be checked out. At least one must be specified. |
+| `checkout[].as` | `string` | N | Used as the key in the `commits` output map. If not specified, the value of the `path` field is used as a key instead. Providing a value for this field is useful when expressions in downstream steps may need to reference specific commits checked out by this step. |
 | `checkout[].branch` | `string` | N | A branch to check out. Mutually exclusive with `commit` and `tag`. If none of these is specified, the default branch will be checked out. |
 | `checkout[].create` | `boolean` | N | In the event `branch` does not already exist on the remote, whether a new, empty, orphaned branch should be created. Default is `false`, but should commonly be set to `true` for Stage-specific branches, which may not exist yet at the time of a Stage's first promotion. |
 | `checkout[].commit` | `string` | N | A specific commit to check out. Mutually exclusive with `branch` and `tag`. If none of these is specified, the default branch will be checked out. |
-| `checkout[].tag` | `string` | N | A tag to check out. Mutually exclusive with `branch` and `commit`. If none of these is specified, the default branch will be checked out. |
 | `checkout[].path` | `string` | Y | The path for a working tree that will be created from the checked out revision. This path is relative to the temporary workspace that Kargo provisions for use by the promotion process. |
+| `checkout[].tag` | `string` | N | A tag to check out. Mutually exclusive with `branch` and `commit`. If none of these is specified, the default branch will be checked out. |
+
+## Output
+
+| Name    | Type                | Description                                                                                      |
+|---------|---------------------|--------------------------------------------------------------------------------------------------|
+| `commits` | `map[string]string` | A map of checkout keys (either the `as` alias or the `path` if `as` is not set) to the HEAD commit hash checked out at each path. This allows downstream steps to reference the exact commit checked out for each working tree. |
 
 ## Examples
 
diff --git a/docs/docs/50-user-guide/60-reference-docs/30-promotion-steps/git-commit.md b/docs/docs/50-user-guide/60-reference-docs/30-promotion-steps/git-commit.md
index abcb5d42..3f7cb704 100644
--- a/docs/docs/50-user-guide/60-reference-docs/30-promotion-steps/git-commit.md
+++ b/docs/docs/50-user-guide/60-reference-docs/30-promotion-steps/git-commit.md
@@ -5,9 +5,11 @@ description: Commits all changes in a working tree to its checked out branch.
 
 # `git-commit`
 
-`git-commit` commits all changes in a working tree to its checked out branch.
-This step is often used after previous steps have put the working tree into the
-desired state and is commonly followed by a [`git-push` step](git-push.md).
+`git-commit` commits all changes in a working tree to its checked out branch. If
+there are no differences from the current `HEAD`, this step is skipped and no
+new commit is created. This step is often used after previous steps have put the
+working tree into the desired state and is commonly followed by a [`git-push`
+step](git-push.md).
 
 ## Configuration
 
diff --git a/docs/docs/50-user-guide/60-reference-docs/40-expressions.md b/docs/docs/50-user-guide/60-reference-docs/40-expressions.md
index 0e7c44c3..f12abded 100644
--- a/docs/docs/50-user-guide/60-reference-docs/40-expressions.md
+++ b/docs/docs/50-user-guide/60-reference-docs/40-expressions.md
@@ -155,12 +155,16 @@ The `ctx` object has the following structure:
 
 ```console
 ctx
-├── project: string       # The name of the Project
-├── stage: string         # The name of the Stage
-├── promotion: string     # The name of the Promotion
+├── project: string           # The name of the Project
+├── stage: string             # The name of the Stage
+├── promotion: string         # The name of the Promotion
+├── targetFreight
+│   ├── name: string          # The name of the Freight that is initiated this Promotion
+│   └── origin
+│       └── name: string      # The name of the Warehouse that contains the Freight
 └── meta
     └── promotion
-        └── actor: string # The creator of the Promotion
+        └── actor: string     # The creator of the Promotion
 ```
 
 The following example promotion process clones a repository and checks out
diff --git a/docs/docs/60-contributor-guide/10-hacking-on-kargo.md b/docs/docs/60-contributor-guide/10-hacking-on-kargo.md
index d87bb50e..2c7ed9f9 100644
--- a/docs/docs/60-contributor-guide/10-hacking-on-kargo.md
+++ b/docs/docs/60-contributor-guide/10-hacking-on-kargo.md
@@ -302,11 +302,13 @@ as native processes.
 
     If you have a paid ngrok account that allows you to use a custom domain name
     for your tunnels, you can specify that domain name using the
-    `KARGO_EXTERNAL_WEBHOOKS_SERVER_HOSTNAME` environment variable before running
-    the make target:
+    `KARGO_EXTERNAL_WEBHOOKS_SERVER_HOSTNAME` and (to properly set the protocol
+    scheme) `KARGO_EXTERNAL_WEBHOOKS_SERVER_TLS_TERMINATED_UPSTREAM` environment
+    variables before running the make target:
 
     ```shell
     export KARGO_EXTERNAL_WEBHOOKS_SERVER_HOSTNAME=my-tunnel.ngrok.io
+    export KARGO_EXTERNAL_WEBHOOKS_SERVER_TLS_TERMINATED_UPSTREAM=true
     make hack-ngrok
     ```
 
diff --git a/docs/docs/80-release-notes/94-v1.5.0.md b/docs/docs/80-release-notes/94-v1.5.0.md
new file mode 100644
index 00000000..a2595ffe
--- /dev/null
+++ b/docs/docs/80-release-notes/94-v1.5.0.md
@@ -0,0 +1,103 @@
+The Kargo team is excited to announce one of our biggest releases to date -- Kargo v1.5.0!
+
+Users will notice right away that the UI has been overhauled with extensive improvements to both usability and responsiveness, but this release has a lot of new and improved functionality under the hood as well, so let's dive in!
+
+## 🆕 What's New? {#whats-new}
+
+So many quality-of-life improvements have landed in v1.5.0 that we cannot give individual attention to each one, but here are some of the highlights:
+
+- Improved Project configuration:
+
+  The `Project` CRD has historically had a `spec` field for defining Project configuration. In practice, this has been awkward to work with. Permission to perform various operations on Project-level resources (e.g. `Warehouses`, `Stages`, etc.) is managed at the _Project-level_ (i.e. with `ServiceAccount`s, `Role`s, and `RoleBinding`s in the Project's namespace). With `Project` resources themselves being _cluster-scoped_, permissions to update or delete them could not, themselves, be managed at the Project-level -- a quirk which has typically necessitated a Kargo _(system-level)_ admin to either manually assign additional permissions to Project admins or else perform `Project` update or delete operations on their behalf.
+
+  To eliminate some of this friction, Kargo v1.5.0 introduces a new _namespaced_ [`ProjectConfig` CRD](../50-user-guide/20-how-to-guides/20-working-with-projects.md#project-configuration). Being namespaced (Project-level), permissions to update or delete Project configuration can now be managed at the Project-level itself, and in fact, such permissions are now automatically granted to the Project-level `kargo-admin` `Role` when a `Project` and its associated namespace are created.
+
+  The difficulty of self-service Project deletion for Project admins remains unresolved, but we anticipate this to be addressed in an upcoming release and in the meantime, eliminating the need for Project admins to lean on Kargo admins for Project configuration updates should be a welcome improvement.
+
+  In concert with this improvement, the definition of promotion policies has moved from the `Project` CRD to the `ProjectConfig` CRD _and_ received a large upgrade in the form of support for matching policies to Stages using patterns and label selectors.
+
+  Note that upgrading to Kargo v1.5.0 from an earlier version will automatically migrate existing promotion policies from `Project` resources to new `ProjectConfig` resources. The `Project` CRD's `spec` field will be removed entirely in Kargo v1.7.0.
+
+- Improved bookkeeping for skipped promotion steps:
+
+  Until now, when conditions specified using an expression in a promotion step's `if` field evaluated to `false`, causing a step to be skipped, the step was recorded as a success, despite never having executed.
+
+  Beginning in Kargo v1.5.0, skipped promotion steps are explicitly marked as such.
+
+- Enhanced conditional promotion step execution:
+
+  Kargo v1.5.0 also makes it possible to incorporate [the cumulative result of prior promotion steps](../50-user-guide/60-reference-docs/15-promotion-templates.md#conditional-steps) into conditions specified using an expression in a step's `if` field.
+
+  - `always()` unconditionally evaluates to `true`, thus an expression like `${{ always() }}` will permit a step to execute regardless of the outcome of prior steps. This is useful for implementing steps that contain cleanup logic.
+
+  - `failure()` evaluates to `true` if _any_ prior step has errored or failed, thus an expression like `${{ failure() }}` will permit a step to execute _only_ under those circumstances. This is useful for implementing steps that contain error-handling logic.
+
+  - `success()` evaluates to `true` if _all_ prior steps have either succeeded or been skipped, thus an expression like `${{ success() }}` will permit a step to execute _only_ under those circumstances.
+
+  - When a step's `if` field is omitted, the step's execution is contingent on all prior steps having either succeeded or having been skipped, as if the `if` field had contained the expression `${{ success() }}`.
+
+  - It is also possible to directly access the status of an individual step using the `status()` function, thus an expression like `${{ status("my-name") == 'Errored' }}` will permit a step to execute _only_ if the step named `my-name` has errored.
+
+  - Last, a new `continueOnError` field can be set to `true` to specify that a step that errors or fails should neither affect the evaluation of the `failure()` and `success()` functions as described above nor influence the overall outcome of the promotion.
+
+- `ConfigMap` access and improved `Secret` access in expressions:
+
+  `ConfigMap` and `Secret` resources within a Project's namespace can now be accessed within expressions using new [`config()`](../50-user-guide/60-reference-docs/40-expressions.md#configmapname) and [`secret()`](../50-user-guide/60-reference-docs/40-expressions.md#secretname) functions, respectively. For example, `${{ configMap('my-config').foo }}` will access the value of the `foo` key in the `my-config` `ConfigMap`. `${{secret('my-secret').bar }}` will access the value of the `bar` key in the in the `my-secret` `Secret`.
+
+  Note the `secret()` function performs better than and replaces the existing `secret` map currently available in expressions, which has been deprecated and will be removed completely in Kargo v1.7.0.
+
+- Improved Workload Identity Federation support in GKE:
+
+  Those running Kargo within Elastic Kubernetes Service (EKS) or Google Kubernetes Engine (GKE) may be familiar with Kargo's ability to use [ambient credentials](../40-operator-guide/40-security/40-managing-credentials.md#ambient-credentials) when accessing Elastic Container Registry (ECR) and Google Artifact Registry (GAR) repositories, respectively.
+
+  For both of these, being a multi-tenant system, and in observance of the principle of least privilege, Kargo attempts to assume/impersonate Project-specific IAM roles/identities when accessing ECR and GAR repositories. On EKS, any failure to do so has automatically fallen back on attempting access using the controller's own ambient credentials directly. This has permitted administrators who may find it onerous to maintain Project-specific roles to grant permissions _directly_ to the controller if strict adherence to the principle of least privilege is not a concern. On GKE, however, this option has not previously been available. Beginning in Kargo v1.5.0, it is, thanks to the diligent efforts of @kacpercesarz98!
+
+## New and Updated Promotion Steps {#new-and-updated-promotion-steps}
+
+- The `git-clone` and `git-commit` promotion steps both now support the ability to specify optional authorship information, including signing keys, if so desired. When specified in either of these steps, that information overrides system-level defaults. Specifying authorship information in the `git-clone` step is a convenient way to extend that information to _all_ commits made to a repository by subsequent `git-commit` steps. Any authorship information specified in a `git-commit` step takes precedence over any that was specified in the `git-clone` step.
+
+- The `git-open-pr` promotion step now exposes a `pr.url` field in its output, which can be used in subsequent steps. This is useful, for instance, to include a link to a PR in a Slack message sent via the `http` step.
+
+- The `git-open-pr` and `git-wait-for-pr` promotion steps now support Bitbucket thanks to @chamodshehanka.
+
+- The `helm-template` promotion step now supports directly setting the values of configuration keys directly in the step's configuration. This can be compared to using the `--set` flag in the Helm CLI to amend or override values specified in a `values.yaml` file.
+
+## 🚨 Breaking Changes {#breaking-changes}
+
+- Functions `commitFrom()`, `imageFrom()`, and `chartFrom()` used within expressions to locate artifacts in a `Promotion`'s `Freight` collection now return `nil` instead of an error when no matching artifact is found. This represents an added convenience, because [Expr](https://expr-lang.org/)'s [nil coalescing](https://expr-lang.org/docs/language-definition#nil-coalescing) and [optional chaining](https://expr-lang.org/docs/language-definition#optional-chaining) features make a `nil` result easier to deal with than an error.
+
+- `phase` and `message` fields in `Project` and `State` statuses were deprecated in v1.3.0 and replaced with [conditions](https://maelvls.dev/kubernetes-conditions/). These deprecated fields have now been removed.
+
+- The `messageFromSteps` field of the `git-commit` promotion step, deprecated in v1.3.0 has now been removed, replaced with guidance to use expressions in the `message` field to construct commit messages.
+
+- The `kargo.akuity.io/project-secret: true` was added in v1.2.0 and promptly deprecated in v1.2.1 and replaced with `kargo.akuity.io/cred-type: generic`. Beginning with Kargo v1.5.0, this label no longer has any effect. Since this label and its effect were never documented, it is very unlikely that anyone has been using it, so listing this as a breaking change is a formality.
+
+## ⚠️ New Deprecations {#new-deprecations}
+
+- The `Project` resource type's `spec` field has been deprecated (as described in "What's New?") and will be removed in Kargo v1.7.0. It is _already_ invalid to create new `Project`'s including a `spec`. Existing `Project` resources with a `spec` field will have a new `ProjectConfig` resource created for them automatically.
+
+- The `Warehouse` resource type's `gitRepoURL` field, previously used to link container image subscriptions to a related Git repository containing the image's source has been deprecated. This information may now be specified by annotating images with `org.opencontainers.image.source` at build time. Kargo has retrieved these annotations from container images since v1.4.0, and now leverages `org.opencontainers.image.source` to establish a link between container images and their source repositories. The `gitRepoURL` field will be removed in Kargo v1.7.0.
+
+- The `secrets` map in expressions deprecated, replaced with a new, better-performing `secret()` function (as described in "What's New?"). The `secrets` map will be removed in Kargo v1.7.0.
+
+## 👋 Welcome Faris! {#welcome-faris}
+
+The Kargo team is also excited to welcome a new maintainer -- @fuskovic!
+
+If you notice improved promotion step timeout accuracy in this release, you can thank @fuskovic for that!
+
+## 🙏 First Time Contributors {#first-time-contributors}
+
+As always, we would love to thank all first-time community contributors for their efforts! This release includes contributions from:
+
+* @hidalz
+* @Utwo
+* @IvanJosipovic
+* @fuskovic
+* @BWagenerGenerali
+* @schildwaechter
+* @mirrajabi
+* @han-steve
+* @sbyrne13
+* @Horgix
+* @Eslam-mohammed-anwar
diff --git a/hack/tools/go.mod b/hack/tools/go.mod
index 66a59fc0..b0dfefec 100644
--- a/hack/tools/go.mod
+++ b/hack/tools/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/bufbuild/buf v1.54.0
 	github.com/golangci/golangci-lint v1.64.8
 	golang.org/x/tools v0.33.0
-	helm.sh/helm/v3 v3.18.0
+	helm.sh/helm/v3 v3.18.1
 	k8s.io/code-generator v0.33.1
 	sigs.k8s.io/controller-tools v0.18.0
 )
diff --git a/hack/tools/go.sum b/hack/tools/go.sum
index c24c1980..dc1f0e71 100644
--- a/hack/tools/go.sum
+++ b/hack/tools/go.sum
@@ -974,8 +974,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
 gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
-helm.sh/helm/v3 v3.18.0 h1:ItOAm3Quo0dus3NUHjs+lluqWWEIO7xrSW+zKWCrvlw=
-helm.sh/helm/v3 v3.18.0/go.mod h1:43QHS1W97RcoFJRk36ZBhHdTfykqBlJdsWp3yhzdq8w=
+helm.sh/helm/v3 v3.18.1 h1:qLhXmtqXOHQb0Xv9HJolOLlah8RWbgyzt50xrtTWAlg=
+helm.sh/helm/v3 v3.18.1/go.mod h1:43QHS1W97RcoFJRk36ZBhHdTfykqBlJdsWp3yhzdq8w=
 honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI=
 honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4=
 k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
diff --git a/internal/controller/management/projectconfigs/project_configs.go b/internal/controller/management/projectconfigs/project_configs.go
index dce74e91..3e2a5054 100644
--- a/internal/controller/management/projectconfigs/project_configs.go
+++ b/internal/controller/management/projectconfigs/project_configs.go
@@ -4,7 +4,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"path"
+	"net/url"
 	"time"
 
 	"github.com/kelseyhightower/envconfig"
@@ -271,12 +271,21 @@ func (r *reconciler) newWebhookReceiver(
 	wr := &kargoapi.WebhookReceiver{
 		Name: rc.Name,
 		Path: external.GenerateWebhookPath(
+			rc.Name,
 			pc.Name,
 			cfg.receiverType,
 			string(secret),
 		),
 	}
-	wr.URL = path.Join(r.cfg.ExternalWebhookServerBaseURL, wr.Path)
+	wr.URL, err = url.JoinPath(r.cfg.ExternalWebhookServerBaseURL + wr.Path)
+	if err != nil {
+		logger.Error(err, "error generating webhook URL",
+			"base-url", r.cfg.ExternalWebhookServerBaseURL,
+			"path", wr.Path,
+		)
+		return nil, fmt.Errorf("error generating webhook URL: %w", err)
+	}
+
 	logger.Debug("webhook receiver initialized",
 		"webhook-receiver", wr,
 	)
diff --git a/internal/controller/promotions/promotions.go b/internal/controller/promotions/promotions.go
index 6d041c8b..262361fe 100644
--- a/internal/controller/promotions/promotions.go
+++ b/internal/controller/promotions/promotions.go
@@ -487,6 +487,7 @@ func (r *reconciler) promote(
 		Promotion:             workingPromo.Name,
 		FreightRequests:       stage.Spec.RequestedFreight,
 		Freight:               *workingPromo.Status.FreightCollection.DeepCopy(),
+		TargetFreightRef:      targetFreightRef,
 		StartFromStep:         promo.Status.CurrentStep,
 		StepExecutionMetadata: promo.Status.StepExecutionMetadata,
 		State:                 pkgPromotion.State(workingPromo.Status.GetState()),
diff --git a/internal/controller/stages/regular_stages.go b/internal/controller/stages/regular_stages.go
index 51b8afa0..17c83641 100644
--- a/internal/controller/stages/regular_stages.go
+++ b/internal/controller/stages/regular_stages.go
@@ -1066,6 +1066,7 @@ func (r *RegularStageReconciler) verifyStageFreight(
 	// verification as successful.
 	if stage.Spec.Verification == nil {
 		newVI := kargoapi.VerificationInfo{
+			ID:         uuid.NewString(),
 			StartTime:  ptr.To(metav1.NewTime(startTime)),
 			FinishTime: ptr.To(metav1.NewTime(endTime())),
 			Phase:      kargoapi.VerificationPhaseSuccessful,
diff --git a/internal/promotion/promotion.go b/internal/promotion/promotion.go
index 5109c5d1..0425b567 100644
--- a/internal/promotion/promotion.go
+++ b/internal/promotion/promotion.go
@@ -45,6 +45,8 @@ type Context struct {
 	// any Freight that has been inherited from the target Stage's current
 	// state.
 	Freight kargoapi.FreightCollection
+	// TargetFreightRef is the actual Freight that triggered this Promotion.
+	TargetFreightRef kargoapi.FreightReference
 	// StartFromStep is the index of the step from which the promotion should
 	// begin execution.
 	StartFromStep int64
@@ -186,6 +188,12 @@ func (s *Step) BuildEnv(promoCtx Context, opts ...StepEnvOption) map[string]any
 			"project":   promoCtx.Project,
 			"promotion": promoCtx.Promotion,
 			"stage":     promoCtx.Stage,
+			"targetFreight": map[string]any{
+				"name": promoCtx.TargetFreightRef.Name,
+				"origin": map[string]any{
+					"name": promoCtx.TargetFreightRef.Origin.Name,
+				},
+			},
 			"meta": map[string]any{
 				"promotion": map[string]any{
 					"actor": promoCtx.Actor,
diff --git a/internal/promotion/runner/builtin/git_cloner.go b/internal/promotion/runner/builtin/git_cloner.go
index ebbcfa86..4877cd74 100644
--- a/internal/promotion/runner/builtin/git_cloner.go
+++ b/internal/promotion/runner/builtin/git_cloner.go
@@ -59,27 +59,53 @@ func (g *gitCloner) Name() string {
 	return "git-clone"
 }
 
+// validateAndUnmarshal validates the config and unmarshals it into a typed
+// struct.
+func (g *gitCloner) validateAndUnmarshal(
+	cfg promotion.Config,
+) (builtin.GitCloneConfig, error) {
+	// Schema validation
+	if err := validate(
+		g.schemaLoader,
+		gojsonschema.NewGoLoader(cfg),
+		g.Name(),
+	); err != nil {
+		return builtin.GitCloneConfig{}, err
+	}
+
+	// Unmarshal to typed config
+	typedCfg, err := promotion.ConfigToStruct[builtin.GitCloneConfig](cfg)
+	if err != nil {
+		return builtin.GitCloneConfig{},
+			fmt.Errorf("could not convert config into %s config: %w", g.Name(), err)
+	}
+
+	// Ensure any specified aliases are unique across all checkouts
+	seen := make(map[string]struct{})
+	for i, checkout := range typedCfg.Checkout {
+		if checkout.As != "" {
+			if _, exists := seen[checkout.As]; exists {
+				return builtin.GitCloneConfig{},
+					fmt.Errorf("duplicate checkout alias %q at checkout[%d]", checkout.As, i)
+			}
+			seen[checkout.As] = struct{}{}
+		}
+	}
+	return typedCfg, nil
+}
+
 // Run implements the promotion.StepRunner interface.
 func (g *gitCloner) Run(
 	ctx context.Context,
 	stepCtx *promotion.StepContext,
 ) (promotion.StepResult, error) {
-	if err := g.validate(stepCtx.Config); err != nil {
-		return promotion.StepResult{Status: kargoapi.PromotionStepStatusErrored}, err
-	}
-	cfg, err := promotion.ConfigToStruct[builtin.GitCloneConfig](stepCtx.Config)
+	cfg, err := g.validateAndUnmarshal(stepCtx.Config)
 	if err != nil {
-		return promotion.StepResult{Status: kargoapi.PromotionStepStatusErrored},
-			fmt.Errorf("could not convert config into %s config: %w", g.Name(), err)
+		return promotion.StepResult{Status: kargoapi.PromotionStepStatusErrored}, err
 	}
 	return g.run(ctx, stepCtx, cfg)
 }
 
-// validate validates gitCloner configuration against a JSON schema.
-func (g *gitCloner) validate(cfg promotion.Config) error {
-	return validate(g.schemaLoader, gojsonschema.NewGoLoader(cfg), g.Name())
-}
-
 func (g *gitCloner) run(
 	ctx context.Context,
 	stepCtx *promotion.StepContext,
@@ -107,9 +133,9 @@ func (g *gitCloner) run(
 	var repoUser git.User
 	if cfg.Author != nil {
 		repoUser = git.User{
-			Name:  cfg.Author.Name,
-			Email: cfg.Author.Email,
-			SigningKey:     cfg.Author.SigningKey, // Optional, may be empty
+			Name:       cfg.Author.Name,
+			Email:      cfg.Author.Email,
+			SigningKey: cfg.Author.SigningKey, // Optional, may be empty
 		}
 	} else {
 		repoUser = g.gitUser // Default to the system-level gitUser
@@ -130,6 +156,7 @@ func (g *gitCloner) run(
 		return promotion.StepResult{Status: kargoapi.PromotionStepStatusErrored},
 			fmt.Errorf("error cloning %s: %w", cfg.RepoURL, err)
 	}
+	commits := make(map[string]any)
 	for _, checkout := range cfg.Checkout {
 		var ref string
 		switch {
@@ -151,20 +178,33 @@ func (g *gitCloner) run(
 				checkout.Path, stepCtx.WorkDir, err,
 			)
 		}
-		if _, err = repo.AddWorkTree(
+		worktree, err := repo.AddWorkTree(
 			path,
 			&git.AddWorkTreeOptions{Ref: ref},
-		); err != nil {
-			return promotion.StepResult{Status: kargoapi.PromotionStepStatusErrored}, fmt.Errorf(
-				"error adding work tree %s to repo %s: %w",
-				checkout.Path, cfg.RepoURL, err,
-			)
+		)
+		if err != nil {
+			return promotion.StepResult{Status: kargoapi.PromotionStepStatusErrored},
+				fmt.Errorf(
+					"error adding work tree %s to repo %s: %w",
+					checkout.Path, cfg.RepoURL, err,
+				)
+		}
+		key := checkout.Path
+		if checkout.As != "" {
+			key = checkout.As
+		}
+		if commits[key], err = worktree.LastCommitID(); err != nil {
+			return promotion.StepResult{Status: kargoapi.PromotionStepStatusErrored},
+				fmt.Errorf("error resolving HEAD for worktree at %s: %w", path, err)
 		}
 	}
 	// Note: We do NOT defer repo.Close() because we want to keep the repository
 	// around on the FS for subsequent promotion steps to use. The Engine will
 	// handle all work dir cleanup.
-	return promotion.StepResult{Status: kargoapi.PromotionStepStatusSucceeded}, nil
+	return promotion.StepResult{
+		Status: kargoapi.PromotionStepStatusSucceeded,
+		Output: map[string]any{"commits": commits},
+	}, nil
 }
 
 // ensureRemoteBranch checks for the existence of a remote branch. If the remote
diff --git a/internal/promotion/runner/builtin/git_commiter.go b/internal/promotion/runner/builtin/git_commiter.go
index 8c22bf12..e441184e 100644
--- a/internal/promotion/runner/builtin/git_commiter.go
+++ b/internal/promotion/runner/builtin/git_commiter.go
@@ -82,6 +82,8 @@ func (g *gitCommitter) run(
 		return promotion.StepResult{Status: kargoapi.PromotionStepStatusErrored},
 			fmt.Errorf("error checking for diffs in working tree: %w", err)
 	}
+
+	// Only commit if diffs have been found
 	if hasDiffs {
 		commitOpts := &git.CommitOptions{}
 		if cfg.Author != nil {
@@ -96,13 +98,20 @@ func (g *gitCommitter) run(
 				fmt.Errorf("error committing to working tree: %w", err)
 		}
 	}
+
 	commitID, err := workTree.LastCommitID()
 	if err != nil {
 		return promotion.StepResult{Status: kargoapi.PromotionStepStatusErrored},
 			fmt.Errorf("error getting last commit ID: %w", err)
 	}
+
+	status := kargoapi.PromotionStepStatusSucceeded
+	// If nothing was committed, return Skipped status instead.
+	if !hasDiffs {
+		status = kargoapi.PromotionStepStatusSkipped
+	}
 	return promotion.StepResult{
-		Status: kargoapi.PromotionStepStatusSucceeded,
+		Status: status,
 		Output: map[string]any{stateKeyCommit: commitID},
 	}, nil
 }
diff --git a/internal/promotion/runner/builtin/http_requester.go b/internal/promotion/runner/builtin/http_requester.go
index 0cf8b52b..e87017cc 100644
--- a/internal/promotion/runner/builtin/http_requester.go
+++ b/internal/promotion/runner/builtin/http_requester.go
@@ -227,12 +227,20 @@ func (h *httpRequester) buildExprEnv(
 		strings.Split(resp.Header.Get(contentTypeHeader), ";")[0],
 	)
 	if len(bodyBytes) > 0 && contentType == contentTypeJSON {
-		body := map[string]any{}
-		if err = json.Unmarshal(bodyBytes, &body); err != nil {
-			return nil, err
+		var parsedBody any
+		if err := json.Unmarshal(bodyBytes, &parsedBody); err != nil {
+			return nil, fmt.Errorf("failed to parse response: %w", err)
+		}
+
+		// Unmarshal into map[string]any or []any
+		switch parsedBody.(type) {
+		case map[string]any, []any:
+			env["response"].(map[string]any)["body"] = parsedBody
+		default:
+			return nil, fmt.Errorf("unexpected type when unmarshaling response: %T", parsedBody)
 		}
-		env["response"].(map[string]any)["body"] = body // nolint: forcetypeassert
 	}
+
 	return env, nil
 }
 
diff --git a/internal/promotion/runner/builtin/schemas/git-clone-config.json b/internal/promotion/runner/builtin/schemas/git-clone-config.json
index 1a832f90..47154410 100644
--- a/internal/promotion/runner/builtin/schemas/git-clone-config.json
+++ b/internal/promotion/runner/builtin/schemas/git-clone-config.json
@@ -45,6 +45,10 @@
         "additionalProperties": false,
         "required": ["path"],
         "properties": {
+          "as": {
+            "type": "string",
+            "description": "Optional alias for this checkout. If provided, it will be used as the key in the 'commits' output map."
+          },
           "branch": {
             "type": "string",
             "description": "The branch to checkout. Mutually exclusive with 'commit' and 'tag'. If none of these are specified, the default branch is checked out."
diff --git a/internal/promotion/simple_engine.go b/internal/promotion/simple_engine.go
index e965fcd0..4e2b204e 100644
--- a/internal/promotion/simple_engine.go
+++ b/internal/promotion/simple_engine.go
@@ -249,8 +249,10 @@ stepLoop:
 		// err.
 
 		switch {
-		case stepExecMeta.Status == kargoapi.PromotionStepStatusSucceeded:
-			// Best case scenario: The step succeeded.
+		case stepExecMeta.Status == kargoapi.PromotionStepStatusSucceeded ||
+			stepExecMeta.Status == kargoapi.PromotionStepStatusSkipped:
+			// Note: A step that ran briefly and self-determined it should be
+			// "skipped" is treated similarly to success.
 			stepExecMeta.FinishedAt = ptr.To(metav1.Now())
 			if healthCheck := result.HealthCheck; healthCheck != nil {
 				healthChecks = append(healthChecks, *healthCheck)
diff --git a/internal/webhook/external/github.go b/internal/webhook/external/github.go
index 0fe264b0..34bd1ebc 100644
--- a/internal/webhook/external/github.go
+++ b/internal/webhook/external/github.go
@@ -27,10 +27,9 @@ func githubHandler(
 ) http.HandlerFunc {
 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		ctx := r.Context()
-		logger := logging.LoggerFromContext(ctx)
-		logger.Debug("retrieving secret",
-			"secret-name", secretName,
-		)
+		logger := logging.LoggerFromContext(ctx).WithValues("path", r.URL.Path)
+		ctx = logging.ContextWithLogger(ctx, logger)
+		logger.Debug("retrieving secret", "secret-name", secretName)
 		var secret corev1.Secret
 		err := c.Get(ctx,
 			client.ObjectKey{
@@ -59,10 +58,13 @@ func githubHandler(
 		// TODO(fuskovic): eventually switch on event type to perform
 		// different actions (e.g. refresh Promotion on PR merge)
 		eventType := r.Header.Get("X-GitHub-Event")
-		if eventType != "push" {
-			xhttp.WriteErrorJSON(w,
+		switch eventType {
+		case "ping", "push":
+		default:
+			xhttp.WriteErrorJSON(
+				w,
 				xhttp.Error(
-					fmt.Errorf("only push events are supported"),
+					fmt.Errorf("event type %s is not supported", eventType),
 					http.StatusNotImplemented,
 				),
 			)
@@ -114,53 +116,57 @@ func githubHandler(
 			return
 		}
 
-		pe, ok := e.(*gh.PushEvent)
-		if !ok {
-			xhttp.WriteErrorJSON(w,
-				xhttp.Error(
-					fmt.Errorf("only push events are supported"),
-					http.StatusBadRequest,
-				),
+		switch e := e.(type) {
+		case *gh.PingEvent:
+			repoWebURL := e.GetRepo().GetHTMLURL()
+			logger.Debug("received ping event", "repo", repoWebURL)
+			xhttp.WriteResponseJSON(w,
+				http.StatusOK,
+				map[string]string{
+					"msg": fmt.Sprintf(
+						"ping event received, webhook is configured correctly for %s",
+						repoWebURL,
+					),
+				},
 			)
-			return
-		}
+		case *gh.PushEvent:
+			repoWebURL := e.GetRepo().GetHTMLURL()
+			logger = logger.WithValues("repoWebURL", repoWebURL)
+			ctx = logging.ContextWithLogger(ctx, logger)
+			result, err := refreshWarehouses(ctx, c, namespace, repoWebURL)
+			if err != nil {
+				xhttp.WriteErrorJSON(w,
+					xhttp.Error(err, http.StatusInternalServerError),
+				)
+				return
+			}
 
-		repo := *pe.Repo.HTMLURL
-		logger.Debug("source repository retrieved", "name", repo)
-		ctx = logging.ContextWithLogger(ctx, logger)
-		result, err := refreshWarehouses(ctx, c, namespace, repo)
-		if err != nil {
-			xhttp.WriteErrorJSON(w,
-				xhttp.Error(err, http.StatusInternalServerError),
+			logger.Debug("execution complete",
+				"successes", result.successes,
+				"failures", result.failures,
 			)
-			return
-		}
 
-		logger.Debug("execution complete",
-			"successes", result.successes,
-			"failures", result.failures,
-		)
+			if result.failures > 0 {
+				xhttp.WriteResponseJSON(w,
+					http.StatusInternalServerError,
+					map[string]string{
+						"error": fmt.Sprintf("failed to refresh %d of %d warehouses",
+							result.failures,
+							result.successes+result.failures,
+						),
+					},
+				)
+				return
+			}
 
-		if result.failures > 0 {
 			xhttp.WriteResponseJSON(w,
-				http.StatusInternalServerError,
+				http.StatusOK,
 				map[string]string{
-					"error": fmt.Sprintf("failed to refresh %d of %d warehouses",
-						result.failures,
-						result.successes+result.failures,
+					"msg": fmt.Sprintf("refreshed %d warehouse(s)",
+						result.successes,
 					),
 				},
 			)
-			return
 		}
-
-		xhttp.WriteResponseJSON(w,
-			http.StatusOK,
-			map[string]string{
-				"msg": fmt.Sprintf("refreshed %d warehouse(s)",
-					result.successes,
-				),
-			},
-		)
 	})
 }
diff --git a/internal/webhook/external/path.go b/internal/webhook/external/path.go
index c2845bac..483cd99d 100644
--- a/internal/webhook/external/path.go
+++ b/internal/webhook/external/path.go
@@ -6,9 +6,16 @@ import (
 	"strings"
 )
 
-func GenerateWebhookPath(project, provider, token string) string {
-	input := []byte(project + token)
+// GenerateWebhookPath generates a unique path for a webhook based on the
+// provided webhook receiver name, project name, kind, and secret.
+// The path is formatted as "/webhook/{kind}/{hash}" where the hash is
+// a SHA-256 hash of the concatenated webhook receiver name, project name,
+// and secret.
+func GenerateWebhookPath(name, project, kind, secret string) string {
+	// Warning: Changes to this line could alter URLs that existing users may
+	// already be using
+	input := []byte(name + project + secret)
 	h := sha256.New()
-	h.Write(input)
-	return fmt.Sprintf("/webhook/%s/%x", strings.ToLower(provider), h.Sum(nil))
+	_, _ = h.Write(input)
+	return fmt.Sprintf("/webhook/%s/%x", strings.ToLower(kind), h.Sum(nil))
 }
diff --git a/internal/webhook/kubernetes/projectconfig/webhook.go b/internal/webhook/kubernetes/projectconfig/webhook.go
index fe77fd20..3fd461a5 100644
--- a/internal/webhook/kubernetes/projectconfig/webhook.go
+++ b/internal/webhook/kubernetes/projectconfig/webhook.go
@@ -135,10 +135,21 @@ func (w *webhook) validateSpec(
 	f *field.Path,
 	spec kargoapi.ProjectConfigSpec,
 ) field.ErrorList {
-	return w.validatePromotionPolicies(
+	var fieldErrs field.ErrorList
+	if errs := w.validatePromotionPolicies(
 		f.Child("promotionPolicies"),
 		spec.PromotionPolicies,
-	)
+	); errs != nil {
+		fieldErrs = append(fieldErrs, errs...)
+	}
+
+	if errs := w.validateWebhookReceivers(
+		f.Child("webhookReceivers"),
+		spec.WebhookReceivers,
+	); errs != nil {
+		fieldErrs = append(fieldErrs, errs...)
+	}
+	return fieldErrs
 }
 
 func (w *webhook) validatePromotionPolicies(
@@ -215,6 +226,29 @@ func (w *webhook) validatePromotionPolicies(
 	return errs
 }
 
+func (w *webhook) validateWebhookReceivers(
+	f *field.Path,
+	webhookReceivers []kargoapi.WebhookReceiverConfig,
+) field.ErrorList {
+	var errs field.ErrorList
+	dupes := make(map[string]int)
+	for i, r := range webhookReceivers {
+		if existingIndex, exists := dupes[r.Name]; exists {
+			errs = append(errs, field.Invalid(
+				f.Index(i).Child("name"),
+				r.Name,
+				fmt.Sprintf(
+					"webhook receiver name already defined at %s",
+					f.Index(existingIndex),
+				),
+			))
+			continue
+		}
+		dupes[r.Name] = i
+	}
+	return errs
+}
+
 func (w *webhook) ensureProjectNamespace(ctx context.Context, meta metav1.ObjectMeta) error {
 	ns := &corev1.Namespace{}
 	if err := w.client.Get(ctx, types.NamespacedName{Name: meta.Namespace}, ns); err != nil {
diff --git a/pkg/x/promotion/runner/builtin/zz_config_types.go b/pkg/x/promotion/runner/builtin/zz_config_types.go
index 9331046b..880c8e61 100644
--- a/pkg/x/promotion/runner/builtin/zz_config_types.go
+++ b/pkg/x/promotion/runner/builtin/zz_config_types.go
@@ -131,6 +131,9 @@ type GitCloneConfigAuthor struct {
 }
 
 type Checkout struct {
+	// Optional alias for this checkout. If provided, it will be used as the key in the
+	// 'commits' output map.
+	As string `json:"as,omitempty"`
 	// The branch to checkout. Mutually exclusive with 'commit' and 'tag'. If none of these are
 	// specified, the default branch is checked out.
 	Branch string `json:"branch,omitempty"`
diff --git a/ui/src/features/auth/jwt-utils.ts b/ui/src/features/auth/jwt-utils.ts
index f0409392..231afac9 100644
--- a/ui/src/features/auth/jwt-utils.ts
+++ b/ui/src/features/auth/jwt-utils.ts
@@ -38,3 +38,31 @@ export const getUserEmail = (user?: JWTInfo | null) => {
 
   return meta;
 };
+
+export const claimsMapping: Record<string, { label: string; description: string }> = {
+  iss: {
+    label: 'Issuer',
+    description: 'Identifies the principal that issued the JWT.'
+  },
+  sub: {
+    label: 'Subject',
+    description: 'Identifies the principal that is the subject of the JWT.'
+  },
+  aud: {
+    label: 'Audience',
+    description: 'Identifies the recipients the JWT is intended for (can be a string or array).'
+  },
+  exp: {
+    label: 'Expiration Time',
+    description:
+      'Token expiration time (as Unix timestamp). After this time, the token must not be accepted.'
+  },
+  iat: {
+    label: 'Issued At',
+    description: 'The time the JWT was issued (as Unix timestamp).'
+  },
+  jti: {
+    label: 'JWT ID',
+    description: 'Unique identifier for the JWT, used to prevent replay attacks.'
+  }
+};
diff --git a/ui/src/features/project/pipelines/freight/short-version-utils.ts b/ui/src/features/project/pipelines/freight/short-version-utils.ts
index 1d1829b9..d4570823 100644
--- a/ui/src/features/project/pipelines/freight/short-version-utils.ts
+++ b/ui/src/features/project/pipelines/freight/short-version-utils.ts
@@ -1,10 +1,10 @@
-export const shortVersion = (version: string = '') => {
-  if (version.length <= 12) {
+export const shortVersion = (version: string = '', length = 12) => {
+  if (version.length <= length) {
     return version;
   }
 
-  const prefix = version.slice(0, 6);
-  const suffix = version.slice(-6);
+  const prefix = version.slice(0, length / 2);
+  const suffix = version.slice(-(length / 2));
 
   return `${prefix}...${suffix}`;
 };
diff --git a/ui/src/features/project/pipelines/promotion/freight-details.tsx b/ui/src/features/project/pipelines/promotion/freight-details.tsx
index 95be05be..10590e4e 100644
--- a/ui/src/features/project/pipelines/promotion/freight-details.tsx
+++ b/ui/src/features/project/pipelines/promotion/freight-details.tsx
@@ -1,11 +1,14 @@
-import { Descriptions } from 'antd';
+import { toJson } from '@bufbuild/protobuf';
+import { Descriptions, Tabs } from 'antd';
 import Link from 'antd/es/typography/Link';
 import { generatePath, useNavigate } from 'react-router-dom';
 
 import { paths } from '@ui/config/paths';
+import { ManifestPreview } from '@ui/features/common/manifest-preview';
+import { FreightStatusList } from '@ui/features/freight/freight-status-list';
 import { FreightTable } from '@ui/features/project/pipelines/freight/freight-table';
 import { useGetFreightCreation } from '@ui/features/project/pipelines/freight/use-get-freight-creation';
-import { Freight } from '@ui/gen/api/v1alpha1/generated_pb';
+import { Freight, FreightSchema } from '@ui/gen/api/v1alpha1/generated_pb';
 
 type FreightDetailsProps = {
   freight: Freight;
@@ -15,43 +18,71 @@ export const FreightDetails = (props: FreightDetailsProps) => {
   const navigate = useNavigate();
   const freightCreatedAt = useGetFreightCreation(props.freight);
 
+  if (!props.freight) {
+    return null;
+  }
+
   return (
     <>
-      <Descriptions
-        column={2}
-        size='small'
-        bordered
-        title='Freight'
+      <Tabs
         items={[
           {
-            label: 'id',
+            key: 'freight',
+            label: 'Freight',
             children: (
-              <Link
-                onClick={() =>
-                  navigate(
-                    generatePath(paths.freight, {
-                      name: props.freight?.metadata?.namespace,
-                      freightName: props.freight?.metadata?.name
-                    })
-                  )
-                }
-              >
-                {props.freight?.metadata?.name}
-              </Link>
+              <>
+                <Descriptions
+                  column={2}
+                  size='small'
+                  bordered
+                  items={[
+                    {
+                      label: 'alias',
+                      children: props.freight?.alias
+                    },
+                    {
+                      label: 'id',
+                      children: (
+                        <Link
+                          onClick={() =>
+                            navigate(
+                              generatePath(paths.freight, {
+                                name: props.freight?.metadata?.namespace,
+                                freightName: props.freight?.metadata?.name
+                              })
+                            )
+                          }
+                        >
+                          {props.freight?.metadata?.name}
+                        </Link>
+                      )
+                    },
+                    {
+                      label: 'uid',
+                      children: props.freight?.metadata?.uid
+                    },
+                    {
+                      label: 'created',
+                      children: `${freightCreatedAt.relative}${freightCreatedAt.relative && ' , on'} ${freightCreatedAt.abs}`
+                    }
+                  ]}
+                />
+
+                <FreightTable className='mt-5' freight={props.freight} />
+
+                <FreightStatusList freight={props.freight} />
+              </>
             )
           },
           {
-            label: 'uid',
-            children: props.freight?.metadata?.uid
-          },
-          {
-            label: 'created',
-            children: `${freightCreatedAt.relative}${freightCreatedAt.relative && ' , on'} ${freightCreatedAt.abs}`
+            key: 'manifest',
+            label: 'YAML',
+            children: (
+              <ManifestPreview object={toJson(FreightSchema, props.freight)} height='500px' />
+            )
           }
         ]}
       />
-
-      <FreightTable className='mt-5' freight={props.freight} />
     </>
   );
 };
diff --git a/ui/src/features/stage/stage-details.tsx b/ui/src/features/stage/stage-details.tsx
index 59c5ed1f..d42d88bb 100644
--- a/ui/src/features/stage/stage-details.tsx
+++ b/ui/src/features/stage/stage-details.tsx
@@ -189,6 +189,7 @@ export const StageDetails = ({ stage }: { stage: Stage }) => {
                       freightHistory={stage?.status?.freightHistory}
                       currentActiveFreight={stage?.status?.lastPromotion?.freight?.name}
                       projectName={projectName || ''}
+                      stageName={stageName || ''}
                     />
                   )
                 },
diff --git a/ui/src/features/stage/tabs/freight-history/freight-history.tsx b/ui/src/features/stage/tabs/freight-history/freight-history.tsx
index a039f1a6..7d4fbac4 100644
--- a/ui/src/features/stage/tabs/freight-history/freight-history.tsx
+++ b/ui/src/features/stage/tabs/freight-history/freight-history.tsx
@@ -1,30 +1,37 @@
 import { create } from '@bufbuild/protobuf';
-import { Descriptions, Flex, Select, Space, Table, Tag } from 'antd';
+import { faTruck } from '@fortawesome/free-solid-svg-icons';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Button, Descriptions, Flex, Select, Space, Table, Tag, Typography } from 'antd';
+import { formatDistance } from 'date-fns';
 import { useMemo } from 'react';
 import { useState } from 'react';
 import { useEffect } from 'react';
-import { generatePath, Link } from 'react-router-dom';
+import { generatePath, Link, useNavigate } from 'react-router-dom';
 
 import { paths } from '@ui/config/paths';
+import { shortVersion } from '@ui/features/project/pipelines/freight/short-version-utils';
 import {
   FreightReference,
   FreightRequest,
   FreightSchema,
   StageStatus
 } from '@ui/gen/api/v1alpha1/generated_pb';
-import { PlainMessage } from '@ui/utils/connectrpc-utils';
+import { PlainMessage, timestampDate } from '@ui/utils/connectrpc-utils';
 
 import { FreightContents } from '../../../freight-timeline/freight-contents';
 
 import { useGetFreightMap } from './use-get-freight-map';
+import { usePromotionsByFreightCollection } from './use-promotions-by-freight-collection';
 
 export const FreightHistory = ({
   projectName,
   freightHistory,
-  requestedFreights
+  requestedFreights,
+  stageName
 }: {
   requestedFreights: FreightRequest[];
   projectName: string;
+  stageName: string;
   // show the freight history thats 1:1 with requested freight
   freightHistory?: StageStatus['freightHistory'];
   // freight hash name which is active at the moment
@@ -32,6 +39,12 @@ export const FreightHistory = ({
   // usually last one is active but we have to consider multi-pipeline case
   currentActiveFreight?: string;
 }) => {
+  const navigate = useNavigate();
+  const promotionsByFreightCollection = usePromotionsByFreightCollection({
+    project: projectName,
+    stage: stageName
+  });
+
   const [selectedRequestedFreight, setSelectedRequestedFreight] = useState<FreightRequest>();
 
   const freightMap = useGetFreightMap(projectName);
@@ -40,7 +53,7 @@ export const FreightHistory = ({
     // to show the history
     const freightHistoryPerWarehouse: Record<
       string /* warehouse eg. Warehouse/w-1 or Warehouse/w-2 */,
-      PlainMessage<FreightReference>[]
+      PlainMessage<{ id: string } & FreightReference>[]
     > = {};
 
     for (const freightCollection of freightHistory || []) {
@@ -53,7 +66,10 @@ export const FreightHistory = ({
           freightHistoryPerWarehouse[warehouseIdentifier] = [];
         }
 
-        freightHistoryPerWarehouse[warehouseIdentifier].push(freightReference);
+        freightHistoryPerWarehouse[warehouseIdentifier].push({
+          id: freightCollection?.id,
+          ...freightReference
+        });
       }
     }
 
@@ -101,29 +117,25 @@ export const FreightHistory = ({
         pagination={{ hideOnSinglePage: true }}
         rowKey={(record, index) => `${record.name}${index}`}
       >
-        <Table.Column<PlainMessage<FreightReference>>
-          title='ID'
-          width={100}
-          render={(value, record) =>
-            freightMap[record?.name || '']?.metadata?.name?.substring(0, 7)
-          }
-        />
         <Table.Column<PlainMessage<FreightReference>>
           title='Alias'
           width={240}
-          render={(value, record, index) => (
-            <Space>
-              <Link
-                to={generatePath(paths.freight, {
-                  name: projectName,
-                  freightName: record.name
-                })}
-              >
-                {freightMap[record?.name || '']?.alias || record.name}
-              </Link>
-              {index === 0 && <Tag color='success'>Active</Tag>}
-            </Space>
-          )}
+          render={(value, record, index) => {
+            const alias = shortVersion(freightMap[record?.name || '']?.alias || record.name, 20);
+            return (
+              <Space>
+                <Link
+                  to={generatePath(paths.freight, {
+                    name: projectName,
+                    freightName: record.name
+                  })}
+                >
+                  {alias}
+                </Link>
+                {index === 0 && <Tag color='success'>Active</Tag>}
+              </Space>
+            );
+          }}
         />
         <Table.Column<PlainMessage<FreightReference>>
           title='Contents'
@@ -141,6 +153,76 @@ export const FreightHistory = ({
             />
           )}
         />
+        <Table.Column<PlainMessage<FreightReference>>
+          title='Created at'
+          render={(_, record) => {
+            const freightCreation = timestampDate(
+              freightMap[record?.name || '']?.metadata?.creationTimestamp
+            );
+
+            if (freightCreation) {
+              return (
+                <Typography.Text
+                  type='secondary'
+                  className='text-xs'
+                  title={freightCreation?.toString?.()}
+                >
+                  {formatDistance(freightCreation, new Date(), { addSuffix: false })}
+                </Typography.Text>
+              );
+            }
+
+            return '-';
+          }}
+        />
+        <Table.Column<PlainMessage<{ id: string } & FreightReference>>
+          title='Promoted at'
+          render={(_, record) => {
+            const promotion = promotionsByFreightCollection[record.id];
+
+            if (promotion) {
+              const promotedAt = timestampDate(promotion?.metadata?.creationTimestamp);
+
+              if (promotedAt) {
+                return (
+                  <>
+                    <Typography.Text
+                      type='secondary'
+                      className='text-xs'
+                      title={promotedAt?.toString?.()}
+                    >
+                      {formatDistance(promotedAt, new Date(), { addSuffix: false })}
+                    </Typography.Text>
+                  </>
+                );
+              }
+            }
+
+            return '-';
+          }}
+        />
+        <Table.Column<PlainMessage<FreightReference>>
+          render={(_, record, idx) =>
+            idx > 0 &&
+            freightMap[record?.name] && (
+              <Button
+                size='small'
+                icon={<FontAwesomeIcon icon={faTruck} />}
+                onClick={() =>
+                  navigate(
+                    generatePath(paths.promote, {
+                      name: projectName,
+                      freight: record.name,
+                      stage: stageName
+                    })
+                  )
+                }
+              >
+                Promote
+              </Button>
+            )
+          }
+        />
       </Table>
     </Flex>
   );
diff --git a/ui/src/features/stage/tabs/freight-history/use-promotions-by-freight-collection.ts b/ui/src/features/stage/tabs/freight-history/use-promotions-by-freight-collection.ts
new file mode 100644
index 00000000..8e0d7d0d
--- /dev/null
+++ b/ui/src/features/stage/tabs/freight-history/use-promotions-by-freight-collection.ts
@@ -0,0 +1,25 @@
+import { useQuery } from '@connectrpc/connect-query';
+import { useMemo } from 'react';
+
+import { listPromotions } from '@ui/gen/api/service/v1alpha1/service-KargoService_connectquery';
+import { Promotion } from '@ui/gen/api/v1alpha1/generated_pb';
+
+export const usePromotionsByFreightCollection = (payload: { project: string; stage: string }) => {
+  const promotionsQuery = useQuery(listPromotions, payload);
+
+  return useMemo(() => {
+    const promotions = promotionsQuery.data?.promotions || [];
+
+    const promotionsByFreightCollection: Record<string, Promotion> = {};
+
+    for (const promotion of promotions) {
+      const freightCollectionId = promotion?.status?.freightCollection?.id || '';
+
+      if (freightCollectionId) {
+        promotionsByFreightCollection[freightCollectionId] = promotion;
+      }
+    }
+
+    return promotionsByFreightCollection;
+  }, [promotionsQuery.data]);
+};
diff --git a/ui/src/gen/api/v1alpha1/generated_pb.ts b/ui/src/gen/api/v1alpha1/generated_pb.ts
index 4ffaef1c..0360accb 100644
--- a/ui/src/gen/api/v1alpha1/generated_pb.ts
+++ b/ui/src/gen/api/v1alpha1/generated_pb.ts
@@ -20,7 +20,7 @@ import type { Message } from "@bufbuild/protobuf";
  * Describes the file api/v1alpha1/generated.proto.
  */
 export const file_api_v1alpha1_generated: GenFile = /*@__PURE__*/
-  fileDesc("ChxhcGkvdjFhbHBoYTEvZ2VuZXJhdGVkLnByb3RvEiRnaXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEiMgoTQW5hbHlzaXNSdW5Bcmd1bWVudBIMCgRuYW1lGAEgASgJEg0KBXZhbHVlGAIgASgJIrACChNBbmFseXNpc1J1bk1ldGFkYXRhElUKBmxhYmVscxgBIAMoCzJFLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5BbmFseXNpc1J1bk1ldGFkYXRhLkxhYmVsc0VudHJ5El8KC2Fubm90YXRpb25zGAIgAygLMkouZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkFuYWx5c2lzUnVuTWV0YWRhdGEuQW5ub3RhdGlvbnNFbnRyeRotCgtMYWJlbHNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBGjIKEEFubm90YXRpb25zRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASJGChRBbmFseXNpc1J1blJlZmVyZW5jZRIRCgluYW1lc3BhY2UYASABKAkSDAoEbmFtZRgCIAEoCRINCgVwaGFzZRgDIAEoCSI3ChlBbmFseXNpc1RlbXBsYXRlUmVmZXJlbmNlEgwKBG5hbWUYASABKAkSDAoEa2luZBgCIAEoCSJPCg1BcHByb3ZlZFN0YWdlEj4KCmFwcHJvdmVkQXQYASABKAsyKi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuVGltZSI4ChVBcmdvQ0RBcHBIZWFsdGhTdGF0dXMSDgoGc3RhdHVzGAEgASgJEg8KB21lc3NhZ2UYAiABKAki1AEKD0FyZ29DREFwcFN0YXR1cxIRCgluYW1lc3BhY2UYASABKAkSDAoEbmFtZRgCIAEoCRJRCgxoZWFsdGhTdGF0dXMYAyABKAsyOy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuQXJnb0NEQXBwSGVhbHRoU3RhdHVzEk0KCnN5bmNTdGF0dXMYBCABKAsyOS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuQXJnb0NEQXBwU3luY1N0YXR1cyJKChNBcmdvQ0RBcHBTeW5jU3RhdHVzEg4KBnN0YXR1cxgBIAEoCRIQCghyZXZpc2lvbhgCIAEoCRIRCglyZXZpc2lvbnMYAyADKAkiNwoFQ2hhcnQSDwoHcmVwb1VSTBgBIAEoCRIMCgRuYW1lGAIgASgJEg8KB3ZlcnNpb24YAyABKAkiYQoUQ2hhcnREaXNjb3ZlcnlSZXN1bHQSDwoHcmVwb1VSTBgBIAEoCRIMCgRuYW1lGAIgASgJEhgKEHNlbXZlckNvbnN0cmFpbnQYAyABKAkSEAoIdmVyc2lvbnMYBCADKAkiZAoRQ2hhcnRTdWJzY3JpcHRpb24SDwoHcmVwb1VSTBgBIAEoCRIMCgRuYW1lGAIgASgJEhgKEHNlbXZlckNvbnN0cmFpbnQYAyABKAkSFgoOZGlzY292ZXJ5TGltaXQYBCABKAUioQEKFENsdXN0ZXJQcm9tb3Rpb25UYXNrEkIKCG1ldGFkYXRhGAEgASgLMjAuazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLk9iamVjdE1ldGESRQoEc3BlYxgCIAEoCzI3LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25UYXNrU3BlYyKnAQoYQ2x1c3RlclByb21vdGlvblRhc2tMaXN0EkAKCG1ldGFkYXRhGAEgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkxpc3RNZXRhEkkKBWl0ZW1zGAIgAygLMjouZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkNsdXN0ZXJQcm9tb3Rpb25UYXNrIkkKDEN1cnJlbnRTdGFnZRI5CgVzaW5jZRgBIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lIrYCChNEaXNjb3ZlcmVkQXJ0aWZhY3RzEkAKDGRpc2NvdmVyZWRBdBgEIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lEkUKA2dpdBgBIAMoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5HaXREaXNjb3ZlcnlSZXN1bHQSSgoGaW1hZ2VzGAIgAygLMjouZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkltYWdlRGlzY292ZXJ5UmVzdWx0EkoKBmNoYXJ0cxgDIAMoCzI6LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5DaGFydERpc2NvdmVyeVJlc3VsdCKwAQoQRGlzY292ZXJlZENvbW1pdBIKCgJpZBgBIAEoCRIOCgZicmFuY2gYAiABKAkSCwoDdGFnGAMgASgJEg8KB3N1YmplY3QYBCABKAkSDgoGYXV0aG9yGAUgASgJEhEKCWNvbW1pdHRlchgGIAEoCRI/CgtjcmVhdG9yRGF0ZRgHIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lIqQCChhEaXNjb3ZlcmVkSW1hZ2VSZWZlcmVuY2USCwoDdGFnGAEgASgJEg4KBmRpZ2VzdBgCIAEoCRJkCgthbm5vdGF0aW9ucxgFIAMoCzJPLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5EaXNjb3ZlcmVkSW1hZ2VSZWZlcmVuY2UuQW5ub3RhdGlvbnNFbnRyeRISCgpnaXRSZXBvVVJMGAMgASgJEj0KCWNyZWF0ZWRBdBgEIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lGjIKEEFubm90YXRpb25zRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASIxChJFeHByZXNzaW9uVmFyaWFibGUSDAoEbmFtZRgBIAEoCRINCgV2YWx1ZRgCIAEoCSKiAwoHRnJlaWdodBJCCghtZXRhZGF0YRgBIAEoCzIwLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5PYmplY3RNZXRhEg0KBWFsaWFzGAcgASgJEkMKBm9yaWdpbhgJIAEoCzIzLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0T3JpZ2luEkAKB2NvbW1pdHMYAyADKAsyLy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuR2l0Q29tbWl0EjsKBmltYWdlcxgEIAMoCzIrLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5JbWFnZRI7CgZjaGFydHMYBSADKAsyKy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuQ2hhcnQSQwoGc3RhdHVzGAYgASgLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRTdGF0dXMirQIKEUZyZWlnaHRDb2xsZWN0aW9uEgoKAmlkGAMgASgJElEKBWl0ZW1zGAEgAygLMkIuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRDb2xsZWN0aW9uLkl0ZW1zRW50cnkSUwoTdmVyaWZpY2F0aW9uSGlzdG9yeRgCIAMoCzI2LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5WZXJpZmljYXRpb25JbmZvGmQKCkl0ZW1zRW50cnkSCwoDa2V5GAEgASgJEkUKBXZhbHVlGAIgASgLMjYuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRSZWZlcmVuY2U6AjgBIo0BCgtGcmVpZ2h0TGlzdBJACghtZXRhZGF0YRgBIAEoCzIuLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5MaXN0TWV0YRI8CgVpdGVtcxgCIAMoCzItLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0IisKDUZyZWlnaHRPcmlnaW4SDAoEa2luZBgBIAEoCRIMCgRuYW1lGAIgASgJIqECChBGcmVpZ2h0UmVmZXJlbmNlEgwKBG5hbWUYASABKAkSQwoGb3JpZ2luGAggASgLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRPcmlnaW4SQAoHY29tbWl0cxgCIAMoCzIvLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5HaXRDb21taXQSOwoGaW1hZ2VzGAMgAygLMisuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkltYWdlEjsKBmNoYXJ0cxgEIAMoCzIrLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5DaGFydCKcAQoORnJlaWdodFJlcXVlc3QSQwoGb3JpZ2luGAEgASgLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRPcmlnaW4SRQoHc291cmNlcxgCIAEoCzI0LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0U291cmNlcyKYAQoORnJlaWdodFNvdXJjZXMSDgoGZGlyZWN0GAEgASgIEg4KBnN0YWdlcxgCIAMoCRJIChByZXF1aXJlZFNvYWtUaW1lGAMgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkR1cmF0aW9uEhwKFGF2YWlsYWJpbGl0eVN0cmF0ZWd5GAQgASgJItcECg1GcmVpZ2h0U3RhdHVzElkKC2N1cnJlbnRseUluGAMgAygLMkQuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRTdGF0dXMuQ3VycmVudGx5SW5FbnRyeRJXCgp2ZXJpZmllZEluGAEgAygLMkMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRTdGF0dXMuVmVyaWZpZWRJbkVudHJ5ElkKC2FwcHJvdmVkRm9yGAIgAygLMkQuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRTdGF0dXMuQXBwcm92ZWRGb3JFbnRyeRpmChBDdXJyZW50bHlJbkVudHJ5EgsKA2tleRgBIAEoCRJBCgV2YWx1ZRgCIAEoCzIyLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5DdXJyZW50U3RhZ2U6AjgBGmYKD1ZlcmlmaWVkSW5FbnRyeRILCgNrZXkYASABKAkSQgoFdmFsdWUYAiABKAsyMy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuVmVyaWZpZWRTdGFnZToCOAEaZwoQQXBwcm92ZWRGb3JFbnRyeRILCgNrZXkYASABKAkSQgoFdmFsdWUYAiABKAsyMy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuQXBwcm92ZWRTdGFnZToCOAEieQoJR2l0Q29tbWl0Eg8KB3JlcG9VUkwYASABKAkSCgoCaWQYAiABKAkSDgoGYnJhbmNoGAMgASgJEgsKA3RhZxgEIAEoCRIPCgdtZXNzYWdlGAYgASgJEg4KBmF1dGhvchgHIAEoCRIRCgljb21taXR0ZXIYCCABKAkibgoSR2l0RGlzY292ZXJ5UmVzdWx0Eg8KB3JlcG9VUkwYASABKAkSRwoHY29tbWl0cxgCIAMoCzI2LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5EaXNjb3ZlcmVkQ29tbWl0IlQKFUdpdEh1YldlYmhvb2tSZWNlaXZlchI7CglzZWNyZXRSZWYYASABKAsyKC5rOHMuaW8uYXBpLmNvcmUudjEuTG9jYWxPYmplY3RSZWZlcmVuY2UijgIKD0dpdFN1YnNjcmlwdGlvbhIPCgdyZXBvVVJMGAEgASgJEh8KF2NvbW1pdFNlbGVjdGlvblN0cmF0ZWd5GAIgASgJEg4KBmJyYW5jaBgDIAEoCRIVCg1zdHJpY3RTZW12ZXJzGAsgASgIEhgKEHNlbXZlckNvbnN0cmFpbnQYBCABKAkSEQoJYWxsb3dUYWdzGAUgASgJEhIKCmlnbm9yZVRhZ3MYBiADKAkSHQoVaW5zZWN1cmVTa2lwVExTVmVyaWZ5GAcgASgIEhQKDGluY2x1ZGVQYXRocxgIIAMoCRIUCgxleGNsdWRlUGF0aHMYCSADKAkSFgoOZGlzY292ZXJ5TGltaXQYCiABKAUiyAEKBkhlYWx0aBIOCgZzdGF0dXMYASABKAkSDgoGaXNzdWVzGAIgAygJEk4KBmNvbmZpZxgEIAEoCzI+Lms4cy5pby5hcGlleHRlbnNpb25zX2FwaXNlcnZlci5wa2cuYXBpcy5hcGlleHRlbnNpb25zLnYxLkpTT04STgoGb3V0cHV0GAUgASgLMj4uazhzLmlvLmFwaWV4dGVuc2lvbnNfYXBpc2VydmVyLnBrZy5hcGlzLmFwaWV4dGVuc2lvbnMudjEuSlNPTiJvCg9IZWFsdGhDaGVja1N0ZXASDAoEdXNlcxgBIAEoCRJOCgZjb25maWcYAiABKAsyPi5rOHMuaW8uYXBpZXh0ZW5zaW9uc19hcGlzZXJ2ZXIucGtnLmFwaXMuYXBpZXh0ZW5zaW9ucy52MS5KU09OIh4KC0hlYWx0aFN0YXRzEg8KB2hlYWx0aHkYASABKAMi0AEKBUltYWdlEg8KB3JlcG9VUkwYASABKAkSEgoKZ2l0UmVwb1VSTBgCIAEoCRILCgN0YWcYAyABKAkSDgoGZGlnZXN0GAQgASgJElEKC2Fubm90YXRpb25zGAUgAygLMjwuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkltYWdlLkFubm90YXRpb25zRW50cnkaMgoQQW5ub3RhdGlvbnNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBIo0BChRJbWFnZURpc2NvdmVyeVJlc3VsdBIPCgdyZXBvVVJMGAEgASgJEhAKCHBsYXRmb3JtGAIgASgJElIKCnJlZmVyZW5jZXMYAyADKAsyPi5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuRGlzY292ZXJlZEltYWdlUmVmZXJlbmNlIvkBChFJbWFnZVN1YnNjcmlwdGlvbhIPCgdyZXBvVVJMGAEgASgJEhIKCmdpdFJlcG9VUkwYAiABKAkSHgoWaW1hZ2VTZWxlY3Rpb25TdHJhdGVneRgDIAEoCRIVCg1zdHJpY3RTZW12ZXJzGAogASgIEhgKEHNlbXZlckNvbnN0cmFpbnQYBCABKAkSEQoJYWxsb3dUYWdzGAUgASgJEhIKCmlnbm9yZVRhZ3MYBiADKAkSEAoIcGxhdGZvcm0YByABKAkSHQoVaW5zZWN1cmVTa2lwVExTVmVyaWZ5GAggASgIEhYKDmRpc2NvdmVyeUxpbWl0GAkgASgFItkBCgdQcm9qZWN0EkIKCG1ldGFkYXRhGAEgASgLMjAuazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLk9iamVjdE1ldGESRQoEc3BlYxgCIAEoCzI3LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9qZWN0Q29uZmlnU3BlYxJDCgZzdGF0dXMYAyABKAsyMy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvamVjdFN0YXR1cyLlAQoNUHJvamVjdENvbmZpZxJCCghtZXRhZGF0YRgBIAEoCzIwLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5PYmplY3RNZXRhEkUKBHNwZWMYAiABKAsyNy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvamVjdENvbmZpZ1NwZWMSSQoGc3RhdHVzGAMgASgLMjkuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb2plY3RDb25maWdTdGF0dXMimQEKEVByb2plY3RDb25maWdMaXN0EkAKCG1ldGFkYXRhGAEgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkxpc3RNZXRhEkIKBWl0ZW1zGAIgAygLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb2plY3RDb25maWcitQEKEVByb2plY3RDb25maWdTcGVjElAKEXByb21vdGlvblBvbGljaWVzGAEgAygLMjUuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb21vdGlvblBvbGljeRJOCglyZWNlaXZlcnMYAiADKAsyOy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuV2ViaG9va1JlY2VpdmVyQ29uZmlnIqQBChNQcm9qZWN0Q29uZmlnU3RhdHVzEkMKCmNvbmRpdGlvbnMYASADKAsyLy5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuQ29uZGl0aW9uEkgKCXJlY2VpdmVycxgCIAMoCzI1LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5XZWJob29rUmVjZWl2ZXIijQEKC1Byb2plY3RMaXN0EkAKCG1ldGFkYXRhGAEgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkxpc3RNZXRhEjwKBWl0ZW1zGAIgAygLMi0uZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb2plY3QimgEKDFByb2plY3RTdGF0cxJICgp3YXJlaG91c2VzGAEgASgLMjQuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLldhcmVob3VzZVN0YXRzEkAKBnN0YWdlcxgCIAEoCzIwLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5TdGFnZVN0YXRzIpcBCg1Qcm9qZWN0U3RhdHVzEkMKCmNvbmRpdGlvbnMYAyADKAsyLy5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuQ29uZGl0aW9uEkEKBXN0YXRzGAQgASgLMjIuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb2plY3RTdGF0cyLZAQoJUHJvbW90aW9uEkIKCG1ldGFkYXRhGAEgASgLMjAuazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLk9iamVjdE1ldGESQQoEc3BlYxgCIAEoCzIzLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25TcGVjEkUKBnN0YXR1cxgDIAEoCzI1LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25TdGF0dXMikQEKDVByb21vdGlvbkxpc3QSQAoIbWV0YWRhdGEYASABKAsyLi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuTGlzdE1ldGESPgoFaXRlbXMYAiADKAsyLy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uIpQBCg9Qcm9tb3Rpb25Qb2xpY3kSDQoFc3RhZ2UYASABKAkSVAoNc3RhZ2VTZWxlY3RvchgDIAEoCzI9LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25Qb2xpY3lTZWxlY3RvchIcChRhdXRvUHJvbW90aW9uRW5hYmxlZBgCIAEoCCJzChdQcm9tb3Rpb25Qb2xpY3lTZWxlY3RvchIMCgRuYW1lGAEgASgJEkoKDWxhYmVsU2VsZWN0b3IYAiABKAsyMy5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuTGFiZWxTZWxlY3RvciLyAQoSUHJvbW90aW9uUmVmZXJlbmNlEgwKBG5hbWUYASABKAkSRwoHZnJlaWdodBgCIAEoCzI2LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0UmVmZXJlbmNlEkUKBnN0YXR1cxgDIAEoCzI1LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25TdGF0dXMSPgoKZmluaXNoZWRBdBgEIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lIrsBCg1Qcm9tb3Rpb25TcGVjEg0KBXN0YWdlGAEgASgJEg8KB2ZyZWlnaHQYAiABKAkSRgoEdmFycxgEIAMoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5FeHByZXNzaW9uVmFyaWFibGUSQgoFc3RlcHMYAyADKAsyMy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uU3RlcCK3BAoPUHJvbW90aW9uU3RhdHVzEhoKEmxhc3RIYW5kbGVkUmVmcmVzaBgEIAEoCRINCgVwaGFzZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJEkcKB2ZyZWlnaHQYBSABKAsyNi5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuRnJlaWdodFJlZmVyZW5jZRJSChFmcmVpZ2h0Q29sbGVjdGlvbhgHIAEoCzI3LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0Q29sbGVjdGlvbhJLCgxoZWFsdGhDaGVja3MYCCADKAsyNS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuSGVhbHRoQ2hlY2tTdGVwEj4KCmZpbmlzaGVkQXQYBiABKAsyKi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuVGltZRITCgtjdXJyZW50U3RlcBgJIAEoAxJaChVzdGVwRXhlY3V0aW9uTWV0YWRhdGEYCyADKAsyOy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuU3RlcEV4ZWN1dGlvbk1ldGFkYXRhEk0KBXN0YXRlGAogASgLMj4uazhzLmlvLmFwaWV4dGVuc2lvbnNfYXBpc2VydmVyLnBrZy5hcGlzLmFwaWV4dGVuc2lvbnMudjEuSlNPTiL7AgoNUHJvbW90aW9uU3RlcBIMCgR1c2VzGAEgASgJEkoKBHRhc2sYBSABKAsyPC5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uVGFza1JlZmVyZW5jZRIKCgJhcxgCIAEoCRIKCgJpZhgHIAEoCRIXCg9jb250aW51ZU9uRXJyb3IYCCABKAgSRwoFcmV0cnkYBCABKAsyOC5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uU3RlcFJldHJ5EkYKBHZhcnMYBiADKAsyOC5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuRXhwcmVzc2lvblZhcmlhYmxlEk4KBmNvbmZpZxgDIAEoCzI+Lms4cy5pby5hcGlleHRlbnNpb25zX2FwaXNlcnZlci5wa2cuYXBpcy5hcGlleHRlbnNpb25zLnYxLkpTT04ibQoSUHJvbW90aW9uU3RlcFJldHJ5Ej8KB3RpbWVvdXQYASABKAsyLi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuRHVyYXRpb24SFgoOZXJyb3JUaHJlc2hvbGQYAiABKA0imgEKDVByb21vdGlvblRhc2sSQgoIbWV0YWRhdGEYASABKAsyMC5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuT2JqZWN0TWV0YRJFCgRzcGVjGAIgASgLMjcuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb21vdGlvblRhc2tTcGVjIpkBChFQcm9tb3Rpb25UYXNrTGlzdBJACghtZXRhZGF0YRgBIAEoCzIuLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5MaXN0TWV0YRJCCgVpdGVtcxgCIAMoCzIzLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25UYXNrIjQKFlByb21vdGlvblRhc2tSZWZlcmVuY2USDAoEbmFtZRgBIAEoCRIMCgRraW5kGAIgASgJIp8BChFQcm9tb3Rpb25UYXNrU3BlYxJGCgR2YXJzGAEgAygLMjguZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkV4cHJlc3Npb25WYXJpYWJsZRJCCgVzdGVwcxgCIAMoCzIzLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25TdGVwIl4KEVByb21vdGlvblRlbXBsYXRlEkkKBHNwZWMYASABKAsyOy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uVGVtcGxhdGVTcGVjIqMBChVQcm9tb3Rpb25UZW1wbGF0ZVNwZWMSRgoEdmFycxgCIAMoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5FeHByZXNzaW9uVmFyaWFibGUSQgoFc3RlcHMYASADKAsyMy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uU3RlcCLmAQoQUmVwb1N1YnNjcmlwdGlvbhJCCgNnaXQYASABKAsyNS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuR2l0U3Vic2NyaXB0aW9uEkYKBWltYWdlGAIgASgLMjcuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkltYWdlU3Vic2NyaXB0aW9uEkYKBWNoYXJ0GAMgASgLMjcuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkNoYXJ0U3Vic2NyaXB0aW9uIs0BCgVTdGFnZRJCCghtZXRhZGF0YRgBIAEoCzIwLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5PYmplY3RNZXRhEj0KBHNwZWMYAiABKAsyLy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuU3RhZ2VTcGVjEkEKBnN0YXR1cxgDIAEoCzIxLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5TdGFnZVN0YXR1cyKJAQoJU3RhZ2VMaXN0EkAKCG1ldGFkYXRhGAEgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkxpc3RNZXRhEjoKBWl0ZW1zGAIgAygLMisuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlN0YWdlItACCglTdGFnZVNwZWMSDQoFc2hhcmQYBCABKAkSRgoEdmFycxgHIAMoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5FeHByZXNzaW9uVmFyaWFibGUSTgoQcmVxdWVzdGVkRnJlaWdodBgFIAMoCzI0LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0UmVxdWVzdBJSChFwcm9tb3Rpb25UZW1wbGF0ZRgGIAEoCzI3LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25UZW1wbGF0ZRJICgx2ZXJpZmljYXRpb24YAyABKAsyMi5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuVmVyaWZpY2F0aW9uIl4KClN0YWdlU3RhdHMSDQoFY291bnQYAiABKAMSQQoGaGVhbHRoGAEgASgLMjEuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkhlYWx0aFN0YXRzItYDCgtTdGFnZVN0YXR1cxJDCgpjb25kaXRpb25zGA0gAygLMi8uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkNvbmRpdGlvbhIaChJsYXN0SGFuZGxlZFJlZnJlc2gYCyABKAkSTwoOZnJlaWdodEhpc3RvcnkYBCADKAsyNy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuRnJlaWdodENvbGxlY3Rpb24SFgoOZnJlaWdodFN1bW1hcnkYDCABKAkSPAoGaGVhbHRoGAggASgLMiwuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkhlYWx0aBIaChJvYnNlcnZlZEdlbmVyYXRpb24YBiABKAMSUgoQY3VycmVudFByb21vdGlvbhgHIAEoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25SZWZlcmVuY2USTwoNbGFzdFByb21vdGlvbhgKIAEoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25SZWZlcmVuY2Ui8wEKFVN0ZXBFeGVjdXRpb25NZXRhZGF0YRINCgVhbGlhcxgBIAEoCRI9CglzdGFydGVkQXQYAiABKAsyKi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuVGltZRI+CgpmaW5pc2hlZEF0GAMgASgLMiouazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLlRpbWUSEgoKZXJyb3JDb3VudBgEIAEoDRIOCgZzdGF0dXMYBSABKAkSDwoHbWVzc2FnZRgGIAEoCRIXCg9jb250aW51ZU9uRXJyb3IYByABKAgiiwIKDFZlcmlmaWNhdGlvbhJaChFhbmFseXNpc1RlbXBsYXRlcxgBIAMoCzI/LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5BbmFseXNpc1RlbXBsYXRlUmVmZXJlbmNlElYKE2FuYWx5c2lzUnVuTWV0YWRhdGEYAiABKAsyOS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuQW5hbHlzaXNSdW5NZXRhZGF0YRJHCgRhcmdzGAMgAygLMjkuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkFuYWx5c2lzUnVuQXJndW1lbnQinQIKEFZlcmlmaWNhdGlvbkluZm8SCgoCaWQYBCABKAkSDQoFYWN0b3IYByABKAkSPQoJc3RhcnRUaW1lGAUgASgLMiouazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLlRpbWUSDQoFcGhhc2UYASABKAkSDwoHbWVzc2FnZRgCIAEoCRJPCgthbmFseXNpc1J1bhgDIAEoCzI6LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5BbmFseXNpc1J1blJlZmVyZW5jZRI+CgpmaW5pc2hUaW1lGAYgASgLMiouazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLlRpbWUilAEKDVZlcmlmaWVkU3RhZ2USPgoKdmVyaWZpZWRBdBgBIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lEkMKC2xvbmdlc3RTb2FrGAIgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkR1cmF0aW9uItkBCglXYXJlaG91c2USQgoIbWV0YWRhdGEYASABKAsyMC5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuT2JqZWN0TWV0YRJBCgRzcGVjGAIgASgLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLldhcmVob3VzZVNwZWMSRQoGc3RhdHVzGAMgASgLMjUuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLldhcmVob3VzZVN0YXR1cyKRAQoNV2FyZWhvdXNlTGlzdBJACghtZXRhZGF0YRgBIAEoCzIuLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5MaXN0TWV0YRI+CgVpdGVtcxgCIAMoCzIvLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5XYXJlaG91c2UizgEKDVdhcmVob3VzZVNwZWMSDQoFc2hhcmQYAiABKAkSQAoIaW50ZXJ2YWwYBCABKAsyLi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuRHVyYXRpb24SHQoVZnJlaWdodENyZWF0aW9uUG9saWN5GAMgASgJEk0KDXN1YnNjcmlwdGlvbnMYASADKAsyNi5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUmVwb1N1YnNjcmlwdGlvbiJiCg5XYXJlaG91c2VTdGF0cxINCgVjb3VudBgCIAEoAxJBCgZoZWFsdGgYASABKAsyMS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuSGVhbHRoU3RhdHMi/QEKD1dhcmVob3VzZVN0YXR1cxJDCgpjb25kaXRpb25zGAkgAygLMi8uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkNvbmRpdGlvbhIaChJsYXN0SGFuZGxlZFJlZnJlc2gYBiABKAkSGgoSb2JzZXJ2ZWRHZW5lcmF0aW9uGAQgASgDEhUKDWxhc3RGcmVpZ2h0SUQYCCABKAkSVgoTZGlzY292ZXJlZEFydGlmYWN0cxgHIAEoCzI5LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5EaXNjb3ZlcmVkQXJ0aWZhY3RzIjoKD1dlYmhvb2tSZWNlaXZlchIMCgRuYW1lGAEgASgJEgwKBHBhdGgYAyABKAkSCwoDdXJsGAQgASgJInIKFVdlYmhvb2tSZWNlaXZlckNvbmZpZxIMCgRuYW1lGAEgASgJEksKBmdpdGh1YhgCIAEoCzI7LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5HaXRIdWJXZWJob29rUmVjZWl2ZXJClwIKKGNvbS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTFCDkdlbmVyYXRlZFByb3RvUAFaJGdpdGh1Yi5jb20vYWt1aXR5L2thcmdvL2FwaS92MWFscGhhMaICBUdDQUtBqgIkR2l0aHViLkNvbS5Ba3VpdHkuS2FyZ28uQXBpLlYxYWxwaGExygIkR2l0aHViXENvbVxBa3VpdHlcS2FyZ29cQXBpXFYxYWxwaGEx4gIwR2l0aHViXENvbVxBa3VpdHlcS2FyZ29cQXBpXFYxYWxwaGExXEdQQk1ldGFkYXRh6gIpR2l0aHViOjpDb206OkFrdWl0eTo6S2FyZ286OkFwaTo6VjFhbHBoYTE", [file_k8s_io_api_core_v1_generated, file_k8s_io_apiextensions_apiserver_pkg_apis_apiextensions_v1_generated, file_k8s_io_apimachinery_pkg_apis_meta_v1_generated, file_k8s_io_apimachinery_pkg_runtime_generated, file_k8s_io_apimachinery_pkg_runtime_schema_generated]);
+  fileDesc("ChxhcGkvdjFhbHBoYTEvZ2VuZXJhdGVkLnByb3RvEiRnaXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEiMgoTQW5hbHlzaXNSdW5Bcmd1bWVudBIMCgRuYW1lGAEgASgJEg0KBXZhbHVlGAIgASgJIrACChNBbmFseXNpc1J1bk1ldGFkYXRhElUKBmxhYmVscxgBIAMoCzJFLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5BbmFseXNpc1J1bk1ldGFkYXRhLkxhYmVsc0VudHJ5El8KC2Fubm90YXRpb25zGAIgAygLMkouZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkFuYWx5c2lzUnVuTWV0YWRhdGEuQW5ub3RhdGlvbnNFbnRyeRotCgtMYWJlbHNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBGjIKEEFubm90YXRpb25zRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASJGChRBbmFseXNpc1J1blJlZmVyZW5jZRIRCgluYW1lc3BhY2UYASABKAkSDAoEbmFtZRgCIAEoCRINCgVwaGFzZRgDIAEoCSI3ChlBbmFseXNpc1RlbXBsYXRlUmVmZXJlbmNlEgwKBG5hbWUYASABKAkSDAoEa2luZBgCIAEoCSJPCg1BcHByb3ZlZFN0YWdlEj4KCmFwcHJvdmVkQXQYASABKAsyKi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuVGltZSI4ChVBcmdvQ0RBcHBIZWFsdGhTdGF0dXMSDgoGc3RhdHVzGAEgASgJEg8KB21lc3NhZ2UYAiABKAki1AEKD0FyZ29DREFwcFN0YXR1cxIRCgluYW1lc3BhY2UYASABKAkSDAoEbmFtZRgCIAEoCRJRCgxoZWFsdGhTdGF0dXMYAyABKAsyOy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuQXJnb0NEQXBwSGVhbHRoU3RhdHVzEk0KCnN5bmNTdGF0dXMYBCABKAsyOS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuQXJnb0NEQXBwU3luY1N0YXR1cyJKChNBcmdvQ0RBcHBTeW5jU3RhdHVzEg4KBnN0YXR1cxgBIAEoCRIQCghyZXZpc2lvbhgCIAEoCRIRCglyZXZpc2lvbnMYAyADKAkiNwoFQ2hhcnQSDwoHcmVwb1VSTBgBIAEoCRIMCgRuYW1lGAIgASgJEg8KB3ZlcnNpb24YAyABKAkiYQoUQ2hhcnREaXNjb3ZlcnlSZXN1bHQSDwoHcmVwb1VSTBgBIAEoCRIMCgRuYW1lGAIgASgJEhgKEHNlbXZlckNvbnN0cmFpbnQYAyABKAkSEAoIdmVyc2lvbnMYBCADKAkiZAoRQ2hhcnRTdWJzY3JpcHRpb24SDwoHcmVwb1VSTBgBIAEoCRIMCgRuYW1lGAIgASgJEhgKEHNlbXZlckNvbnN0cmFpbnQYAyABKAkSFgoOZGlzY292ZXJ5TGltaXQYBCABKAUioQEKFENsdXN0ZXJQcm9tb3Rpb25UYXNrEkIKCG1ldGFkYXRhGAEgASgLMjAuazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLk9iamVjdE1ldGESRQoEc3BlYxgCIAEoCzI3LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25UYXNrU3BlYyKnAQoYQ2x1c3RlclByb21vdGlvblRhc2tMaXN0EkAKCG1ldGFkYXRhGAEgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkxpc3RNZXRhEkkKBWl0ZW1zGAIgAygLMjouZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkNsdXN0ZXJQcm9tb3Rpb25UYXNrIkkKDEN1cnJlbnRTdGFnZRI5CgVzaW5jZRgBIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lIrYCChNEaXNjb3ZlcmVkQXJ0aWZhY3RzEkAKDGRpc2NvdmVyZWRBdBgEIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lEkUKA2dpdBgBIAMoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5HaXREaXNjb3ZlcnlSZXN1bHQSSgoGaW1hZ2VzGAIgAygLMjouZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkltYWdlRGlzY292ZXJ5UmVzdWx0EkoKBmNoYXJ0cxgDIAMoCzI6LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5DaGFydERpc2NvdmVyeVJlc3VsdCKwAQoQRGlzY292ZXJlZENvbW1pdBIKCgJpZBgBIAEoCRIOCgZicmFuY2gYAiABKAkSCwoDdGFnGAMgASgJEg8KB3N1YmplY3QYBCABKAkSDgoGYXV0aG9yGAUgASgJEhEKCWNvbW1pdHRlchgGIAEoCRI/CgtjcmVhdG9yRGF0ZRgHIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lIqQCChhEaXNjb3ZlcmVkSW1hZ2VSZWZlcmVuY2USCwoDdGFnGAEgASgJEg4KBmRpZ2VzdBgCIAEoCRJkCgthbm5vdGF0aW9ucxgFIAMoCzJPLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5EaXNjb3ZlcmVkSW1hZ2VSZWZlcmVuY2UuQW5ub3RhdGlvbnNFbnRyeRISCgpnaXRSZXBvVVJMGAMgASgJEj0KCWNyZWF0ZWRBdBgEIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lGjIKEEFubm90YXRpb25zRW50cnkSCwoDa2V5GAEgASgJEg0KBXZhbHVlGAIgASgJOgI4ASIxChJFeHByZXNzaW9uVmFyaWFibGUSDAoEbmFtZRgBIAEoCRINCgV2YWx1ZRgCIAEoCSKiAwoHRnJlaWdodBJCCghtZXRhZGF0YRgBIAEoCzIwLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5PYmplY3RNZXRhEg0KBWFsaWFzGAcgASgJEkMKBm9yaWdpbhgJIAEoCzIzLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0T3JpZ2luEkAKB2NvbW1pdHMYAyADKAsyLy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuR2l0Q29tbWl0EjsKBmltYWdlcxgEIAMoCzIrLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5JbWFnZRI7CgZjaGFydHMYBSADKAsyKy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuQ2hhcnQSQwoGc3RhdHVzGAYgASgLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRTdGF0dXMirQIKEUZyZWlnaHRDb2xsZWN0aW9uEgoKAmlkGAMgASgJElEKBWl0ZW1zGAEgAygLMkIuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRDb2xsZWN0aW9uLkl0ZW1zRW50cnkSUwoTdmVyaWZpY2F0aW9uSGlzdG9yeRgCIAMoCzI2LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5WZXJpZmljYXRpb25JbmZvGmQKCkl0ZW1zRW50cnkSCwoDa2V5GAEgASgJEkUKBXZhbHVlGAIgASgLMjYuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRSZWZlcmVuY2U6AjgBIo0BCgtGcmVpZ2h0TGlzdBJACghtZXRhZGF0YRgBIAEoCzIuLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5MaXN0TWV0YRI8CgVpdGVtcxgCIAMoCzItLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0IisKDUZyZWlnaHRPcmlnaW4SDAoEa2luZBgBIAEoCRIMCgRuYW1lGAIgASgJIqECChBGcmVpZ2h0UmVmZXJlbmNlEgwKBG5hbWUYASABKAkSQwoGb3JpZ2luGAggASgLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRPcmlnaW4SQAoHY29tbWl0cxgCIAMoCzIvLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5HaXRDb21taXQSOwoGaW1hZ2VzGAMgAygLMisuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkltYWdlEjsKBmNoYXJ0cxgEIAMoCzIrLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5DaGFydCKcAQoORnJlaWdodFJlcXVlc3QSQwoGb3JpZ2luGAEgASgLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRPcmlnaW4SRQoHc291cmNlcxgCIAEoCzI0LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0U291cmNlcyKYAQoORnJlaWdodFNvdXJjZXMSDgoGZGlyZWN0GAEgASgIEg4KBnN0YWdlcxgCIAMoCRJIChByZXF1aXJlZFNvYWtUaW1lGAMgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkR1cmF0aW9uEhwKFGF2YWlsYWJpbGl0eVN0cmF0ZWd5GAQgASgJIp0GCg1GcmVpZ2h0U3RhdHVzElkKC2N1cnJlbnRseUluGAMgAygLMkQuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRTdGF0dXMuQ3VycmVudGx5SW5FbnRyeRJXCgp2ZXJpZmllZEluGAEgAygLMkMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRTdGF0dXMuVmVyaWZpZWRJbkVudHJ5ElkKC2FwcHJvdmVkRm9yGAIgAygLMkQuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkZyZWlnaHRTdGF0dXMuQXBwcm92ZWRGb3JFbnRyeRJTCghtZXRhZGF0YRgEIAMoCzJBLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0U3RhdHVzLk1ldGFkYXRhRW50cnkaZgoQQ3VycmVudGx5SW5FbnRyeRILCgNrZXkYASABKAkSQQoFdmFsdWUYAiABKAsyMi5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuQ3VycmVudFN0YWdlOgI4ARpmCg9WZXJpZmllZEluRW50cnkSCwoDa2V5GAEgASgJEkIKBXZhbHVlGAIgASgLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlZlcmlmaWVkU3RhZ2U6AjgBGmcKEEFwcHJvdmVkRm9yRW50cnkSCwoDa2V5GAEgASgJEkIKBXZhbHVlGAIgASgLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkFwcHJvdmVkU3RhZ2U6AjgBGm8KDU1ldGFkYXRhRW50cnkSCwoDa2V5GAEgASgJEk0KBXZhbHVlGAIgASgLMj4uazhzLmlvLmFwaWV4dGVuc2lvbnNfYXBpc2VydmVyLnBrZy5hcGlzLmFwaWV4dGVuc2lvbnMudjEuSlNPTjoCOAEieQoJR2l0Q29tbWl0Eg8KB3JlcG9VUkwYASABKAkSCgoCaWQYAiABKAkSDgoGYnJhbmNoGAMgASgJEgsKA3RhZxgEIAEoCRIPCgdtZXNzYWdlGAYgASgJEg4KBmF1dGhvchgHIAEoCRIRCgljb21taXR0ZXIYCCABKAkibgoSR2l0RGlzY292ZXJ5UmVzdWx0Eg8KB3JlcG9VUkwYASABKAkSRwoHY29tbWl0cxgCIAMoCzI2LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5EaXNjb3ZlcmVkQ29tbWl0IlQKFUdpdEh1YldlYmhvb2tSZWNlaXZlchI7CglzZWNyZXRSZWYYASABKAsyKC5rOHMuaW8uYXBpLmNvcmUudjEuTG9jYWxPYmplY3RSZWZlcmVuY2UijgIKD0dpdFN1YnNjcmlwdGlvbhIPCgdyZXBvVVJMGAEgASgJEh8KF2NvbW1pdFNlbGVjdGlvblN0cmF0ZWd5GAIgASgJEg4KBmJyYW5jaBgDIAEoCRIVCg1zdHJpY3RTZW12ZXJzGAsgASgIEhgKEHNlbXZlckNvbnN0cmFpbnQYBCABKAkSEQoJYWxsb3dUYWdzGAUgASgJEhIKCmlnbm9yZVRhZ3MYBiADKAkSHQoVaW5zZWN1cmVTa2lwVExTVmVyaWZ5GAcgASgIEhQKDGluY2x1ZGVQYXRocxgIIAMoCRIUCgxleGNsdWRlUGF0aHMYCSADKAkSFgoOZGlzY292ZXJ5TGltaXQYCiABKAUiyAEKBkhlYWx0aBIOCgZzdGF0dXMYASABKAkSDgoGaXNzdWVzGAIgAygJEk4KBmNvbmZpZxgEIAEoCzI+Lms4cy5pby5hcGlleHRlbnNpb25zX2FwaXNlcnZlci5wa2cuYXBpcy5hcGlleHRlbnNpb25zLnYxLkpTT04STgoGb3V0cHV0GAUgASgLMj4uazhzLmlvLmFwaWV4dGVuc2lvbnNfYXBpc2VydmVyLnBrZy5hcGlzLmFwaWV4dGVuc2lvbnMudjEuSlNPTiJvCg9IZWFsdGhDaGVja1N0ZXASDAoEdXNlcxgBIAEoCRJOCgZjb25maWcYAiABKAsyPi5rOHMuaW8uYXBpZXh0ZW5zaW9uc19hcGlzZXJ2ZXIucGtnLmFwaXMuYXBpZXh0ZW5zaW9ucy52MS5KU09OIh4KC0hlYWx0aFN0YXRzEg8KB2hlYWx0aHkYASABKAMi0AEKBUltYWdlEg8KB3JlcG9VUkwYASABKAkSEgoKZ2l0UmVwb1VSTBgCIAEoCRILCgN0YWcYAyABKAkSDgoGZGlnZXN0GAQgASgJElEKC2Fubm90YXRpb25zGAUgAygLMjwuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkltYWdlLkFubm90YXRpb25zRW50cnkaMgoQQW5ub3RhdGlvbnNFbnRyeRILCgNrZXkYASABKAkSDQoFdmFsdWUYAiABKAk6AjgBIo0BChRJbWFnZURpc2NvdmVyeVJlc3VsdBIPCgdyZXBvVVJMGAEgASgJEhAKCHBsYXRmb3JtGAIgASgJElIKCnJlZmVyZW5jZXMYAyADKAsyPi5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuRGlzY292ZXJlZEltYWdlUmVmZXJlbmNlIvkBChFJbWFnZVN1YnNjcmlwdGlvbhIPCgdyZXBvVVJMGAEgASgJEhIKCmdpdFJlcG9VUkwYAiABKAkSHgoWaW1hZ2VTZWxlY3Rpb25TdHJhdGVneRgDIAEoCRIVCg1zdHJpY3RTZW12ZXJzGAogASgIEhgKEHNlbXZlckNvbnN0cmFpbnQYBCABKAkSEQoJYWxsb3dUYWdzGAUgASgJEhIKCmlnbm9yZVRhZ3MYBiADKAkSEAoIcGxhdGZvcm0YByABKAkSHQoVaW5zZWN1cmVTa2lwVExTVmVyaWZ5GAggASgIEhYKDmRpc2NvdmVyeUxpbWl0GAkgASgFItkBCgdQcm9qZWN0EkIKCG1ldGFkYXRhGAEgASgLMjAuazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLk9iamVjdE1ldGESRQoEc3BlYxgCIAEoCzI3LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9qZWN0Q29uZmlnU3BlYxJDCgZzdGF0dXMYAyABKAsyMy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvamVjdFN0YXR1cyLlAQoNUHJvamVjdENvbmZpZxJCCghtZXRhZGF0YRgBIAEoCzIwLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5PYmplY3RNZXRhEkUKBHNwZWMYAiABKAsyNy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvamVjdENvbmZpZ1NwZWMSSQoGc3RhdHVzGAMgASgLMjkuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb2plY3RDb25maWdTdGF0dXMimQEKEVByb2plY3RDb25maWdMaXN0EkAKCG1ldGFkYXRhGAEgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkxpc3RNZXRhEkIKBWl0ZW1zGAIgAygLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb2plY3RDb25maWcitQEKEVByb2plY3RDb25maWdTcGVjElAKEXByb21vdGlvblBvbGljaWVzGAEgAygLMjUuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb21vdGlvblBvbGljeRJOCglyZWNlaXZlcnMYAiADKAsyOy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuV2ViaG9va1JlY2VpdmVyQ29uZmlnIqQBChNQcm9qZWN0Q29uZmlnU3RhdHVzEkMKCmNvbmRpdGlvbnMYASADKAsyLy5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuQ29uZGl0aW9uEkgKCXJlY2VpdmVycxgCIAMoCzI1LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5XZWJob29rUmVjZWl2ZXIijQEKC1Byb2plY3RMaXN0EkAKCG1ldGFkYXRhGAEgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkxpc3RNZXRhEjwKBWl0ZW1zGAIgAygLMi0uZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb2plY3QimgEKDFByb2plY3RTdGF0cxJICgp3YXJlaG91c2VzGAEgASgLMjQuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLldhcmVob3VzZVN0YXRzEkAKBnN0YWdlcxgCIAEoCzIwLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5TdGFnZVN0YXRzIpcBCg1Qcm9qZWN0U3RhdHVzEkMKCmNvbmRpdGlvbnMYAyADKAsyLy5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuQ29uZGl0aW9uEkEKBXN0YXRzGAQgASgLMjIuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb2plY3RTdGF0cyLZAQoJUHJvbW90aW9uEkIKCG1ldGFkYXRhGAEgASgLMjAuazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLk9iamVjdE1ldGESQQoEc3BlYxgCIAEoCzIzLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25TcGVjEkUKBnN0YXR1cxgDIAEoCzI1LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25TdGF0dXMikQEKDVByb21vdGlvbkxpc3QSQAoIbWV0YWRhdGEYASABKAsyLi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuTGlzdE1ldGESPgoFaXRlbXMYAiADKAsyLy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uIpQBCg9Qcm9tb3Rpb25Qb2xpY3kSDQoFc3RhZ2UYASABKAkSVAoNc3RhZ2VTZWxlY3RvchgDIAEoCzI9LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25Qb2xpY3lTZWxlY3RvchIcChRhdXRvUHJvbW90aW9uRW5hYmxlZBgCIAEoCCJzChdQcm9tb3Rpb25Qb2xpY3lTZWxlY3RvchIMCgRuYW1lGAEgASgJEkoKDWxhYmVsU2VsZWN0b3IYAiABKAsyMy5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuTGFiZWxTZWxlY3RvciLyAQoSUHJvbW90aW9uUmVmZXJlbmNlEgwKBG5hbWUYASABKAkSRwoHZnJlaWdodBgCIAEoCzI2LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0UmVmZXJlbmNlEkUKBnN0YXR1cxgDIAEoCzI1LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25TdGF0dXMSPgoKZmluaXNoZWRBdBgEIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lIrsBCg1Qcm9tb3Rpb25TcGVjEg0KBXN0YWdlGAEgASgJEg8KB2ZyZWlnaHQYAiABKAkSRgoEdmFycxgEIAMoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5FeHByZXNzaW9uVmFyaWFibGUSQgoFc3RlcHMYAyADKAsyMy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uU3RlcCK3BAoPUHJvbW90aW9uU3RhdHVzEhoKEmxhc3RIYW5kbGVkUmVmcmVzaBgEIAEoCRINCgVwaGFzZRgBIAEoCRIPCgdtZXNzYWdlGAIgASgJEkcKB2ZyZWlnaHQYBSABKAsyNi5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuRnJlaWdodFJlZmVyZW5jZRJSChFmcmVpZ2h0Q29sbGVjdGlvbhgHIAEoCzI3LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0Q29sbGVjdGlvbhJLCgxoZWFsdGhDaGVja3MYCCADKAsyNS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuSGVhbHRoQ2hlY2tTdGVwEj4KCmZpbmlzaGVkQXQYBiABKAsyKi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuVGltZRITCgtjdXJyZW50U3RlcBgJIAEoAxJaChVzdGVwRXhlY3V0aW9uTWV0YWRhdGEYCyADKAsyOy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuU3RlcEV4ZWN1dGlvbk1ldGFkYXRhEk0KBXN0YXRlGAogASgLMj4uazhzLmlvLmFwaWV4dGVuc2lvbnNfYXBpc2VydmVyLnBrZy5hcGlzLmFwaWV4dGVuc2lvbnMudjEuSlNPTiL7AgoNUHJvbW90aW9uU3RlcBIMCgR1c2VzGAEgASgJEkoKBHRhc2sYBSABKAsyPC5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uVGFza1JlZmVyZW5jZRIKCgJhcxgCIAEoCRIKCgJpZhgHIAEoCRIXCg9jb250aW51ZU9uRXJyb3IYCCABKAgSRwoFcmV0cnkYBCABKAsyOC5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uU3RlcFJldHJ5EkYKBHZhcnMYBiADKAsyOC5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuRXhwcmVzc2lvblZhcmlhYmxlEk4KBmNvbmZpZxgDIAEoCzI+Lms4cy5pby5hcGlleHRlbnNpb25zX2FwaXNlcnZlci5wa2cuYXBpcy5hcGlleHRlbnNpb25zLnYxLkpTT04ibQoSUHJvbW90aW9uU3RlcFJldHJ5Ej8KB3RpbWVvdXQYASABKAsyLi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuRHVyYXRpb24SFgoOZXJyb3JUaHJlc2hvbGQYAiABKA0imgEKDVByb21vdGlvblRhc2sSQgoIbWV0YWRhdGEYASABKAsyMC5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuT2JqZWN0TWV0YRJFCgRzcGVjGAIgASgLMjcuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlByb21vdGlvblRhc2tTcGVjIpkBChFQcm9tb3Rpb25UYXNrTGlzdBJACghtZXRhZGF0YRgBIAEoCzIuLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5MaXN0TWV0YRJCCgVpdGVtcxgCIAMoCzIzLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25UYXNrIjQKFlByb21vdGlvblRhc2tSZWZlcmVuY2USDAoEbmFtZRgBIAEoCRIMCgRraW5kGAIgASgJIp8BChFQcm9tb3Rpb25UYXNrU3BlYxJGCgR2YXJzGAEgAygLMjguZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkV4cHJlc3Npb25WYXJpYWJsZRJCCgVzdGVwcxgCIAMoCzIzLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25TdGVwIl4KEVByb21vdGlvblRlbXBsYXRlEkkKBHNwZWMYASABKAsyOy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uVGVtcGxhdGVTcGVjIqMBChVQcm9tb3Rpb25UZW1wbGF0ZVNwZWMSRgoEdmFycxgCIAMoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5FeHByZXNzaW9uVmFyaWFibGUSQgoFc3RlcHMYASADKAsyMy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUHJvbW90aW9uU3RlcCLmAQoQUmVwb1N1YnNjcmlwdGlvbhJCCgNnaXQYASABKAsyNS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuR2l0U3Vic2NyaXB0aW9uEkYKBWltYWdlGAIgASgLMjcuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkltYWdlU3Vic2NyaXB0aW9uEkYKBWNoYXJ0GAMgASgLMjcuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkNoYXJ0U3Vic2NyaXB0aW9uIs0BCgVTdGFnZRJCCghtZXRhZGF0YRgBIAEoCzIwLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5PYmplY3RNZXRhEj0KBHNwZWMYAiABKAsyLy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuU3RhZ2VTcGVjEkEKBnN0YXR1cxgDIAEoCzIxLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5TdGFnZVN0YXR1cyKJAQoJU3RhZ2VMaXN0EkAKCG1ldGFkYXRhGAEgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkxpc3RNZXRhEjoKBWl0ZW1zGAIgAygLMisuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLlN0YWdlItACCglTdGFnZVNwZWMSDQoFc2hhcmQYBCABKAkSRgoEdmFycxgHIAMoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5FeHByZXNzaW9uVmFyaWFibGUSTgoQcmVxdWVzdGVkRnJlaWdodBgFIAMoCzI0LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5GcmVpZ2h0UmVxdWVzdBJSChFwcm9tb3Rpb25UZW1wbGF0ZRgGIAEoCzI3LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25UZW1wbGF0ZRJICgx2ZXJpZmljYXRpb24YAyABKAsyMi5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuVmVyaWZpY2F0aW9uIl4KClN0YWdlU3RhdHMSDQoFY291bnQYAiABKAMSQQoGaGVhbHRoGAEgASgLMjEuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkhlYWx0aFN0YXRzItYDCgtTdGFnZVN0YXR1cxJDCgpjb25kaXRpb25zGA0gAygLMi8uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkNvbmRpdGlvbhIaChJsYXN0SGFuZGxlZFJlZnJlc2gYCyABKAkSTwoOZnJlaWdodEhpc3RvcnkYBCADKAsyNy5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuRnJlaWdodENvbGxlY3Rpb24SFgoOZnJlaWdodFN1bW1hcnkYDCABKAkSPAoGaGVhbHRoGAggASgLMiwuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkhlYWx0aBIaChJvYnNlcnZlZEdlbmVyYXRpb24YBiABKAMSUgoQY3VycmVudFByb21vdGlvbhgHIAEoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25SZWZlcmVuY2USTwoNbGFzdFByb21vdGlvbhgKIAEoCzI4LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5Qcm9tb3Rpb25SZWZlcmVuY2Ui8wEKFVN0ZXBFeGVjdXRpb25NZXRhZGF0YRINCgVhbGlhcxgBIAEoCRI9CglzdGFydGVkQXQYAiABKAsyKi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuVGltZRI+CgpmaW5pc2hlZEF0GAMgASgLMiouazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLlRpbWUSEgoKZXJyb3JDb3VudBgEIAEoDRIOCgZzdGF0dXMYBSABKAkSDwoHbWVzc2FnZRgGIAEoCRIXCg9jb250aW51ZU9uRXJyb3IYByABKAgiiwIKDFZlcmlmaWNhdGlvbhJaChFhbmFseXNpc1RlbXBsYXRlcxgBIAMoCzI/LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5BbmFseXNpc1RlbXBsYXRlUmVmZXJlbmNlElYKE2FuYWx5c2lzUnVuTWV0YWRhdGEYAiABKAsyOS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuQW5hbHlzaXNSdW5NZXRhZGF0YRJHCgRhcmdzGAMgAygLMjkuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLkFuYWx5c2lzUnVuQXJndW1lbnQinQIKEFZlcmlmaWNhdGlvbkluZm8SCgoCaWQYBCABKAkSDQoFYWN0b3IYByABKAkSPQoJc3RhcnRUaW1lGAUgASgLMiouazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLlRpbWUSDQoFcGhhc2UYASABKAkSDwoHbWVzc2FnZRgCIAEoCRJPCgthbmFseXNpc1J1bhgDIAEoCzI6LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5BbmFseXNpc1J1blJlZmVyZW5jZRI+CgpmaW5pc2hUaW1lGAYgASgLMiouazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLlRpbWUilAEKDVZlcmlmaWVkU3RhZ2USPgoKdmVyaWZpZWRBdBgBIAEoCzIqLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5UaW1lEkMKC2xvbmdlc3RTb2FrGAIgASgLMi4uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkR1cmF0aW9uItkBCglXYXJlaG91c2USQgoIbWV0YWRhdGEYASABKAsyMC5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuT2JqZWN0TWV0YRJBCgRzcGVjGAIgASgLMjMuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLldhcmVob3VzZVNwZWMSRQoGc3RhdHVzGAMgASgLMjUuZ2l0aHViLmNvbS5ha3VpdHkua2FyZ28uYXBpLnYxYWxwaGExLldhcmVob3VzZVN0YXR1cyKRAQoNV2FyZWhvdXNlTGlzdBJACghtZXRhZGF0YRgBIAEoCzIuLms4cy5pby5hcGltYWNoaW5lcnkucGtnLmFwaXMubWV0YS52MS5MaXN0TWV0YRI+CgVpdGVtcxgCIAMoCzIvLmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5XYXJlaG91c2UizgEKDVdhcmVob3VzZVNwZWMSDQoFc2hhcmQYAiABKAkSQAoIaW50ZXJ2YWwYBCABKAsyLi5rOHMuaW8uYXBpbWFjaGluZXJ5LnBrZy5hcGlzLm1ldGEudjEuRHVyYXRpb24SHQoVZnJlaWdodENyZWF0aW9uUG9saWN5GAMgASgJEk0KDXN1YnNjcmlwdGlvbnMYASADKAsyNi5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuUmVwb1N1YnNjcmlwdGlvbiJiCg5XYXJlaG91c2VTdGF0cxINCgVjb3VudBgCIAEoAxJBCgZoZWFsdGgYASABKAsyMS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTEuSGVhbHRoU3RhdHMi/QEKD1dhcmVob3VzZVN0YXR1cxJDCgpjb25kaXRpb25zGAkgAygLMi8uazhzLmlvLmFwaW1hY2hpbmVyeS5wa2cuYXBpcy5tZXRhLnYxLkNvbmRpdGlvbhIaChJsYXN0SGFuZGxlZFJlZnJlc2gYBiABKAkSGgoSb2JzZXJ2ZWRHZW5lcmF0aW9uGAQgASgDEhUKDWxhc3RGcmVpZ2h0SUQYCCABKAkSVgoTZGlzY292ZXJlZEFydGlmYWN0cxgHIAEoCzI5LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5EaXNjb3ZlcmVkQXJ0aWZhY3RzIjoKD1dlYmhvb2tSZWNlaXZlchIMCgRuYW1lGAEgASgJEgwKBHBhdGgYAyABKAkSCwoDdXJsGAQgASgJInIKFVdlYmhvb2tSZWNlaXZlckNvbmZpZxIMCgRuYW1lGAEgASgJEksKBmdpdGh1YhgCIAEoCzI7LmdpdGh1Yi5jb20uYWt1aXR5LmthcmdvLmFwaS52MWFscGhhMS5HaXRIdWJXZWJob29rUmVjZWl2ZXJClwIKKGNvbS5naXRodWIuY29tLmFrdWl0eS5rYXJnby5hcGkudjFhbHBoYTFCDkdlbmVyYXRlZFByb3RvUAFaJGdpdGh1Yi5jb20vYWt1aXR5L2thcmdvL2FwaS92MWFscGhhMaICBUdDQUtBqgIkR2l0aHViLkNvbS5Ba3VpdHkuS2FyZ28uQXBpLlYxYWxwaGExygIkR2l0aHViXENvbVxBa3VpdHlcS2FyZ29cQXBpXFYxYWxwaGEx4gIwR2l0aHViXENvbVxBa3VpdHlcS2FyZ29cQXBpXFYxYWxwaGExXEdQQk1ldGFkYXRh6gIpR2l0aHViOjpDb206OkFrdWl0eTo6S2FyZ286OkFwaTo6VjFhbHBoYTE", [file_k8s_io_api_core_v1_generated, file_k8s_io_apiextensions_apiserver_pkg_apis_apiextensions_v1_generated, file_k8s_io_apimachinery_pkg_apis_meta_v1_generated, file_k8s_io_apimachinery_pkg_runtime_generated, file_k8s_io_apimachinery_pkg_runtime_schema_generated]);
 
 /**
  * AnalysisRunArgument represents an argument to be added to an AnalysisRun.
@@ -1092,6 +1092,15 @@ export type FreightStatus = Message<"github.com.akuity.kargo.api.v1alpha1.Freigh
    * @generated from field: map<string, github.com.akuity.kargo.api.v1alpha1.ApprovedStage> approvedFor = 2;
    */
   approvedFor: { [key: string]: ApprovedStage };
+
+  /**
+   * Metadata is a map of arbitrary metadata associated with the Freight.
+   * This is useful for storing additional information about the Freight
+   * or Promotion that can be shared across steps or stages.
+   *
+   * @generated from field: map<string, k8s.io.apiextensions_apiserver.pkg.apis.apiextensions.v1.JSON> metadata = 4;
+   */
+  metadata: { [key: string]: JSON };
 };
 
 /**
@@ -1908,9 +1917,6 @@ export type ProjectConfigSpec = Message<"github.com.akuity.kargo.api.v1alpha1.Pr
    * WebhookReceivers describes Project-specific webhook receivers used for
    * processing events from various external platforms
    *
-   * +kubebuilder:validation:MaxItems=5
-   * +kubebuilder:validation:XValidation:message="WebhookReceiverConfig must have a unique name",rule="self.all(i, size(self.filter(j, i.name == j.name)) == 1)"
-   *
    * @generated from field: repeated github.com.akuity.kargo.api.v1alpha1.WebhookReceiverConfig receivers = 2;
    */
   receivers: WebhookReceiverConfig[];
@@ -3141,6 +3147,8 @@ export type VerificationInfo = Message<"github.com.akuity.kargo.api.v1alpha1.Ver
   /**
    * ID is the identifier of the Verification process.
    *
+   * +kubebuilder:validation:Required
+   *
    * @generated from field: optional string id = 4;
    */
   id: string;
@@ -3156,6 +3164,8 @@ export type VerificationInfo = Message<"github.com.akuity.kargo.api.v1alpha1.Ver
   /**
    * StartTime is the time at which the Verification process was started.
    *
+   * +kubebuilder:validation:Required
+   *
    * @generated from field: optional k8s.io.apimachinery.pkg.apis.meta.v1.Time startTime = 5;
    */
   startTime?: Time;
@@ -3166,6 +3176,8 @@ export type VerificationInfo = Message<"github.com.akuity.kargo.api.v1alpha1.Ver
    * there are exceptions to this, such as in the case where an AnalysisRun
    * cannot be launched successfully.
    *
+   * +kubebuilder:validation:Required
+   *
    * @generated from field: optional string phase = 1;
    */
   phase: string;
diff --git a/ui/src/gen/directives/git-clone-config.json b/ui/src/gen/directives/git-clone-config.json
index 7e26261a..108aa48c 100644
--- a/ui/src/gen/directives/git-clone-config.json
+++ b/ui/src/gen/directives/git-clone-config.json
@@ -41,6 +41,10 @@
     "type": "object",
     "additionalProperties": false,
     "properties": {
+     "as": {
+      "type": "string",
+      "description": "Optional alias for this checkout. If provided, it will be used as the key in the 'commits' output map."
+     },
      "branch": {
       "type": "string",
       "description": "The branch to checkout. Mutually exclusive with 'commit' and 'tag'. If none of these are specified, the default branch is checked out."
diff --git a/ui/src/gen/schema/freights.kargo.akuity.io_v1alpha1.json b/ui/src/gen/schema/freights.kargo.akuity.io_v1alpha1.json
index e11cce39..47399aa3 100644
--- a/ui/src/gen/schema/freights.kargo.akuity.io_v1alpha1.json
+++ b/ui/src/gen/schema/freights.kargo.akuity.io_v1alpha1.json
@@ -164,6 +164,13 @@
           "description": "CurrentlyIn describes the Stages in which this Freight is currently in use.",
           "type": "object"
         },
+        "metadata": {
+          "additionalProperties": {
+            "x-kubernetes-preserve-unknown-fields": true
+          },
+          "description": "Metadata is a map of arbitrary metadata associated with the Freight.\nThis is useful for storing additional information about the Freight\nor Promotion that can be shared across steps or stages.",
+          "type": "object"
+        },
         "verifiedIn": {
           "additionalProperties": {
             "description": "VerifiedStage describes a Stage in which Freight has been verified.",
diff --git a/ui/src/gen/schema/projectconfigs.kargo.akuity.io_v1alpha1.json b/ui/src/gen/schema/projectconfigs.kargo.akuity.io_v1alpha1.json
index 3e03b5f6..05b3c312 100644
--- a/ui/src/gen/schema/projectconfigs.kargo.akuity.io_v1alpha1.json
+++ b/ui/src/gen/schema/projectconfigs.kargo.akuity.io_v1alpha1.json
@@ -126,14 +126,7 @@
             ],
             "type": "object"
           },
-          "maxItems": 5,
-          "type": "array",
-          "x-kubernetes-validations": [
-            {
-              "message": "WebhookReceiverConfig must have a unique name",
-              "rule": "self.all(i, size(self.filter(j, i.name == j.name)) == 1)"
-            }
-          ]
+          "type": "array"
         }
       },
       "type": "object"
diff --git a/ui/src/gen/schema/projects.kargo.akuity.io_v1alpha1.json b/ui/src/gen/schema/projects.kargo.akuity.io_v1alpha1.json
index 3d3f8b57..00a5e52f 100644
--- a/ui/src/gen/schema/projects.kargo.akuity.io_v1alpha1.json
+++ b/ui/src/gen/schema/projects.kargo.akuity.io_v1alpha1.json
@@ -126,14 +126,7 @@
             ],
             "type": "object"
           },
-          "maxItems": 5,
-          "type": "array",
-          "x-kubernetes-validations": [
-            {
-              "message": "WebhookReceiverConfig must have a unique name",
-              "rule": "self.all(i, size(self.filter(j, i.name == j.name)) == 1)"
-            }
-          ]
+          "type": "array"
         }
       },
       "type": "object"
diff --git a/ui/src/gen/schema/promotions.kargo.akuity.io_v1alpha1.json b/ui/src/gen/schema/promotions.kargo.akuity.io_v1alpha1.json
index 9a6a5501..f12d7d7c 100644
--- a/ui/src/gen/schema/promotions.kargo.akuity.io_v1alpha1.json
+++ b/ui/src/gen/schema/promotions.kargo.akuity.io_v1alpha1.json
@@ -492,6 +492,11 @@
                     "type": "string"
                   }
                 },
+                "required": [
+                  "id",
+                  "phase",
+                  "startTime"
+                ],
                 "type": "object"
               },
               "type": "array"
diff --git a/ui/src/gen/schema/stages.kargo.akuity.io_v1alpha1.json b/ui/src/gen/schema/stages.kargo.akuity.io_v1alpha1.json
index beaaa225..2b9450c6 100644
--- a/ui/src/gen/schema/stages.kargo.akuity.io_v1alpha1.json
+++ b/ui/src/gen/schema/stages.kargo.akuity.io_v1alpha1.json
@@ -857,6 +857,11 @@
                             "type": "string"
                           }
                         },
+                        "required": [
+                          "id",
+                          "phase",
+                          "startTime"
+                        ],
                         "type": "object"
                       },
                       "type": "array"
@@ -1149,6 +1154,11 @@
                       "type": "string"
                     }
                   },
+                  "required": [
+                    "id",
+                    "phase",
+                    "startTime"
+                  ],
                   "type": "object"
                 },
                 "type": "array"
@@ -1659,6 +1669,11 @@
                             "type": "string"
                           }
                         },
+                        "required": [
+                          "id",
+                          "phase",
+                          "startTime"
+                        ],
                         "type": "object"
                       },
                       "type": "array"
diff --git a/ui/src/pages/user.tsx b/ui/src/pages/user.tsx
index 378dbccc..cf495e50 100644
--- a/ui/src/pages/user.tsx
+++ b/ui/src/pages/user.tsx
@@ -1,13 +1,13 @@
-import { faLink, faUserShield, IconDefinition } from '@fortawesome/free-solid-svg-icons';
+import { faQuestionCircle, IconDefinition } from '@fortawesome/free-solid-svg-icons';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
-import { Descriptions, Tag, Typography } from 'antd';
+import { Descriptions, Popover, Tag } from 'antd';
 import { DescriptionsItemType } from 'antd/es/descriptions';
 import { Navigate } from 'react-router-dom';
 
 import { redirectToQueryParam } from '@ui/config/auth';
 import { paths } from '@ui/config/paths';
 import { useAuthContext } from '@ui/features/auth/context/use-auth-context';
-import { isAdmin, isJWTDirty } from '@ui/features/auth/jwt-utils';
+import { claimsMapping, isJWTDirty } from '@ui/features/auth/jwt-utils';
 import { PageTitle } from '@ui/features/common';
 
 export const User = () => {
@@ -21,47 +21,16 @@ export const User = () => {
 
   const items: DescriptionsItemType[] = [];
 
-  if (isAdmin(JWTInfo)) {
+  for (const [key, value] of Object.entries(JWTInfo || {})) {
     items.push({
-      children: <User.Label icon={faUserShield} label='Admin' />,
-      labelStyle: {
-        display: 'none'
-      }
-    });
-  }
-
-  items.push({
-    label: <User.Label icon={faLink} label='Issuer' />,
-    children: JWTInfo?.iss
-  });
-
-  if (JWTInfo?.email) {
-    items.push({
-      label: <User.Label label='Email' />,
-      children: JWTInfo.email
-    });
-  }
-
-  if (JWTInfo?.preferred_username) {
-    items.push({
-      label: <User.Label label='Username' />,
-      children: JWTInfo.preferred_username
-    });
-  }
-
-  if (JWTInfo && Object.hasOwn(JWTInfo, 'groups')) {
-    items.push({
-      label: <User.Label label='Groups' />,
-      children:
-        (JWTInfo.groups?.length || 0) > 0 ? (
-          JWTInfo.groups?.map((group) => (
-            <Tag key={group} className='m-2'>
-              {group}
+      label: <User.Label label={key} />,
+      children: Array.isArray(value)
+        ? value.map((v) => (
+            <Tag key={v} className='m-2'>
+              {v}
             </Tag>
           ))
-        ) : (
-          <Typography.Text type='secondary'>No Groups</Typography.Text>
-        )
+        : `${value}`
     });
   }
 
@@ -69,13 +38,37 @@ export const User = () => {
     <div className='p-6'>
       <PageTitle title='User' />
 
-      <Descriptions layout='horizontal' column={1} className='w-5/12' bordered items={items} />
+      <Descriptions
+        title={
+          <>
+            Claims{' '}
+            <Popover content='Decoded from identity token issued by the identity provider'>
+              <FontAwesomeIcon icon={faQuestionCircle} className='text-xs' />
+            </Popover>
+          </>
+        }
+        layout='horizontal'
+        column={2}
+        bordered
+        items={items}
+      />
     </div>
   );
 };
 
-User.Label = (props: { icon?: IconDefinition; label: string }) => (
-  <>
-    {props.icon && <FontAwesomeIcon icon={props.icon} className='mr-2' />} {props.label}
-  </>
-);
+User.Label = (props: { icon?: IconDefinition; label: string }) => {
+  const claimHelpers = claimsMapping[props.label];
+  return (
+    <>
+      {props.icon && <FontAwesomeIcon icon={props.icon} className='mr-2' />} {props.label}{' '}
+      {!!claimHelpers && (
+        <>
+          ({claimHelpers.label}){' '}
+          <Popover content={claimHelpers.description}>
+            <FontAwesomeIcon icon={faQuestionCircle} className='text-xs' />
+          </Popover>
+        </>
+      )}
+    </>
+  );
+};



Test patch:
diff --git a/api/v1alpha1/freight_types_test.go b/api/v1alpha1/freight_types_test.go
index 7ae65efe..dff1084e 100644
--- a/api/v1alpha1/freight_types_test.go
+++ b/api/v1alpha1/freight_types_test.go
@@ -1,10 +1,13 @@
 package v1alpha1
 
 import (
+	"encoding/json"
 	"testing"
 	"time"
 
+	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
+	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -448,3 +451,148 @@ func TestFreightStatus_AddApprovedStage(t *testing.T) {
 		require.Equal(t, now, record.ApprovedAt.Time)
 	})
 }
+
+func TestFreightStatus_UpsertAndGetMetadata_Integration(t *testing.T) {
+	tests := []struct {
+		name string
+		data any
+	}{
+		{
+			name: "string value",
+			data: "test-string",
+		},
+		{
+			name: "integer value",
+			data: 42,
+		},
+		{
+			name: "boolean value",
+			data: true,
+		},
+		{
+			name: "complex struct",
+			data: struct {
+				Name    string            `json:"name"`
+				Age     int               `json:"age"`
+				Active  bool              `json:"active"`
+				Tags    []string          `json:"tags"`
+				Configs map[string]string `json:"configs"`
+			}{
+				Name:   "test-user",
+				Age:    30,
+				Active: true,
+				Tags:   []string{"admin", "developer"},
+				Configs: map[string]string{
+					"theme": "dark",
+					"lang":  "en",
+				},
+			},
+		},
+		{
+			name: "slice of integers",
+			data: []int{1, 2, 3, 4, 5},
+		},
+		{
+			name: "nested map",
+			data: map[string]any{
+				"level1": map[string]any{
+					"level2": map[string]string{
+						"key": "value",
+					},
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			status := FreightStatus{}
+			key := "integration-test-key"
+
+			// Test Upsert
+			err1 := status.UpsertMetadata(key, tt.data)
+			require.NoError(t, err1)
+
+			// Verify metadata was stored
+			assert.NotNil(t, status.Metadata)
+			assert.Contains(t, status.Metadata, key)
+
+			// Test Get with correct type
+			switch expected := tt.data.(type) {
+			case string:
+				var result string
+				found, err := status.GetMetadata(key, &result)
+				assert.True(t, found)
+				assert.NoError(t, err)
+				assert.Equal(t, expected, result)
+
+			case int:
+				var result int
+				found, err := status.GetMetadata(key, &result)
+				assert.True(t, found)
+				assert.NoError(t, err)
+				assert.Equal(t, expected, result)
+
+			case bool:
+				var result bool
+				found, err := status.GetMetadata(key, &result)
+				assert.True(t, found)
+				assert.NoError(t, err)
+				assert.Equal(t, expected, result)
+
+			case []int:
+				var result []int
+				found, err := status.GetMetadata(key, &result)
+				assert.True(t, found)
+				assert.NoError(t, err)
+				assert.Equal(t, expected, result)
+
+			default:
+				// For complex types, use any and compare JSON representation
+				var result any
+				found, err := status.GetMetadata(key, &result)
+				assert.True(t, found)
+				assert.NoError(t, err)
+
+				// Compare by marshaling both to JSON
+				expectedJSON, err := json.Marshal(expected)
+				require.NoError(t, err)
+				resultJSON, err := json.Marshal(result)
+				require.NoError(t, err)
+				assert.JSONEq(t, string(expectedJSON), string(resultJSON))
+			}
+
+			// Test updating the same key
+			newData := "updated-value"
+			err := status.UpsertMetadata(key, newData)
+			require.NoError(t, err)
+
+			var updatedResult string
+			found, err := status.GetMetadata(key, &updatedResult)
+			assert.True(t, found)
+			assert.NoError(t, err)
+			assert.Equal(t, newData, updatedResult)
+		})
+	}
+}
+
+func TestFreightStatus_MetadataEdgeCases(t *testing.T) {
+	t.Run("empty key", func(t *testing.T) {
+		status := FreightStatus{}
+		err := status.UpsertMetadata("", "value")
+		assert.Error(t, err)
+	})
+
+	t.Run("nil target for Get", func(t *testing.T) {
+		status := FreightStatus{
+			Metadata: map[string]apiextensionsv1.JSON{
+				"test-key": {Raw: []byte(`"test-value"`)},
+			},
+		}
+
+		// This should cause a panic or error during unmarshal
+		found, err := status.GetMetadata("test-key", nil)
+		assert.False(t, found)
+		assert.Error(t, err)
+	})
+}
diff --git a/internal/controller/management/projectconfigs/project_configs_test.go b/internal/controller/management/projectconfigs/project_configs_test.go
index b6394ac7..18228db2 100644
--- a/internal/controller/management/projectconfigs/project_configs_test.go
+++ b/internal/controller/management/projectconfigs/project_configs_test.go
@@ -254,6 +254,7 @@ func TestReconciler_syncWebhookReceivers(t *testing.T) {
 				Spec: kargoapi.ProjectConfigSpec{
 					WebhookReceivers: []kargoapi.WebhookReceiverConfig{
 						{
+							Name: "fake-webhook-receiver-name",
 							GitHub: &kargoapi.GitHubWebhookReceiver{
 								SecretRef: corev1.LocalObjectReference{
 									Name: "secret-that-exists",
@@ -269,6 +270,7 @@ func TestReconciler_syncWebhookReceivers(t *testing.T) {
 				require.NotNil(t, pc.Spec.WebhookReceivers[0].GitHub)
 				require.Equal(t,
 					external.GenerateWebhookPath(
+						"fake-webhook-receiver-name",
 						pc.Name,
 						kargoapi.WebhookReceiverTypeGitHub,
 						"fake-secret-data",
diff --git a/internal/promotion/promotion_test.go b/internal/promotion/promotion_test.go
index e7ab90e5..b4533d1f 100644
--- a/internal/promotion/promotion_test.go
+++ b/internal/promotion/promotion_test.go
@@ -8,7 +8,6 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	k8sruntime "k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/utils/ptr"
 	"sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -88,7 +87,7 @@ func TestStep_GetConfig(t *testing.T) {
 
 	testClient := fake.NewClientBuilder().WithScheme(testScheme).WithObjects(
 		&kargoapi.Warehouse{
-			ObjectMeta: v1.ObjectMeta{
+			ObjectMeta: metav1.ObjectMeta{
 				Name:      "fake-warehouse",
 				Namespace: "fake-project",
 			},
@@ -135,18 +134,32 @@ func TestStep_GetConfig(t *testing.T) {
 				Stage:     "fake-stage",
 				Promotion: "fake-promotion",
 				Actor:     "fake-creator",
+				TargetFreightRef: kargoapi.FreightReference{
+					Name: "fake-freight",
+					Origin: kargoapi.FreightOrigin{
+						Name: "fake-warehouse",
+					},
+				},
 			},
 			rawCfg: []byte(`{
 				"project": "${{ ctx.project }}",
 				"stage": "${{ ctx.stage }}",
 				"promotion": "${{ ctx.promotion }}",
-				"actor": "${{ ctx.meta.promotion.actor }}"
+				"actor": "${{ ctx.meta.promotion.actor }}",
+				"targetFreight": {
+					"name": "${{ ctx.targetFreight.name }}",
+					"origin": "${{ ctx.targetFreight.origin.name }}"
+				}
 			}`),
 			expectedCfg: promotion.Config{
 				"project":   "fake-project",
 				"stage":     "fake-stage",
 				"promotion": "fake-promotion",
 				"actor":     "fake-creator",
+				"targetFreight": map[string]any{
+					"name":   "fake-freight",
+					"origin": "fake-warehouse",
+				},
 			},
 		},
 		{
@@ -261,6 +274,12 @@ func TestStep_GetConfig(t *testing.T) {
 			// Test that the warehouse() function can be used to reference freight
 			// origins
 			promoCtx: Context{
+				TargetFreightRef: kargoapi.FreightReference{
+					Name: "fake-freight",
+					Origin: kargoapi.FreightOrigin{
+						Name: "fake-origin-warehouse",
+					},
+				},
 				Vars: []kargoapi.ExpressionVariable{{
 					Name:  "warehouseName",
 					Value: "fake-warehouse",
@@ -268,7 +287,8 @@ func TestStep_GetConfig(t *testing.T) {
 			},
 			rawCfg: []byte(`{
 				"origin1": "${{ warehouse('fake-warehouse') }}",
-				"origin2": "${{ warehouse(vars.warehouseName) }}"
+				"origin2": "${{ warehouse(vars.warehouseName) }}",
+				"origin3": "${{ warehouse(ctx.targetFreight.origin.name) }}"
 			}`),
 			expectedCfg: promotion.Config{
 				"origin1": map[string]any{
@@ -279,6 +299,10 @@ func TestStep_GetConfig(t *testing.T) {
 					"kind": "Warehouse",
 					"name": "fake-warehouse",
 				},
+				"origin3": map[string]any{
+					"kind": "Warehouse",
+					"name": "fake-origin-warehouse",
+				},
 			},
 		},
 		{
@@ -497,7 +521,7 @@ func TestStep_GetVars(t *testing.T) {
 
 	testClient := fake.NewClientBuilder().WithScheme(testScheme).WithObjects(
 		&kargoapi.Warehouse{
-			ObjectMeta: v1.ObjectMeta{
+			ObjectMeta: metav1.ObjectMeta{
 				Name:      "fake-warehouse",
 				Namespace: "fake-project",
 			},
@@ -778,6 +802,12 @@ func TestStep_GetVars(t *testing.T) {
 				Stage:     "fake-stage",
 				Promotion: "fake-promotion",
 				Actor:     "fake-creator",
+				TargetFreightRef: kargoapi.FreightReference{
+					Name: "fake-freight",
+					Origin: kargoapi.FreightOrigin{
+						Name: "fake-warehouse",
+					},
+				},
 			},
 			step: Step{
 				Vars: []kargoapi.ExpressionVariable{
@@ -797,13 +827,18 @@ func TestStep_GetVars(t *testing.T) {
 						Name:  "actor",
 						Value: "${{ ctx.meta.promotion.actor }}",
 					},
+					{
+						Name:  "targetFreight",
+						Value: "${{ ctx.targetFreight.origin.name }}",
+					},
 				},
 			},
 			expectedVars: map[string]any{
-				"proj":  "fake-project",
-				"stage": "fake-stage",
-				"promo": "fake-promotion",
-				"actor": "fake-creator",
+				"proj":          "fake-project",
+				"stage":         "fake-stage",
+				"promo":         "fake-promotion",
+				"actor":         "fake-creator",
+				"targetFreight": "fake-warehouse",
 			},
 		},
 		{
diff --git a/internal/promotion/runner/builtin/git_cloner_test.go b/internal/promotion/runner/builtin/git_cloner_test.go
index 7f6bdc0f..8dc75807 100644
--- a/internal/promotion/runner/builtin/git_cloner_test.go
+++ b/internal/promotion/runner/builtin/git_cloner_test.go
@@ -18,7 +18,7 @@ import (
 	"github.com/akuity/kargo/pkg/x/promotion/runner/builtin"
 )
 
-func Test_gitCloner_validate(t *testing.T) {
+func Test_gitCloner_validateAndUnmarshal(t *testing.T) {
 	testCases := []struct {
 		name             string
 		config           promotion.Config
@@ -115,6 +115,25 @@ func Test_gitCloner_validate(t *testing.T) {
 				"checkout.0: Must validate one and only one schema",
 			},
 		},
+		{
+			name: "duplicate aliases",
+			config: promotion.Config{
+				"repoURL": "https://github.com/example/repo.git",
+				"checkout": []promotion.Config{
+					{
+						"as":   "alias1",
+						"path": "/fake/path/0",
+					},
+					{
+						"as":   "alias1",
+						"path": "/fake/path/1",
+					},
+				},
+			},
+			expectedProblems: []string{
+				`duplicate checkout alias "alias1" at checkout[1]`,
+			},
+		},
 		{
 			name: "author name is missing",
 			config: promotion.Config{
@@ -130,7 +149,7 @@ func Test_gitCloner_validate(t *testing.T) {
 				},
 			},
 			expectedProblems: []string{
-				"invalid git-clone config: author: name is required",
+				"author: name is required",
 			},
 		},
 		{
@@ -148,7 +167,7 @@ func Test_gitCloner_validate(t *testing.T) {
 				},
 			},
 			expectedProblems: []string{
-				"invalid git-clone config: author.name: String length must be greater than or equal to 1",
+				"author.name: String length must be greater than or equal to 1",
 			},
 		},
 		{
@@ -166,7 +185,7 @@ func Test_gitCloner_validate(t *testing.T) {
 				},
 			},
 			expectedProblems: []string{
-				"invalid git-clone config: author: email is required",
+				"author: email is required",
 			},
 		},
 		{
@@ -184,7 +203,7 @@ func Test_gitCloner_validate(t *testing.T) {
 				},
 			},
 			expectedProblems: []string{
-				"invalid git-clone config: author.email: Does not match format 'email'",
+				"author.email: Does not match format 'email'",
 			},
 		},
 		{
@@ -267,12 +286,14 @@ func Test_gitCloner_validate(t *testing.T) {
 					},
 					{
 						"path": "/fake/path/8",
+						"as":   "alias1",
 					},
 					{
 						"branch": "",
 						"commit": "",
 						"tag":    "",
 						"path":   "/fake/path/9",
+						"as":     "alias2",
 					},
 					{
 						"path": "/fake/path/10",
@@ -288,7 +309,7 @@ func Test_gitCloner_validate(t *testing.T) {
 
 	for _, testCase := range testCases {
 		t.Run(testCase.name, func(t *testing.T) {
-			err := runner.validate(testCase.config)
+			_, err := runner.validateAndUnmarshal(testCase.config)
 			if len(testCase.expectedProblems) == 0 {
 				require.NoError(t, err)
 			} else {
@@ -326,7 +347,7 @@ func Test_gitCloner_run(t *testing.T) {
 	err = repo.Push(nil)
 	require.NoError(t, err)
 
-	commitID, err := repo.LastCommitID()
+	srcBranchCommitID, err := repo.LastCommitID()
 	require.NoError(t, err)
 
 	// Now we can proceed to test gitCloner...
@@ -346,7 +367,7 @@ func Test_gitCloner_run(t *testing.T) {
 			RepoURL: fmt.Sprintf("%s/test.git", server.URL),
 			Checkout: []builtin.Checkout{
 				{
-					Commit: commitID,
+					Commit: srcBranchCommitID,
 					Path:   "src",
 				},
 				{
@@ -370,4 +391,18 @@ func Test_gitCloner_run(t *testing.T) {
 	require.NoError(t, err)
 	require.Len(t, dirEntries, 1) // Just the .git file
 	require.FileExists(t, filepath.Join(stepCtx.WorkDir, "out", ".git"))
+
+	// Assert output map contains the expected commit hashes for each checkout
+	outTree, err := git.LoadWorkTree(filepath.Join(stepCtx.WorkDir, "out"), nil)
+	require.NoError(t, err)
+	outBranchCommitID, err := outTree.LastCommitID()
+	require.NoError(t, err)
+	require.Equal(
+		t,
+		map[string]any{
+			"src": srcBranchCommitID,
+			"out": outBranchCommitID,
+		},
+		res.Output["commits"],
+	)
 }
diff --git a/internal/promotion/runner/builtin/git_commiter_test.go b/internal/promotion/runner/builtin/git_commiter_test.go
index a8834958..0eabb87d 100644
--- a/internal/promotion/runner/builtin/git_commiter_test.go
+++ b/internal/promotion/runner/builtin/git_commiter_test.go
@@ -69,7 +69,7 @@ func Test_gitCommitter_validate(t *testing.T) {
 		{
 			name: "author email is not specified",
 			config: promotion.Config{ // If author is specified, email must be specified
-				"author":  promotion.Config{
+				"author": promotion.Config{
 					"name": "Tony Stark",
 				},
 				"path":    "/tmp/foo",
@@ -96,7 +96,7 @@ func Test_gitCommitter_validate(t *testing.T) {
 		{
 			name: "author name is not specified",
 			config: promotion.Config{ // If author is specified, name must be specified
-				"author":  promotion.Config{
+				"author": promotion.Config{
 					"email": "example@example.com",
 				},
 				"path":    "/tmp/foo",
@@ -110,7 +110,7 @@ func Test_gitCommitter_validate(t *testing.T) {
 			name: "author name is empty string",
 			config: promotion.Config{
 				"author": promotion.Config{
-					"name": "",
+					"name":  "",
 					"email": "example@example.com",
 				},
 				"path":    "/tmp/foo",
@@ -150,8 +150,8 @@ func Test_gitCommitter_validate(t *testing.T) {
 			name: "valid kitchen sink",
 			config: promotion.Config{
 				"author": promotion.Config{
-					"email": "tony@starkindustries.com",
-					"name":  "Tony Stark",
+					"email":      "tony@starkindustries.com",
+					"name":       "Tony Stark",
 					"signingKey": "valid-signing-key",
 				},
 				"path":    "/tmp/foo",
@@ -254,4 +254,23 @@ func Test_gitCommitter_run(t *testing.T) {
 	lastCommitMsg, err := workTree.CommitMessage("HEAD")
 	require.NoError(t, err)
 	require.Equal(t, "Initial commit\n", lastCommitMsg)
+
+	// Run the step again to confirm a Skipped status is returned when no new
+	// commit is actually made.
+	res, err = runner.run(
+		context.Background(),
+		stepCtx,
+		builtin.GitCommitConfig{
+			Path:    "master",
+			Message: "No-Op commit",
+		},
+	)
+	require.NoError(t, err)
+	require.Equal(t, kargoapi.PromotionStepStatusSkipped, res.Status)
+
+	// Despite the Skipped status, step output should still contain the commit ID
+	// from the head of the branch.
+	actualCommit, ok = res.Output[stateKeyCommit]
+	require.True(t, ok)
+	require.Equal(t, expectedCommit, actualCommit)
 }
diff --git a/internal/promotion/runner/builtin/http_requester_test.go b/internal/promotion/runner/builtin/http_requester_test.go
index 4f8e0877..b89f3456 100644
--- a/internal/promotion/runner/builtin/http_requester_test.go
+++ b/internal/promotion/runner/builtin/http_requester_test.go
@@ -326,6 +326,39 @@ func Test_httpRequester_run(t *testing.T) {
 				)
 			},
 		},
+		{
+			name: "success and not failed with json body and response is array",
+			handler: func(w http.ResponseWriter, _ *http.Request) {
+				w.Header().Set(contentTypeHeader, contentTypeJSON)
+				_, err := w.Write([]byte(`[{"theMeaningOfLife": 42}]`))
+				require.NoError(t, err)
+			},
+			cfg: builtin.HTTPConfig{
+				SuccessExpression: "true",
+				Outputs: []builtin.HTTPOutput{
+					{
+						Name:           "status",
+						FromExpression: "response.status",
+					},
+					{
+						Name:           "theMeaningOfLife",
+						FromExpression: "response.body[0].theMeaningOfLife",
+					},
+				},
+			},
+			assertions: func(t *testing.T, res promotion.StepResult, err error) {
+				require.NoError(t, err)
+				require.Equal(t, kargoapi.PromotionStepStatusSucceeded, res.Status)
+				require.Equal(
+					t,
+					map[string]any{
+						"status":           int64(http.StatusOK),
+						"theMeaningOfLife": float64(42),
+					},
+					res.Output,
+				)
+			},
+		},
 		{
 			name: "failed and not success",
 			handler: func(w http.ResponseWriter, _ *http.Request) {
@@ -456,6 +489,20 @@ func Test_httpRequester_buildExprEnv(t *testing.T) {
 		resp       *http.Response
 		assertions func(*testing.T, map[string]any, error)
 	}{
+		{
+			name: "response body Content-Length exceeds limit",
+			resp: &http.Response{
+				StatusCode:    200,
+				ContentLength: (2 << 20) + 1,
+				Header:        http.Header{"Content-Type": []string{"application/json"}},
+				Body:          io.NopCloser(strings.NewReader(`{"foo": "bar"}`)),
+			},
+			assertions: func(t *testing.T, env map[string]any, err error) {
+				require.Error(t, err)
+				require.ErrorContains(t, err, "response body size")
+				require.Nil(t, env)
+			},
+		},
 		{
 			name: "without body",
 			resp: &http.Response{
@@ -503,6 +550,52 @@ func Test_httpRequester_buildExprEnv(t *testing.T) {
 				require.Equal(t, map[string]any{"foo": "bar"}, body)
 			},
 		},
+		{
+			name: "with body as an array",
+			resp: &http.Response{
+				StatusCode: 200,
+				Header:     http.Header{"Content-Type": []string{"application/json"}},
+				Body:       io.NopCloser(strings.NewReader(`[{"foo1": "bar1"}, {"foo2": "bar2"}]`)),
+			},
+			assertions: func(t *testing.T, env map[string]any, err error) {
+				require.NoError(t, err)
+				bodyAny, ok := env["response"].(map[string]any)["body"]
+				require.True(t, ok)
+
+				// Check if interface is of type []any
+				body, ok := bodyAny.([]any)
+				require.True(t, ok)
+				require.Len(t, body, 2)
+
+				firstItem, ok := body[0].(map[string]any)
+				require.True(t, ok)
+				require.Equal(t, map[string]any{"foo1": "bar1"}, firstItem)
+			},
+		},
+		{
+			name: "invalid JSON body",
+			resp: &http.Response{
+				StatusCode: 200,
+				Header:     http.Header{"Content-Type": []string{"application/json"}},
+				Body:       io.NopCloser(strings.NewReader(`{"foo":`)),
+			},
+			assertions: func(t *testing.T, env map[string]any, err error) {
+				require.Error(t, err)
+				require.ErrorContains(t, err, "failed to parse response")
+			},
+		},
+		{
+			name: "valid JSON but unexpected type string",
+			resp: &http.Response{
+				StatusCode: 200,
+				Header:     http.Header{"Content-Type": []string{"application/json"}},
+				Body:       io.NopCloser(strings.NewReader(`"foo"`)),
+			},
+			assertions: func(t *testing.T, env map[string]any, err error) {
+				require.Error(t, err)
+				require.ErrorContains(t, err, "unexpected type when unmarshaling")
+			},
+		},
 	}
 	h := &httpRequester{}
 	for _, testCase := range testCases {
diff --git a/internal/promotion/simple_engine_test.go b/internal/promotion/simple_engine_test.go
index d48f4996..7161fb73 100644
--- a/internal/promotion/simple_engine_test.go
+++ b/internal/promotion/simple_engine_test.go
@@ -211,6 +211,36 @@ func TestSimpleEngine_executeSteps(t *testing.T) {
 				}, result.State)
 			},
 		},
+		{
+			name: "execute the skipped step",
+			steps: []Step{
+				{Kind: "skipped-step", Alias: "step1"},
+				{Kind: "skipped-step", Alias: "step2"},
+			},
+			assertions: func(t *testing.T, result Result) {
+				assert.Equal(t, kargoapi.PromotionPhaseSucceeded, result.Status)
+				assert.Empty(t, result.Message)
+				assert.Equal(t, int64(1), result.CurrentStep)
+
+				// Verify the result contains metadata from both steps
+				assert.Len(t, result.StepExecutionMetadata, 2)
+				for _, metadata := range result.StepExecutionMetadata {
+					assert.Equal(t, kargoapi.PromotionStepStatusSkipped, metadata.Status)
+					assert.NotNil(t, metadata.StartedAt)
+					assert.NotNil(t, metadata.FinishedAt)
+				}
+
+				// Verify state contains output from both steps
+				assert.Equal(t, promotion.State{
+					"step1": map[string]any{
+						"key": "value",
+					},
+					"step2": map[string]any{
+						"key": "value",
+					},
+				}, result.State)
+			},
+		},
 		{
 			name: "conditional step execution",
 			steps: []Step{
@@ -601,6 +631,13 @@ func TestSimpleEngine_executeSteps(t *testing.T) {
 						Output: map[string]any{"key": "value"},
 					},
 				},
+				&promotion.MockStepRunner{
+					StepName: "skipped-step",
+					RunResult: promotion.StepResult{
+						Status: kargoapi.PromotionStepStatusSkipped,
+						Output: map[string]any{"key": "value"},
+					},
+				},
 				&promotion.MockStepRunner{
 					StepName: "running-step",
 					RunResult: promotion.StepResult{
diff --git a/internal/webhook/external/github_test.go b/internal/webhook/external/github_test.go
index 1ca3016b..1d4819ee 100644
--- a/internal/webhook/external/github_test.go
+++ b/internal/webhook/external/github_test.go
@@ -64,6 +64,7 @@ func TestGithubHandler(t *testing.T) {
 								WebhookReceivers: []kargoapi.WebhookReceiver{
 									{
 										Path: GenerateWebhookPath(
+											"fake-webhook-receiver-name",
 											"fakename",
 											kargoapi.WebhookReceiverTypeGitHub,
 											"fakesecret",
@@ -135,6 +136,7 @@ func TestGithubHandler(t *testing.T) {
 								WebhookReceivers: []kargoapi.WebhookReceiver{
 									{
 										Path: GenerateWebhookPath(
+											"fake-webhook-receiver-name",
 											"fakename",
 											kargoapi.WebhookReceiverTypeGitHub,
 											"fakesecret",
@@ -206,6 +208,7 @@ func TestGithubHandler(t *testing.T) {
 								WebhookReceivers: []kargoapi.WebhookReceiver{
 									{
 										Path: GenerateWebhookPath(
+											"fake-webhook-receiver-name",
 											"fakename",
 											kargoapi.WebhookReceiverTypeGitHub,
 											"mysupersecrettoken",
@@ -232,11 +235,11 @@ func TestGithubHandler(t *testing.T) {
 				req := httptest.NewRequest(http.MethodPost, url, b)
 				req.Header.Set("Content-Type", "application/json")
 				req.Header.Set("X-Hub-Signature-256", sign(t, testSecret, b.Bytes()))
-				req.Header.Set("X-GitHub-Event", "ping")
+				req.Header.Set("X-GitHub-Event", "deployment")
 				return req
 			},
 			secret: "fakesecret",
-			msg:    "{\"error\":\"only push events are supported\"}\n",
+			msg:    "{\"error\":\"event type deployment is not supported\"}\n",
 			code:   http.StatusNotImplemented,
 		},
 		{
@@ -277,6 +280,7 @@ func TestGithubHandler(t *testing.T) {
 								WebhookReceivers: []kargoapi.WebhookReceiver{
 									{
 										Path: GenerateWebhookPath(
+											"fake-webhook-receiver-name",
 											"fakename",
 											kargoapi.WebhookReceiverTypeGitHub,
 											"mysupersecrettoken",
@@ -349,6 +353,7 @@ func TestGithubHandler(t *testing.T) {
 								WebhookReceivers: []kargoapi.WebhookReceiver{
 									{
 										Path: GenerateWebhookPath(
+											"fake-webhook-receiver-name",
 											"fakename",
 											kargoapi.WebhookReceiverTypeGitHub,
 											"mysupersecrettoken",
@@ -418,6 +423,7 @@ func TestGithubHandler(t *testing.T) {
 								WebhookReceivers: []kargoapi.WebhookReceiver{
 									{
 										Path: GenerateWebhookPath(
+											"fake-webhook-receiver-name",
 											"fakename",
 											kargoapi.WebhookReceiverTypeGitHub,
 											"mysupersecrettoken",
@@ -488,6 +494,7 @@ func TestGithubHandler(t *testing.T) {
 								WebhookReceivers: []kargoapi.WebhookReceiver{
 									{
 										Path: GenerateWebhookPath(
+											"fake-webhook-receiver-name",
 											"fakename",
 											kargoapi.WebhookReceiverTypeGitHub,
 											"mysupersecrettoken",
@@ -522,7 +529,7 @@ func TestGithubHandler(t *testing.T) {
 			code:   http.StatusBadRequest,
 		},
 		{
-			name: "OK",
+			name: "success - push event",
 			kClient: func() client.Client {
 				scheme := runtime.NewScheme()
 				require.NoError(t, corev1.AddToScheme(scheme))
@@ -559,6 +566,7 @@ func TestGithubHandler(t *testing.T) {
 								WebhookReceivers: []kargoapi.WebhookReceiver{
 									{
 										Path: GenerateWebhookPath(
+											"fake-webhook-receiver-name",
 											"fakename",
 											kargoapi.WebhookReceiverTypeGitHub,
 											"mysupersecrettoken",
@@ -607,6 +615,39 @@ func TestGithubHandler(t *testing.T) {
 			msg:    "{\"msg\":\"refreshed 1 warehouse(s)\"}\n",
 			code:   http.StatusOK,
 		},
+		{
+			name: "success - ping event",
+			kClient: func() client.Client {
+				scheme := runtime.NewScheme()
+				require.NoError(t, corev1.AddToScheme(scheme))
+				require.NoError(t, kargoapi.AddToScheme(scheme))
+				return fake.NewClientBuilder().
+					WithScheme(scheme).
+					WithObjects(
+						&corev1.Secret{
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "fakesecret",
+								Namespace: "fakenamespace",
+							},
+							Data: map[string][]byte{
+								"token": []byte("mysupersecrettoken"),
+							},
+						},
+					).
+					Build()
+			},
+			req: func() *http.Request {
+				b := newBody()
+				req := httptest.NewRequest(http.MethodPost, url, b)
+				req.Header.Set("Content-Type", "application/json")
+				req.Header.Set("X-Hub-Signature-256", sign(t, "mysupersecrettoken", b.Bytes()))
+				req.Header.Set("X-GitHub-Event", "ping")
+				return req
+			},
+			secret: "fakesecret",
+			msg:    "{\"msg\":\"ping event received, webhook is configured correctly for https://github.com/username/repo\"}\n", // nolint: lll
+			code:   http.StatusOK,
+		},
 	} {
 		t.Run(test.name, func(t *testing.T) {
 			req := test.req()
@@ -627,7 +668,7 @@ func sign(t *testing.T, s string, b []byte) string {
 	t.Helper()
 
 	mac := hmac.New(sha256.New, []byte(s))
-	mac.Write(b)
+	_, _ = mac.Write(b)
 	return fmt.Sprintf("sha256=%s",
 		hex.EncodeToString(mac.Sum(nil)),
 	)
diff --git a/internal/webhook/external/server_test.go b/internal/webhook/external/server_test.go
index 129a0dfd..fc3d5d8e 100644
--- a/internal/webhook/external/server_test.go
+++ b/internal/webhook/external/server_test.go
@@ -129,6 +129,7 @@ func TestRouteHandler(t *testing.T) {
 								WebhookReceivers: []kargoapi.WebhookReceiver{
 									{
 										Path: GenerateWebhookPath(
+											"fake-webhook-receiver-name",
 											"fakename",
 											kargoapi.WebhookReceiverTypeGitHub,
 											"mysupersecrettoken",
@@ -169,6 +170,7 @@ func TestRouteHandler(t *testing.T) {
 				return s
 			},
 			path: GenerateWebhookPath(
+				"fake-webhook-receiver-name",
 				"fakename",
 				kargoapi.WebhookReceiverTypeGitHub,
 				"mysupersecrettoken",
diff --git a/internal/webhook/kubernetes/projectconfig/webhook_test.go b/internal/webhook/kubernetes/projectconfig/webhook_test.go
index 5f69ea51..f566e931 100644
--- a/internal/webhook/kubernetes/projectconfig/webhook_test.go
+++ b/internal/webhook/kubernetes/projectconfig/webhook_test.go
@@ -68,6 +68,10 @@ func Test_webhook_ValidateCreate(t *testing.T) {
 						{Stage: "stage-1"},
 						{Stage: "stage-2"},
 					},
+					WebhookReceivers: []kargoapi.WebhookReceiverConfig{
+						{Name: "receiver-1"},
+						{Name: "receiver-2"},
+					},
 				},
 			},
 			objects: []client.Object{testNs},
@@ -502,6 +506,46 @@ func Test_webhook_ValidateCreate(t *testing.T) {
 					`invalid pattern identifier "badpattern"`)
 			},
 		},
+		{
+			name: "duplicate webhook receiver names",
+			projectConfig: &kargoapi.ProjectConfig{
+				ObjectMeta: metav1.ObjectMeta{
+					Name:      testProjectName,
+					Namespace: testProjectName,
+				},
+				Spec: kargoapi.ProjectConfigSpec{
+					PromotionPolicies: []kargoapi.PromotionPolicy{
+						{Stage: "stage-1"},
+						{Stage: "stage-2"},
+					},
+					WebhookReceivers: []kargoapi.WebhookReceiverConfig{
+						{Name: "my-webhook-receiver"},
+						{Name: "my-webhook-receiver"},
+					},
+				},
+			},
+			objects: []client.Object{testNs},
+			assertions: func(t *testing.T, warnings admission.Warnings, err error) {
+				assert.Empty(t, warnings)
+				require.Error(t, err)
+
+				var statusErr *apierrors.StatusError
+				require.True(t, errors.As(err, &statusErr))
+
+				assert.Equal(t, metav1.StatusReasonInvalid, statusErr.ErrStatus.Reason)
+				assert.Equal(t, 1, len(statusErr.ErrStatus.Details.Causes))
+
+				// Sort errors for consistent testing
+				sort.Slice(statusErr.ErrStatus.Details.Causes, func(i, j int) bool {
+					return statusErr.ErrStatus.Details.Causes[i].Field < statusErr.ErrStatus.Details.Causes[j].Field
+				})
+
+				assert.Equal(t, "spec.webhookReceivers[1].name", statusErr.ErrStatus.Details.Causes[0].Field)
+				assert.Equal(t, metav1.CauseTypeFieldValueInvalid, statusErr.ErrStatus.Details.Causes[0].Type)
+				assert.Contains(t, statusErr.ErrStatus.Details.Causes[0].Message,
+					"webhook receiver name already defined at spec.webhookReceivers[0]")
+			},
+		},
 	}
 
 	for _, tt := range tests {