Repository: google/go-containerregistry
Pull request #1528 (2023-01-10 19:22:16)

crane: support --omit-digest-tags in crane ls
Before:

```
$ crane ls cgr.dev/chainguard/static | head -n 10
20220302
sha256-bab7d22b93180e8a05d0f02c739605cc7ed52b414b8f70ad6a173f2c0c8dc946.sig
sha256-e86edc0a7f00adb469d13b49192f38f992a981d778aa3f24525aa83b370b4001.sig
20220303
sha256-1535c58eb82f566a382131ba23583135aaba4e22fccccaadcd00b24286c47ad7.sig
sha256-061d30276694f8ae8e6a47eec9e7b7526d5e0c89436217907cac09cc19d9a953.sig
20220304
sha256-6168c94e3afa1b01a090632f77309ce541e1f502d8783071d026eb97a211b723.sig
sha256-8953bc492aebe6574d8735174000a7cea046de6ec8b6b28501ae27110352bc11.sig
20220305
$ crane ls cgr.dev/chainguard/static | wc -l
7890
```

After:

```
$ go run ./cmd/crane ls cgr.dev/chainguard/static --omit-digest-tags | head -n 10
20220302
20220303
20220304
20220305
20220306
20220307
20220308
20220309
20220310
20220311
$ go run ./cmd/crane ls cgr.dev/chainguard/static --omit-digest-tags | wc -l     
     369
```

(This image is built and tagged daily)


Patch:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7defff7d..037c6ad3 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,7 +17,7 @@ jobs:
         with:
           go-version: 1.18
           check-latest: true
-      - uses: goreleaser/goreleaser-action@v4.1.0
+      - uses: goreleaser/goreleaser-action@v4.2.0
         id: run-goreleaser
         with:
           version: latest
@@ -72,7 +72,7 @@ jobs:
         run: |
           set -euo pipefail
           gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "*.tar.gz"
-          gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "attestation.intoto.jsonl"
+          gh -R "$GITHUB_REPOSITORY" release download "$GITHUB_REF_NAME" -p "multiple.intoto.jsonl"
       - name: Verify assets
         env:
           CHECKSUMS: ${{ needs.goreleaser.outputs.hashes }}
diff --git a/.github/workflows/style.yaml b/.github/workflows/style.yaml
index 47fca0b5..768685a5 100644
--- a/.github/workflows/style.yaml
+++ b/.github/workflows/style.yaml
@@ -28,7 +28,7 @@ jobs:
           go-version: 1.18
           check-latest: true
 
-      - uses: golangci/golangci-lint-action@v3.3.1
+      - uses: golangci/golangci-lint-action@v3.4.0
         with:
           version: v1.45.2
 
diff --git a/.golangci.yaml b/.golangci.yaml
index ed4a6462..0771b706 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -32,3 +32,9 @@ linters:
 
   disable:
   - errcheck
+
+linters-settings:
+  depguard:
+    include-go-root: true
+    packages-with-error-message:
+    - crypto/sha256: "use crypto.SHA256 instead"
diff --git a/cmd/crane/README.md b/cmd/crane/README.md
index 26668011..a05dbeb3 100644
--- a/cmd/crane/README.md
+++ b/cmd/crane/README.md
@@ -10,27 +10,50 @@ A collection of useful things you can do with `crane` is [here](recipes.md).
 ## Installation
 
 ### Install from Releases
-Download [latest release](https://github.com/google/go-containerregistry/releases/latest):
 
-```
-$ VERSION=TODO   # Latest, or other
-$ OS=Linux       # or Darwin, Windows
-$ ARCH=x86_64    # or arm64, x86_64, armv6, i386, s390x
-$ curl -sL "https://github.com/google/go-containerregistry/releases/download/v${VERSION}/go-containerregistry_${VERSION}_${OS}_${ARCH}.tar.gz" > go-containerregistry.tar.gz
-```
+1. Get the [latest release](https://github.com/google/go-containerregistry/releases/latest) version.
 
-We generate [SLSA 3 provenance](https://slsa.dev) using the OpenSSF's [slsa-framework/slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator). To verify our release, install the verification tool from [slsa-framework/slsa-verifier#installation](https://github.com/slsa-framework/slsa-verifier#installation) and verify as follows:
+   ```sh
+   $ VERSION=$(curl -s "https://api.github.com/repos/google/go-containerregistry/releases/latest" | jq -r '.tag_name')
+   ```
 
-```
-$ curl -sL https://github.com/google/go-containerregistry/releases/download/v${VERSION}/attestation.intoto.jsonl > provenance.intoto.jsonl
-$ slsa-verifier -artifact-path go-containerregistry.tar.gz -provenance provenance.intoto.jsonl -source github.com/google/go-containerregistry -tag "v${VERSION}"
-  PASSED: Verified SLSA provenance
-```
+   or set a specific version:
+
+   ```sh
+   $ VERSION=vX.Y.Z   # Version number with a leading v
+   ```
+
+1. Download the release.
+
+   ```sh
+   $ OS=Linux       # or Darwin, Windows
+   $ ARCH=x86_64    # or arm64, x86_64, armv6, i386, s390x
+   $ curl -sL "https://github.com/google/go-containerregistry/releases/download/${VERSION}/go-containerregistry_${OS}_${ARCH}.tar.gz" > go-containerregistry.tar.gz
+   ```
+
+1. Verify the signature. We generate [SLSA 3 provenance](https://slsa.dev) using
+   the OpenSSF's [slsa-framework/slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator).
+   To verify our release, install the verification tool from [slsa-framework/slsa-verifier#installation](https://github.com/slsa-framework/slsa-verifier#installation)
+   and verify as follows:
+
+   ```sh
+   $ curl -sL https://github.com/google/go-containerregistry/releases/download/${VERSION}/multiple.intoto.jsonl > provenance.intoto.jsonl
+   $ # NOTE: You may be using a different architecture.
+   $ slsa-verifier-linux-amd64 verify-artifact go-containerregistry.tar.gz --provenance-path provenance.intoto.jsonl --source-uri github.com/google/go-containerregistry --source-tag "${VERSION}"
+     PASSED: Verified SLSA provenance
+   ```
+
+1. Unpack it in the PATH.
+
+   ```sh
+   $ tar -zxvf go-containerregistry.tar.gz -C /usr/local/bin/ crane
+   ```
 
 ### Install manually
+
 Install manually:
 
-```
+```sh
 go install github.com/google/go-containerregistry/cmd/crane@latest
 ```
 
diff --git a/cmd/crane/cmd/index.go b/cmd/crane/cmd/index.go
new file mode 100644
index 00000000..8d4b425d
--- /dev/null
+++ b/cmd/crane/cmd/index.go
@@ -0,0 +1,291 @@
+// Copyright 2023 Google LLC All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cmd
+
+import (
+	"errors"
+	"fmt"
+
+	"github.com/google/go-containerregistry/pkg/crane"
+	"github.com/google/go-containerregistry/pkg/logs"
+	"github.com/google/go-containerregistry/pkg/name"
+	v1 "github.com/google/go-containerregistry/pkg/v1"
+	"github.com/google/go-containerregistry/pkg/v1/empty"
+	"github.com/google/go-containerregistry/pkg/v1/match"
+	"github.com/google/go-containerregistry/pkg/v1/mutate"
+	"github.com/google/go-containerregistry/pkg/v1/partial"
+	"github.com/google/go-containerregistry/pkg/v1/remote"
+	"github.com/google/go-containerregistry/pkg/v1/types"
+	"github.com/spf13/cobra"
+)
+
+// NewCmdIndex creates a new cobra.Command for the index subcommand.
+func NewCmdIndex(options *[]crane.Option) *cobra.Command {
+	cmd := &cobra.Command{
+		Use:   "index",
+		Short: "Modify an image index.",
+		Args:  cobra.ExactArgs(1),
+		Run: func(cmd *cobra.Command, _ []string) {
+			cmd.Usage()
+		},
+	}
+	cmd.AddCommand(NewCmdIndexFilter(options), NewCmdIndexAppend(options))
+	return cmd
+}
+
+// NewCmdIndexFilter creates a new cobra.Command for the index filter subcommand.
+func NewCmdIndexFilter(options *[]crane.Option) *cobra.Command {
+	var newTag string
+	platforms := &platformsValue{}
+
+	cmd := &cobra.Command{
+		Use:   "filter",
+		Short: "Modifies a remote index by filtering based on platform.",
+		Example: `  # Filter out weird platforms from ubuntu, copy result to example.com/ubuntu
+  crane index filter ubuntu --platform linux/amd64 --platform linux/arm64 -t example.com/ubuntu
+
+  # Filter out any non-linux platforms, push to example.com/hello-world
+  crane index filter hello-world --platform linux -t example.com/hello-world
+
+  # Same as above, but in-place
+  crane index filter example.com/hello-world:some-tag --platform linux`,
+		Args: cobra.ExactArgs(1),
+		RunE: func(_ *cobra.Command, args []string) error {
+			o := crane.GetOptions(*options...)
+			baseRef := args[0]
+
+			ref, err := name.ParseReference(baseRef)
+			if err != nil {
+				return err
+			}
+			base, err := remote.Index(ref, o.Remote...)
+			if err != nil {
+				return fmt.Errorf("pulling %s: %w", baseRef, err)
+			}
+
+			idx := filterIndex(base, platforms.platforms)
+
+			digest, err := idx.Digest()
+			if err != nil {
+				return err
+			}
+
+			if newTag != "" {
+				ref, err = name.ParseReference(newTag)
+				if err != nil {
+					return fmt.Errorf("parsing reference %s: %w", newTag, err)
+				}
+			} else {
+				if _, ok := ref.(name.Digest); ok {
+					ref = ref.Context().Digest(digest.String())
+				}
+			}
+
+			if err := remote.WriteIndex(ref, idx, o.Remote...); err != nil {
+				return fmt.Errorf("pushing image %s: %w", newTag, err)
+			}
+			fmt.Println(ref.Context().Digest(digest.String()))
+			return nil
+		},
+	}
+	cmd.Flags().StringVarP(&newTag, "tag", "t", "", "Tag to apply to resulting image")
+
+	// Consider reusing the persistent flag for this, it's separate so we can have multiple values.
+	cmd.Flags().Var(platforms, "platform", "Specifies the platform(s) to keep from base in the form os/arch[/variant][:osversion][,<platform>] (e.g. linux/amd64).")
+
+	return cmd
+}
+
+// NewCmdIndexAppend creates a new cobra.Command for the index append subcommand.
+func NewCmdIndexAppend(options *[]crane.Option) *cobra.Command {
+	var baseRef, newTag string
+	var newManifests []string
+	var dockerEmptyBase, flatten bool
+
+	cmd := &cobra.Command{
+		Use:   "append",
+		Short: "Append manifests to a remote index.",
+		Long: `This sub-command pushes an index based on an (optional) base index, with appended manifests.
+
+The platform for appended manifests is inferred from the config file or omitted if that is infeasible.`,
+		Example: ` # Append a windows hello-world image to ubuntu, push to example.com/hello-world:weird
+  crane index append ubuntu -m hello-world@sha256:87b9ca29151260634b95efb84d43b05335dc3ed36cc132e2b920dd1955342d20 -t example.com/hello-world:weird
+
+  # Create an index from scratch for etcd.
+  crane index append -m registry.k8s.io/etcd-amd64:3.4.9 -m registry.k8s.io/etcd-arm64:3.4.9 -t example.com/etcd`,
+		Args: cobra.MaximumNArgs(1),
+		RunE: func(_ *cobra.Command, args []string) error {
+			if len(args) == 1 {
+				baseRef = args[0]
+			}
+			o := crane.GetOptions(*options...)
+
+			var (
+				base v1.ImageIndex
+				err  error
+				ref  name.Reference
+			)
+
+			if baseRef == "" {
+				if newTag == "" {
+					return errors.New("at least one of --base or --tag must be specified")
+				}
+
+				logs.Warn.Printf("base unspecified, using empty index")
+				base = empty.Index
+				if dockerEmptyBase {
+					base = mutate.IndexMediaType(base, types.DockerManifestList)
+				}
+			} else {
+				ref, err = name.ParseReference(baseRef)
+				if err != nil {
+					return err
+				}
+				base, err = remote.Index(ref, o.Remote...)
+				if err != nil {
+					return fmt.Errorf("pulling %s: %w", baseRef, err)
+				}
+			}
+
+			adds := make([]mutate.IndexAddendum, 0, len(newManifests))
+
+			for _, m := range newManifests {
+				ref, err := name.ParseReference(m)
+				if err != nil {
+					return err
+				}
+				desc, err := remote.Get(ref, o.Remote...)
+				if err != nil {
+					return err
+				}
+				if desc.MediaType.IsImage() {
+					img, err := desc.Image()
+					if err != nil {
+						return err
+					}
+
+					cf, err := img.ConfigFile()
+					if err != nil {
+						return err
+					}
+					newDesc, err := partial.Descriptor(img)
+					if err != nil {
+						return err
+					}
+					newDesc.Platform = cf.Platform()
+					adds = append(adds, mutate.IndexAddendum{
+						Add:        img,
+						Descriptor: *newDesc,
+					})
+				} else if desc.MediaType.IsIndex() {
+					idx, err := desc.ImageIndex()
+					if err != nil {
+						return err
+					}
+					if flatten {
+						im, err := idx.IndexManifest()
+						if err != nil {
+							return err
+						}
+						for _, child := range im.Manifests {
+							switch {
+							case child.MediaType.IsImage():
+								img, err := idx.Image(child.Digest)
+								if err != nil {
+									return err
+								}
+								adds = append(adds, mutate.IndexAddendum{
+									Add:        img,
+									Descriptor: child,
+								})
+							case child.MediaType.IsIndex():
+								idx, err := idx.ImageIndex(child.Digest)
+								if err != nil {
+									return err
+								}
+								adds = append(adds, mutate.IndexAddendum{
+									Add:        idx,
+									Descriptor: child,
+								})
+							default:
+								return fmt.Errorf("unexpected child %q with media type %q", child.Digest, child.MediaType)
+							}
+						}
+					} else {
+						adds = append(adds, mutate.IndexAddendum{
+							Add: idx,
+						})
+					}
+				} else {
+					return fmt.Errorf("saw unexpected MediaType %q for %q", desc.MediaType, m)
+				}
+			}
+
+			idx := mutate.AppendManifests(base, adds...)
+			digest, err := idx.Digest()
+			if err != nil {
+				return err
+			}
+
+			if newTag != "" {
+				ref, err = name.ParseReference(newTag)
+				if err != nil {
+					return fmt.Errorf("parsing reference %s: %w", newTag, err)
+				}
+			} else {
+				if _, ok := ref.(name.Digest); ok {
+					ref = ref.Context().Digest(digest.String())
+				}
+			}
+
+			if err := remote.WriteIndex(ref, idx, o.Remote...); err != nil {
+				return fmt.Errorf("pushing image %s: %w", newTag, err)
+			}
+			fmt.Println(ref.Context().Digest(digest.String()))
+			return nil
+		},
+	}
+	cmd.Flags().StringVarP(&newTag, "tag", "t", "", "Tag to apply to resulting image")
+	cmd.Flags().StringSliceVarP(&newManifests, "manifest", "m", []string{}, "References to manifests to append to the base index")
+	cmd.Flags().BoolVar(&dockerEmptyBase, "docker-empty-base", false, "If true, empty base index will have Docker media types instead of OCI")
+	cmd.Flags().BoolVar(&flatten, "flatten", true, "If true, appending an index will append each of its children rather than the index itself")
+
+	return cmd
+}
+
+func filterIndex(idx v1.ImageIndex, platforms []v1.Platform) v1.ImageIndex {
+	matcher := not(satisfiesPlatforms(platforms))
+	return mutate.RemoveManifests(idx, matcher)
+}
+
+func satisfiesPlatforms(platforms []v1.Platform) match.Matcher {
+	return func(desc v1.Descriptor) bool {
+		if desc.Platform == nil {
+			return false
+		}
+		for _, p := range platforms {
+			if desc.Platform.Satisfies(p) {
+				return true
+			}
+		}
+		return false
+	}
+}
+
+func not(in match.Matcher) match.Matcher {
+	return func(desc v1.Descriptor) bool {
+		return !in(desc)
+	}
+}
diff --git a/cmd/crane/cmd/list.go b/cmd/crane/cmd/list.go
index 417b3721..3902ccde 100644
--- a/cmd/crane/cmd/list.go
+++ b/cmd/crane/cmd/list.go
@@ -16,6 +16,7 @@ package cmd
 
 import (
 	"fmt"
+	"strings"
 
 	"github.com/google/go-containerregistry/pkg/crane"
 	"github.com/google/go-containerregistry/pkg/name"
@@ -24,7 +25,7 @@ import (
 
 // NewCmdList creates a new cobra.Command for the ls subcommand.
 func NewCmdList(options *[]crane.Option) *cobra.Command {
-	var fullRef bool
+	var fullRef, omitDigestTags bool
 	cmd := &cobra.Command{
 		Use:   "ls REPO",
 		Short: "List the tags in a repo",
@@ -42,6 +43,10 @@ func NewCmdList(options *[]crane.Option) *cobra.Command {
 			}
 
 			for _, tag := range tags {
+				if omitDigestTags && strings.HasPrefix(tag, "sha256-") {
+					continue
+				}
+
 				if fullRef {
 					fmt.Println(r.Tag(tag))
 				} else {
@@ -52,5 +57,6 @@ func NewCmdList(options *[]crane.Option) *cobra.Command {
 		},
 	}
 	cmd.Flags().BoolVar(&fullRef, "full-ref", false, "(Optional) if true, print the full image reference")
+	cmd.Flags().BoolVar(&omitDigestTags, "omit-digest-tags", false, "(Optional), if true, omit digest tags (e.g., ':sha256-...')")
 	return cmd
 }
diff --git a/cmd/crane/cmd/root.go b/cmd/crane/cmd/root.go
index e635523b..cb6fde55 100644
--- a/cmd/crane/cmd/root.go
+++ b/cmd/crane/cmd/root.go
@@ -106,6 +106,7 @@ func New(use, short string, options []crane.Option) *cobra.Command {
 		cmd.NewCmdEdit(&options),
 		NewCmdExport(&options),
 		NewCmdFlatten(&options),
+		NewCmdIndex(&options),
 		NewCmdList(&options),
 		NewCmdManifest(&options),
 		NewCmdMutate(&options),
diff --git a/cmd/crane/cmd/util.go b/cmd/crane/cmd/util.go
index 1e08a1ba..f4ff6514 100644
--- a/cmd/crane/cmd/util.go
+++ b/cmd/crane/cmd/util.go
@@ -15,12 +15,40 @@
 package cmd
 
 import (
-	"fmt"
 	"strings"
 
 	v1 "github.com/google/go-containerregistry/pkg/v1"
 )
 
+type platformsValue struct {
+	platforms []v1.Platform
+}
+
+func (ps *platformsValue) Set(platform string) error {
+	if ps.platforms == nil {
+		ps.platforms = []v1.Platform{}
+	}
+	p, err := parsePlatform(platform)
+	if err != nil {
+		return err
+	}
+	pv := platformValue{p}
+	ps.platforms = append(ps.platforms, *pv.platform)
+	return nil
+}
+
+func (ps *platformsValue) String() string {
+	ss := make([]string, 0, len(ps.platforms))
+	for _, p := range ps.platforms {
+		ss = append(ss, p.String())
+	}
+	return strings.Join(ss, ",")
+}
+
+func (ps *platformsValue) Type() string {
+	return "platform(s)"
+}
+
 type platformValue struct {
 	platform *v1.Platform
 }
@@ -46,14 +74,7 @@ func platformToString(p *v1.Platform) string {
 	if p == nil {
 		return "all"
 	}
-	platform := ""
-	if p.OS != "" && p.Architecture != "" {
-		platform = p.OS + "/" + p.Architecture
-	}
-	if p.Variant != "" {
-		platform += "/" + p.Variant
-	}
-	return platform
+	return p.String()
 }
 
 func parsePlatform(platform string) (*v1.Platform, error) {
@@ -61,27 +82,5 @@ func parsePlatform(platform string) (*v1.Platform, error) {
 		return nil, nil
 	}
 
-	p := &v1.Platform{}
-
-	parts := strings.SplitN(platform, ":", 2)
-	if len(parts) == 2 {
-		p.OSVersion = parts[1]
-	}
-
-	parts = strings.Split(parts[0], "/")
-
-	if len(parts) < 2 {
-		return nil, fmt.Errorf("failed to parse platform '%s': expected format os/arch[/variant]", platform)
-	}
-	if len(parts) > 3 {
-		return nil, fmt.Errorf("failed to parse platform '%s': too many slashes", platform)
-	}
-
-	p.OS = parts[0]
-	p.Architecture = parts[1]
-	if len(parts) > 2 {
-		p.Variant = parts[2]
-	}
-
-	return p, nil
+	return v1.ParsePlatform(platform)
 }
diff --git a/cmd/crane/doc/crane.md b/cmd/crane/doc/crane.md
index 84e9dbc7..87776e6a 100644
--- a/cmd/crane/doc/crane.md
+++ b/cmd/crane/doc/crane.md
@@ -28,6 +28,7 @@ crane [flags]
 * [crane digest](crane_digest.md)	 - Get the digest of an image
 * [crane export](crane_export.md)	 - Export filesystem of a container image as a tarball
 * [crane flatten](crane_flatten.md)	 - Flatten an image's layers into a single layer
+* [crane index](crane_index.md)	 - Modify an image index.
 * [crane ls](crane_ls.md)	 - List the tags in a repo
 * [crane manifest](crane_manifest.md)	 - Get the manifest of an image
 * [crane mutate](crane_mutate.md)	 - Modify image labels and annotations. The container must be pushed to a registry, and the manifest is updated there.
diff --git a/cmd/crane/doc/crane_index.md b/cmd/crane/doc/crane_index.md
new file mode 100644
index 00000000..2adea48a
--- /dev/null
+++ b/cmd/crane/doc/crane_index.md
@@ -0,0 +1,29 @@
+## crane index
+
+Modify an image index.
+
+```
+crane index [flags]
+```
+
+### Options
+
+```
+  -h, --help   help for index
+```
+
+### Options inherited from parent commands
+
+```
+      --allow-nondistributable-artifacts   Allow pushing non-distributable (foreign) layers
+      --insecure                           Allow image references to be fetched without TLS
+      --platform platform                  Specifies the platform in the form os/arch[/variant][:osversion] (e.g. linux/amd64). (default all)
+  -v, --verbose                            Enable debug logs
+```
+
+### SEE ALSO
+
+* [crane](crane.md)	 - Crane is a tool for managing container images
+* [crane index append](crane_index_append.md)	 - Append manifests to a remote index.
+* [crane index filter](crane_index_filter.md)	 - Modifies a remote index by filtering based on platform.
+
diff --git a/cmd/crane/doc/crane_index_append.md b/cmd/crane/doc/crane_index_append.md
new file mode 100644
index 00000000..a6c1541d
--- /dev/null
+++ b/cmd/crane/doc/crane_index_append.md
@@ -0,0 +1,47 @@
+## crane index append
+
+Append manifests to a remote index.
+
+### Synopsis
+
+This sub-command pushes an index based on an (optional) base index, with appended manifests.
+
+The platform for appended manifests is inferred from the config file or omitted if that is infeasible.
+
+```
+crane index append [flags]
+```
+
+### Examples
+
+```
+ # Append a windows hello-world image to ubuntu, push to example.com/hello-world:weird
+  crane index append ubuntu -m hello-world@sha256:87b9ca29151260634b95efb84d43b05335dc3ed36cc132e2b920dd1955342d20 -t example.com/hello-world:weird
+
+  # Create an index from scratch for etcd.
+  crane index append -m registry.k8s.io/etcd-amd64:3.4.9 -m registry.k8s.io/etcd-arm64:3.4.9 -t example.com/etcd
+```
+
+### Options
+
+```
+      --docker-empty-base   If true, empty base index will have Docker media types instead of OCI
+      --flatten             If true, appending an index will append each of its children rather than the index itself (default true)
+  -h, --help                help for append
+  -m, --manifest strings    References to manifests to append to the base index
+  -t, --tag string          Tag to apply to resulting image
+```
+
+### Options inherited from parent commands
+
+```
+      --allow-nondistributable-artifacts   Allow pushing non-distributable (foreign) layers
+      --insecure                           Allow image references to be fetched without TLS
+      --platform platform                  Specifies the platform in the form os/arch[/variant][:osversion] (e.g. linux/amd64). (default all)
+  -v, --verbose                            Enable debug logs
+```
+
+### SEE ALSO
+
+* [crane index](crane_index.md)	 - Modify an image index.
+
diff --git a/cmd/crane/doc/crane_index_filter.md b/cmd/crane/doc/crane_index_filter.md
new file mode 100644
index 00000000..bda1f8d7
--- /dev/null
+++ b/cmd/crane/doc/crane_index_filter.md
@@ -0,0 +1,41 @@
+## crane index filter
+
+Modifies a remote index by filtering based on platform.
+
+```
+crane index filter [flags]
+```
+
+### Examples
+
+```
+  # Filter out weird platforms from ubuntu, copy result to example.com/ubuntu
+  crane index filter ubuntu --platform linux/amd64 --platform linux/arm64 -t example.com/ubuntu
+
+  # Filter out any non-linux platforms, push to example.com/hello-world
+  crane index filter hello-world --platform linux -t example.com/hello-world
+
+  # Same as above, but in-place
+  crane index filter example.com/hello-world:some-tag --platform linux
+```
+
+### Options
+
+```
+  -h, --help                   help for filter
+      --platform platform(s)   Specifies the platform(s) to keep from base in the form os/arch[/variant][:osversion][,<platform>] (e.g. linux/amd64).
+  -t, --tag string             Tag to apply to resulting image
+```
+
+### Options inherited from parent commands
+
+```
+      --allow-nondistributable-artifacts   Allow pushing non-distributable (foreign) layers
+      --insecure                           Allow image references to be fetched without TLS
+  -v, --verbose                            Enable debug logs
+```
+
+### SEE ALSO
+
+* [crane index](crane_index.md)	 - Modify an image index.
+
diff --git a/cmd/crane/doc/crane_ls.md b/cmd/crane/doc/crane_ls.md
index a4c5ceed..6616820b 100644
--- a/cmd/crane/doc/crane_ls.md
+++ b/cmd/crane/doc/crane_ls.md
@@ -9,8 +9,9 @@ crane ls REPO [flags]
 ### Options
 
 ```
-      --full-ref   (Optional) if true, print the full image reference
-  -h, --help       help for ls
+      --full-ref           (Optional) if true, print the full image reference
+  -h, --help               help for ls
+      --omit-digest-tags   (Optional), if true, omit digest tags (e.g., ':sha256-...')
 ```
 
 ### Options inherited from parent commands
diff --git a/cmd/crane/recipes.md b/cmd/crane/recipes.md
index 17accd56..1c0121de 100644
--- a/cmd/crane/recipes.md
+++ b/cmd/crane/recipes.md
@@ -79,3 +79,27 @@ crane manifest gcr.io/buildpacks/builder:v1 | jq '.config.size + ([.layers[].siz
 ```
 
 For image indexes, you can pass the `--platform` flag to `crane` to get a platform-specific image.
+
+### Filter irrelevant platforms from a multi-platform image
+
+Perhaps you use a base image that supports a wide variety of exotic platforms, but you only care about linux/amd64 and linux/arm64.
+If you want to copy that base image into a different registry, you will end up with a bunch of images you don't use.
+You can filter the base to include only platforms that are relevant to you.
+
+```
+crane index filter ubuntu --platform linux/amd64 --platform linux/arm64 -t ${IMAGE}
+```
+
+Note that this will obviously modify the digest of the multi-platform image you're using, so this may invalidate other artifacts that reference it, e.g. signatures.
+
+### Create a multi-platform image from scratch
+
+If you have a bunch of platform-specific images that you want to turn into a multi-platform image, `crane index append` can do that:
+
+```
+crane index append -t ${IMAGE} \
+  -m ubuntu@sha256:c985bc3f77946b8e92c9a3648c6f31751a7dd972e06604785e47303f4ad47c4c \
+  -m ubuntu@sha256:61bd0b97000996232eb07b8d0e9375d14197f78aa850c2506417ef995a7199a7
+```
+
+Note that this is less flexible than [`manifest-tool`](https://github.com/estesp/manifest-tool) because it derives the platform from each image's config file, but it should work in most cases.
diff --git a/go.sum b/go.sum
index 1bfe65be..c8f9b9ee 100644
--- a/go.sum
+++ b/go.sum
@@ -1,97 +1,17 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
-cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
-cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
-cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
-cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
-cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
-cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
-cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
-cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
-cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
-cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
-cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
-cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
-cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
-cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
-cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
-cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
-cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
-cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
-cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
-cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
-cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
-cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
-cloud.google.com/go v0.102.1 h1:vpK6iQWv/2uUeFJth4/cBHsQAGjn1iIE6AAlxipRaA0=
-cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
-cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
-cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
-cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
-cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
-cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
-cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
-cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
-cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
-cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
-cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
 cloud.google.com/go/compute v1.10.0 h1:aoLIYaA1fX3ywihqpBk2APQKOo20nXsp1GEZQbx5Jk4=
 cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
-cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
-cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
-cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
-cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
-cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
 github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
 github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
-github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/containerd/stargz-snapshotter/estargz v0.12.1 h1:+7nYmHJb0tEkcRaAW+MHqoKaJYZmkikupxCqVtmPuY0=
 github.com/containerd/stargz-snapshotter/estargz v0.12.1/go.mod h1:12VUuCq3qPq4y8yUW+l5w3+oXV3cx2Po3KSe/SmPGqw=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
 github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -107,117 +27,23 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh
 github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
-github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
-github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
-github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
 github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
-github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
-github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
-github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
-github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
-github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
-github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
 github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
 github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
 github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
 github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -238,11 +64,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
-github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -250,7 +71,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI=
 github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
@@ -258,513 +78,74 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
 github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
-github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
-github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
-golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
-golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
 golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
 golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
-golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
-golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 golang.org/x/oauth2 v0.1.0 h1:isLCZuhj4v+tYv7eskaN4v/TM+A1begWWgyVJDdl1+Y=
 golang.org/x/oauth2 v0.1.0/go.mod h1:G9FE4dLTsbXUu90h/Pf85g4w1D+SSAgR+q46nJZ8M4A=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs=
 golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
-golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
-google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
-google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
-google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
-google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
-google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
-google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
-google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
-google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
-google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
-google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
-google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
-google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
-google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
-google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
-google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
-google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
-google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
-google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
-google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
-google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
-google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
-google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
-google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
-google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
-google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
-google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
-google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
-google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
-google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
-google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
 google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 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.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
 gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
 gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
-rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/internal/cmd/edit.go b/internal/cmd/edit.go
index 01b64fc0..d0e4d0de 100644
--- a/internal/cmd/edit.go
+++ b/internal/cmd/edit.go
@@ -17,6 +17,7 @@ package cmd
 import (
 	"archive/tar"
 	"bytes"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -30,6 +31,7 @@ import (
 	v1 "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/mutate"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
+	"github.com/google/go-containerregistry/pkg/v1/static"
 	"github.com/google/go-containerregistry/pkg/v1/tarball"
 	"github.com/google/go-containerregistry/pkg/v1/types"
 	"github.com/spf13/cobra"
@@ -82,18 +84,18 @@ func NewCmdEditConfig(options *[]crane.Option) *cobra.Command {
 
 // NewCmdManifest creates a new cobra.Command for the manifest subcommand.
 func NewCmdEditManifest(options *[]crane.Option) *cobra.Command {
-	var dst string
+	var (
+		dst string
+		mt  string
+	)
 	cmd := &cobra.Command{
 		Use:   "manifest",
 		Short: "Edit an image's manifest.",
-		Example: `  # Edit ubuntu's config file
-  crane edit config ubuntu
-
-  # Overwrite ubuntu's config file with '{}'
-  echo '{}' | crane edit config ubuntu`,
+		Example: `  # Edit ubuntu's manifest
+  crane edit manifest ubuntu`,
 		Args: cobra.ExactArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			ref, err := editManifest(cmd.InOrStdin(), cmd.OutOrStdout(), args[0], dst, *options...)
+			ref, err := editManifest(cmd.InOrStdin(), cmd.OutOrStdout(), args[0], dst, mt, *options...)
 			if err != nil {
 				return fmt.Errorf("editing manifest: %w", err)
 			}
@@ -102,6 +104,7 @@ func NewCmdEditManifest(options *[]crane.Option) *cobra.Command {
 		},
 	}
 	cmd.Flags().StringVarP(&dst, "tag", "t", "", "New tag reference to apply to mutated image. If not provided, uses original tag or pushes a new digest.")
+	cmd.Flags().StringVarP(&mt, "media-type", "m", "", "Override the mediaType used as the Content-Type for PUT")
 
 	return cmd
 }
@@ -158,6 +161,15 @@ func editConfig(in io.Reader, out io.Writer, src, dst string, options ...crane.O
 		return nil, err
 	}
 
+	m, err := img.Manifest()
+	if err != nil {
+		return nil, err
+	}
+	mt, err := img.MediaType()
+	if err != nil {
+		return nil, err
+	}
+
 	var edited []byte
 	if interactive(in, out) {
 		rcf, err := img.RawConfigFile()
@@ -176,21 +188,25 @@ func editConfig(in io.Reader, out io.Writer, src, dst string, options ...crane.O
 		edited = b
 	}
 
-	cf, err := v1.ParseConfigFile(bytes.NewReader(edited))
+	l := static.NewLayer(edited, m.Config.MediaType)
+	layerDigest, err := l.Digest()
 	if err != nil {
 		return nil, err
 	}
 
-	img, err = mutate.ConfigFile(img, cf)
+	m.Config.Digest = layerDigest
+	m.Config.Size = int64(len(edited))
+	b, err := json.Marshal(m)
 	if err != nil {
 		return nil, err
 	}
-
-	digest, err := img.Digest()
-	if err != nil {
-		return nil, err
+	rm := &rawManifest{
+		body:      b,
+		mediaType: mt,
 	}
 
+	digest, _, _ := v1.SHA256(bytes.NewReader(b))
+
 	if dst == "" {
 		dst = src
 		ref, err := name.ParseReference(src, o.Name...)
@@ -207,14 +223,18 @@ func editConfig(in io.Reader, out io.Writer, src, dst string, options ...crane.O
 		return nil, err
 	}
 
-	if err := crane.Push(img, dst, options...); err != nil {
+	if err := remote.WriteLayer(dstRef.Context(), l, o.Remote...); err != nil {
+		return nil, err
+	}
+
+	if err := remote.Put(dstRef, rm, o.Remote...); err != nil {
 		return nil, err
 	}
 
 	return dstRef, nil
 }
 
-func editManifest(in io.Reader, out io.Writer, src string, dst string, options ...crane.Option) (name.Reference, error) {
+func editManifest(in io.Reader, out io.Writer, src string, dst string, mt string, options ...crane.Option) (name.Reference, error) {
 	o := crane.GetOptions(options...)
 
 	ref, err := name.ParseReference(src, o.Name...)
@@ -257,9 +277,22 @@ func editManifest(in io.Reader, out io.Writer, src string, dst string, options .
 		return nil, err
 	}
 
+	if mt == "" {
+		// If --media-type is unset, use Content-Type by default.
+		mt = string(desc.MediaType)
+
+		// If document contains mediaType, default to that.
+		wmt := withMediaType{}
+		if err := json.Unmarshal(edited, &wmt); err == nil {
+			if wmt.MediaType != "" {
+				mt = wmt.MediaType
+			}
+		}
+	}
+
 	rm := &rawManifest{
 		body:      edited,
-		mediaType: desc.MediaType,
+		mediaType: types.MediaType(mt),
 	}
 
 	if err := remote.Put(dstRef, rm, o.Remote...); err != nil {
@@ -402,6 +435,10 @@ func normalize(name string) string {
 	return filepath.Clean("/" + name)
 }
 
+type withMediaType struct {
+	MediaType string `json:"mediaType,omitempty"`
+}
+
 type rawManifest struct {
 	body      []byte
 	mediaType types.MediaType
diff --git a/pkg/authn/kubernetes/keychain.go b/pkg/authn/kubernetes/keychain.go
index cdda38ac..368d829a 100644
--- a/pkg/authn/kubernetes/keychain.go
+++ b/pkg/authn/kubernetes/keychain.go
@@ -58,6 +58,12 @@ type Options struct {
 	// ImagePullSecrets holds the names of the Kubernetes secrets (scoped to
 	// Namespace) containing credential data to use for the image pull.
 	ImagePullSecrets []string
+
+	// UseMountSecrets determines whether or not mount secrets in the ServiceAccount
+	// should be considered. Mount secrets are those listed under the `.secrets`
+	// attribute of the ServiceAccount resource. Ignored if ServiceAccountName is set
+	// to NoServiceAccount.
+	UseMountSecrets bool
 }
 
 // New returns a new authn.Keychain suitable for resolving image references as
@@ -111,6 +117,19 @@ func New(ctx context.Context, client kubernetes.Interface, opt Options) (authn.K
 				}
 				pullSecrets = append(pullSecrets, *ps)
 			}
+
+			if opt.UseMountSecrets {
+				for _, obj := range sa.Secrets {
+					s, err := client.CoreV1().Secrets(opt.Namespace).Get(ctx, obj.Name, metav1.GetOptions{})
+					if k8serrors.IsNotFound(err) {
+						logs.Warn.Printf("secret %s/%s not found; ignoring", opt.Namespace, obj.Name)
+						continue
+					} else if err != nil {
+						return nil, err
+					}
+					pullSecrets = append(pullSecrets, *s)
+				}
+			}
 		}
 	}
 
diff --git a/pkg/crane/options.go b/pkg/crane/options.go
index 2e992789..5d2e0e4b 100644
--- a/pkg/crane/options.go
+++ b/pkg/crane/options.go
@@ -16,6 +16,7 @@ package crane
 
 import (
 	"context"
+	"crypto/tls"
 	"net/http"
 
 	"github.com/google/go-containerregistry/pkg/authn"
@@ -30,6 +31,9 @@ type Options struct {
 	Remote   []remote.Option
 	Platform *v1.Platform
 	Keychain authn.Keychain
+
+	transport http.RoundTripper
+	insecure  bool
 }
 
 // GetOptions exposes the underlying []remote.Option, []name.Option, and
@@ -47,9 +51,22 @@ func makeOptions(opts ...Option) Options {
 		},
 		Keychain: authn.DefaultKeychain,
 	}
+
 	for _, o := range opts {
 		o(&opt)
 	}
+
+	// Allow for untrusted certificates if the user
+	// passed Insecure but no custom transport.
+	if opt.insecure && opt.transport == nil {
+		transport := remote.DefaultTransport.(*http.Transport).Clone()
+		transport.TLSClientConfig = &tls.Config{
+			InsecureSkipVerify: true, //nolint: gosec
+		}
+
+		WithTransport(transport)(&opt)
+	}
+
 	return opt
 }
 
@@ -57,16 +74,21 @@ func makeOptions(opts ...Option) Options {
 type Option func(*Options)
 
 // WithTransport is a functional option for overriding the default transport
-// for remote operations.
+// for remote operations. Setting a transport will override the Insecure option's
+// configuration allowing for image registries to use untrusted certificates.
 func WithTransport(t http.RoundTripper) Option {
 	return func(o *Options) {
 		o.Remote = append(o.Remote, remote.WithTransport(t))
+		o.transport = t
 	}
 }
 
 // Insecure is an Option that allows image references to be fetched without TLS.
+// This will also allow for untrusted (e.g. self-signed) certificates in cases where
+// the default transport is used (i.e. when WithTransport is not used).
 func Insecure(o *Options) {
 	o.Name = append(o.Name, name.Insecure)
+	o.insecure = true
 }
 
 // WithPlatform is an Option to specify the platform.
diff --git a/pkg/legacy/tarball/write.go b/pkg/legacy/tarball/write.go
index 2a2b73c1..e3f173cb 100644
--- a/pkg/legacy/tarball/write.go
+++ b/pkg/legacy/tarball/write.go
@@ -17,8 +17,6 @@ package tarball
 import (
 	"archive/tar"
 	"bytes"
-	"crypto/sha256"
-	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -63,8 +61,9 @@ func v1LayerID(layer v1.Layer, parentID string, rawConfig []byte) (string, error
 	if len(rawConfig) != 0 {
 		s = fmt.Sprintf("%s %s", s, string(rawConfig))
 	}
-	rawDigest := sha256.Sum256([]byte(s))
-	return hex.EncodeToString(rawDigest[:]), nil
+
+	h, _, _ := v1.SHA256(strings.NewReader(s))
+	return h.Hex, nil
 }
 
 // newTopV1Layer creates a new v1Layer for a layer other than the top layer in a v1 image tarball.
diff --git a/pkg/name/digest.go b/pkg/name/digest.go
index c4a2e693..c049c1ef 100644
--- a/pkg/name/digest.go
+++ b/pkg/name/digest.go
@@ -15,6 +15,7 @@
 package name
 
 import (
+	// nolint: depguard
 	_ "crypto/sha256" // Recommended by go-digest.
 	"strings"
 
diff --git a/pkg/registry/manifest.go b/pkg/registry/manifest.go
index 6c556604..cd788f7b 100644
--- a/pkg/registry/manifest.go
+++ b/pkg/registry/manifest.go
@@ -16,8 +16,6 @@ package registry
 
 import (
 	"bytes"
-	"crypto/sha256"
-	"encoding/hex"
 	"encoding/json"
 	"fmt"
 	"io"
@@ -81,6 +79,16 @@ func isCatalog(req *http.Request) bool {
 	return elems[len(elems)-1] == "_catalog"
 }
 
+// Returns whether this url should be handled by the referrers handler
+func isReferrers(req *http.Request) bool {
+	elems := strings.Split(req.URL.Path, "/")
+	elems = elems[1:]
+	if len(elems) < 4 {
+		return false
+	}
+	return elems[len(elems)-2] == "referrers"
+}
+
 // https://github.com/opencontainers/distribution-spec/blob/master/spec.md#pulling-an-image-manifest
 // https://github.com/opencontainers/distribution-spec/blob/master/spec.md#pushing-an-image
 func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regError {
@@ -110,9 +118,8 @@ func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regErro
 				Message: "Unknown manifest",
 			}
 		}
-		rd := sha256.Sum256(m.blob)
-		d := "sha256:" + hex.EncodeToString(rd[:])
-		resp.Header().Set("Docker-Content-Digest", d)
+		h, _, _ := v1.SHA256(bytes.NewReader(m.blob))
+		resp.Header().Set("Docker-Content-Digest", h.String())
 		resp.Header().Set("Content-Type", m.contentType)
 		resp.Header().Set("Content-Length", fmt.Sprint(len(m.blob)))
 		resp.WriteHeader(http.StatusOK)
@@ -137,9 +144,8 @@ func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regErro
 				Message: "Unknown manifest",
 			}
 		}
-		rd := sha256.Sum256(m.blob)
-		d := "sha256:" + hex.EncodeToString(rd[:])
-		resp.Header().Set("Docker-Content-Digest", d)
+		h, _, _ := v1.SHA256(bytes.NewReader(m.blob))
+		resp.Header().Set("Docker-Content-Digest", h.String())
 		resp.Header().Set("Content-Type", m.contentType)
 		resp.Header().Set("Content-Length", fmt.Sprint(len(m.blob)))
 		resp.WriteHeader(http.StatusOK)
@@ -153,8 +159,8 @@ func (m *manifests) handle(resp http.ResponseWriter, req *http.Request) *regErro
 		}
 		b := &bytes.Buffer{}
 		io.Copy(b, req.Body)
-		rd := sha256.Sum256(b.Bytes())
-		digest := "sha256:" + hex.EncodeToString(rd[:])
+		h, _, _ := v1.SHA256(bytes.NewReader(b.Bytes()))
+		digest := h.String()
 		mf := manifest{
 			blob:        b.Bytes(),
 			contentType: req.Header.Get("Content-Type"),
@@ -343,3 +349,82 @@ func (m *manifests) handleCatalog(resp http.ResponseWriter, req *http.Request) *
 		Message: "We don't understand your method + url",
 	}
 }
+
+// TODO: implement handling of artifactType querystring
+func (m *manifests) handleReferrers(resp http.ResponseWriter, req *http.Request) *regError {
+	// Ensure this is a GET request
+	if req.Method != "GET" {
+		return &regError{
+			Status:  http.StatusBadRequest,
+			Code:    "METHOD_UNKNOWN",
+			Message: "We don't understand your method + url",
+		}
+	}
+
+	elem := strings.Split(req.URL.Path, "/")
+	elem = elem[1:]
+	target := elem[len(elem)-1]
+	repo := strings.Join(elem[1:len(elem)-2], "/")
+
+	// Validate that incoming target is a valid digest
+	if _, err := v1.NewHash(target); err != nil {
+		return &regError{
+			Status:  http.StatusBadRequest,
+			Code:    "UNSUPPORTED",
+			Message: "Target must be a valid digest",
+		}
+	}
+
+	m.lock.Lock()
+	defer m.lock.Unlock()
+
+	digestToManifestMap, repoExists := m.manifests[repo]
+	if !repoExists {
+		return &regError{
+			Status:  http.StatusNotFound,
+			Code:    "NAME_UNKNOWN",
+			Message: "Unknown name",
+		}
+	}
+
+	im := v1.IndexManifest{
+		SchemaVersion: 2,
+		MediaType:     types.OCIImageIndex,
+		Manifests:     []v1.Descriptor{},
+	}
+	for digest, manifest := range digestToManifestMap {
+		h, err := v1.NewHash(digest)
+		if err != nil {
+			continue
+		}
+		var refPointer struct {
+			Subject *v1.Descriptor `json:"subject"`
+		}
+		json.Unmarshal(manifest.blob, &refPointer)
+		if refPointer.Subject == nil {
+			continue
+		}
+		referenceDigest := refPointer.Subject.Digest
+		if referenceDigest.String() != target {
+			continue
+		}
+		// At this point, we know the current digest references the target
+		var imageAsArtifact struct {
+			Config struct {
+				MediaType string `json:"mediaType"`
+			} `json:"config"`
+		}
+		json.Unmarshal(manifest.blob, &imageAsArtifact)
+		im.Manifests = append(im.Manifests, v1.Descriptor{
+			MediaType:    types.MediaType(manifest.contentType),
+			Size:         int64(len(manifest.blob)),
+			Digest:       h,
+			ArtifactType: imageAsArtifact.Config.MediaType,
+		})
+	}
+	msg, _ := json.Marshal(&im)
+	resp.Header().Set("Content-Length", fmt.Sprint(len(msg)))
+	resp.WriteHeader(http.StatusOK)
+	io.Copy(resp, bytes.NewReader([]byte(msg)))
+	return nil
+}
diff --git a/pkg/registry/registry.go b/pkg/registry/registry.go
index e6129092..303e6e7e 100644
--- a/pkg/registry/registry.go
+++ b/pkg/registry/registry.go
@@ -30,9 +30,10 @@ import (
 )
 
 type registry struct {
-	log       *log.Logger
-	blobs     blobs
-	manifests manifests
+	log              *log.Logger
+	blobs            blobs
+	manifests        manifests
+	referrersEnabled bool
 }
 
 // https://docs.docker.com/registry/spec/api/#api-version-check
@@ -50,6 +51,9 @@ func (r *registry) v2(resp http.ResponseWriter, req *http.Request) *regError {
 	if isCatalog(req) {
 		return r.manifests.handleCatalog(resp, req)
 	}
+	if r.referrersEnabled && isReferrers(req) {
+		return r.manifests.handleReferrers(resp, req)
+	}
 	resp.Header().Set("Docker-Distribution-API-Version", "registry/2.0")
 	if req.URL.Path != "/v2/" && req.URL.Path != "/v2" {
 		return &regError{
@@ -104,3 +108,10 @@ func Logger(l *log.Logger) Option {
 		r.blobs.log = l
 	}
 }
+
+// WithReferrersSupport enables the referrers API endpoint (OCI 1.1+)
+func WithReferrersSupport(enabled bool) Option {
+	return func(r *registry) {
+		r.referrersEnabled = enabled
+	}
+}
diff --git a/pkg/v1/config.go b/pkg/v1/config.go
index 08ec0158..960c93b5 100644
--- a/pkg/v1/config.go
+++ b/pkg/v1/config.go
@@ -38,6 +38,21 @@ type ConfigFile struct {
 	Config        Config    `json:"config"`
 	OSVersion     string    `json:"os.version,omitempty"`
 	Variant       string    `json:"variant,omitempty"`
+	OSFeatures    []string  `json:"os.features,omitempty"`
+}
+
+// Platform attempts to generates a Platform from the ConfigFile fields.
+func (cf *ConfigFile) Platform() *Platform {
+	if cf.OS == "" && cf.Architecture == "" && cf.OSVersion == "" && cf.Variant == "" && len(cf.OSFeatures) == 0 {
+		return nil
+	}
+	return &Platform{
+		OS:           cf.OS,
+		Architecture: cf.Architecture,
+		OSVersion:    cf.OSVersion,
+		Variant:      cf.Variant,
+		OSFeatures:   cf.OSFeatures,
+	}
 }
 
 // History is one entry of a list recording how this container image was built.
diff --git a/pkg/v1/empty/index.go b/pkg/v1/empty/index.go
index 8edab24d..10665356 100644
--- a/pkg/v1/empty/index.go
+++ b/pkg/v1/empty/index.go
@@ -59,5 +59,6 @@ func (i emptyIndex) ImageIndex(v1.Hash) (v1.ImageIndex, error) {
 func base() *v1.IndexManifest {
 	return &v1.IndexManifest{
 		SchemaVersion: 2,
+		MediaType:     types.OCIImageIndex,
 	}
 }
diff --git a/pkg/v1/fake/image.go b/pkg/v1/fake/image.go
index 8c4758d0..f95ac61a 100644
--- a/pkg/v1/fake/image.go
+++ b/pkg/v1/fake/image.go
@@ -143,7 +143,7 @@ type FakeImage struct {
 		result1 int64
 		result2 error
 	}
-	invocations      map[string][][]any
+	invocations      map[string][][]interface{}
 	invocationsMutex sync.RWMutex
 }
 
@@ -154,7 +154,7 @@ func (fake *FakeImage) ConfigFile() (*v1.ConfigFile, error) {
 	}{})
 	stub := fake.ConfigFileStub
 	fakeReturns := fake.configFileReturns
-	fake.recordInvocation("ConfigFile", []any{})
+	fake.recordInvocation("ConfigFile", []interface{}{})
 	fake.configFileMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -210,7 +210,7 @@ func (fake *FakeImage) ConfigName() (v1.Hash, error) {
 	}{})
 	stub := fake.ConfigNameStub
 	fakeReturns := fake.configNameReturns
-	fake.recordInvocation("ConfigName", []any{})
+	fake.recordInvocation("ConfigName", []interface{}{})
 	fake.configNameMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -266,7 +266,7 @@ func (fake *FakeImage) Digest() (v1.Hash, error) {
 	}{})
 	stub := fake.DigestStub
 	fakeReturns := fake.digestReturns
-	fake.recordInvocation("Digest", []any{})
+	fake.recordInvocation("Digest", []interface{}{})
 	fake.digestMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -323,7 +323,7 @@ func (fake *FakeImage) LayerByDiffID(arg1 v1.Hash) (v1.Layer, error) {
 	}{arg1})
 	stub := fake.LayerByDiffIDStub
 	fakeReturns := fake.layerByDiffIDReturns
-	fake.recordInvocation("LayerByDiffID", []any{arg1})
+	fake.recordInvocation("LayerByDiffID", []interface{}{arg1})
 	fake.layerByDiffIDMutex.Unlock()
 	if stub != nil {
 		return stub(arg1)
@@ -387,7 +387,7 @@ func (fake *FakeImage) LayerByDigest(arg1 v1.Hash) (v1.Layer, error) {
 	}{arg1})
 	stub := fake.LayerByDigestStub
 	fakeReturns := fake.layerByDigestReturns
-	fake.recordInvocation("LayerByDigest", []any{arg1})
+	fake.recordInvocation("LayerByDigest", []interface{}{arg1})
 	fake.layerByDigestMutex.Unlock()
 	if stub != nil {
 		return stub(arg1)
@@ -450,7 +450,7 @@ func (fake *FakeImage) Layers() ([]v1.Layer, error) {
 	}{})
 	stub := fake.LayersStub
 	fakeReturns := fake.layersReturns
-	fake.recordInvocation("Layers", []any{})
+	fake.recordInvocation("Layers", []interface{}{})
 	fake.layersMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -506,7 +506,7 @@ func (fake *FakeImage) Manifest() (*v1.Manifest, error) {
 	}{})
 	stub := fake.ManifestStub
 	fakeReturns := fake.manifestReturns
-	fake.recordInvocation("Manifest", []any{})
+	fake.recordInvocation("Manifest", []interface{}{})
 	fake.manifestMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -562,7 +562,7 @@ func (fake *FakeImage) MediaType() (types.MediaType, error) {
 	}{})
 	stub := fake.MediaTypeStub
 	fakeReturns := fake.mediaTypeReturns
-	fake.recordInvocation("MediaType", []any{})
+	fake.recordInvocation("MediaType", []interface{}{})
 	fake.mediaTypeMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -618,7 +618,7 @@ func (fake *FakeImage) RawConfigFile() ([]byte, error) {
 	}{})
 	stub := fake.RawConfigFileStub
 	fakeReturns := fake.rawConfigFileReturns
-	fake.recordInvocation("RawConfigFile", []any{})
+	fake.recordInvocation("RawConfigFile", []interface{}{})
 	fake.rawConfigFileMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -674,7 +674,7 @@ func (fake *FakeImage) RawManifest() ([]byte, error) {
 	}{})
 	stub := fake.RawManifestStub
 	fakeReturns := fake.rawManifestReturns
-	fake.recordInvocation("RawManifest", []any{})
+	fake.recordInvocation("RawManifest", []interface{}{})
 	fake.rawManifestMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -730,7 +730,7 @@ func (fake *FakeImage) Size() (int64, error) {
 	}{})
 	stub := fake.SizeStub
 	fakeReturns := fake.sizeReturns
-	fake.recordInvocation("Size", []any{})
+	fake.recordInvocation("Size", []interface{}{})
 	fake.sizeMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -779,7 +779,7 @@ func (fake *FakeImage) SizeReturnsOnCall(i int, result1 int64, result2 error) {
 	}{result1, result2}
 }
 
-func (fake *FakeImage) Invocations() map[string][][]any {
+func (fake *FakeImage) Invocations() map[string][][]interface{} {
 	fake.invocationsMutex.RLock()
 	defer fake.invocationsMutex.RUnlock()
 	fake.configFileMutex.RLock()
@@ -804,21 +804,21 @@ func (fake *FakeImage) Invocations() map[string][][]any {
 	defer fake.rawManifestMutex.RUnlock()
 	fake.sizeMutex.RLock()
 	defer fake.sizeMutex.RUnlock()
-	copiedInvocations := map[string][][]any{}
+	copiedInvocations := map[string][][]interface{}{}
 	for key, value := range fake.invocations {
 		copiedInvocations[key] = value
 	}
 	return copiedInvocations
 }
 
-func (fake *FakeImage) recordInvocation(key string, args []any) {
+func (fake *FakeImage) recordInvocation(key string, args []interface{}) {
 	fake.invocationsMutex.Lock()
 	defer fake.invocationsMutex.Unlock()
 	if fake.invocations == nil {
-		fake.invocations = map[string][][]any{}
+		fake.invocations = map[string][][]interface{}{}
 	}
 	if fake.invocations[key] == nil {
-		fake.invocations[key] = [][]any{}
+		fake.invocations[key] = [][]interface{}{}
 	}
 	fake.invocations[key] = append(fake.invocations[key], args)
 }
diff --git a/pkg/v1/fake/index.go b/pkg/v1/fake/index.go
index e430e865..8c66a984 100644
--- a/pkg/v1/fake/index.go
+++ b/pkg/v1/fake/index.go
@@ -95,7 +95,7 @@ type FakeImageIndex struct {
 		result1 int64
 		result2 error
 	}
-	invocations      map[string][][]any
+	invocations      map[string][][]interface{}
 	invocationsMutex sync.RWMutex
 }
 
@@ -106,7 +106,7 @@ func (fake *FakeImageIndex) Digest() (v1.Hash, error) {
 	}{})
 	stub := fake.DigestStub
 	fakeReturns := fake.digestReturns
-	fake.recordInvocation("Digest", []any{})
+	fake.recordInvocation("Digest", []interface{}{})
 	fake.digestMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -163,7 +163,7 @@ func (fake *FakeImageIndex) Image(arg1 v1.Hash) (v1.Image, error) {
 	}{arg1})
 	stub := fake.ImageStub
 	fakeReturns := fake.imageReturns
-	fake.recordInvocation("Image", []any{arg1})
+	fake.recordInvocation("Image", []interface{}{arg1})
 	fake.imageMutex.Unlock()
 	if stub != nil {
 		return stub(arg1)
@@ -227,7 +227,7 @@ func (fake *FakeImageIndex) ImageIndex(arg1 v1.Hash) (v1.ImageIndex, error) {
 	}{arg1})
 	stub := fake.ImageIndexStub
 	fakeReturns := fake.imageIndexReturns
-	fake.recordInvocation("ImageIndex", []any{arg1})
+	fake.recordInvocation("ImageIndex", []interface{}{arg1})
 	fake.imageIndexMutex.Unlock()
 	if stub != nil {
 		return stub(arg1)
@@ -290,7 +290,7 @@ func (fake *FakeImageIndex) IndexManifest() (*v1.IndexManifest, error) {
 	}{})
 	stub := fake.IndexManifestStub
 	fakeReturns := fake.indexManifestReturns
-	fake.recordInvocation("IndexManifest", []any{})
+	fake.recordInvocation("IndexManifest", []interface{}{})
 	fake.indexManifestMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -346,7 +346,7 @@ func (fake *FakeImageIndex) MediaType() (types.MediaType, error) {
 	}{})
 	stub := fake.MediaTypeStub
 	fakeReturns := fake.mediaTypeReturns
-	fake.recordInvocation("MediaType", []any{})
+	fake.recordInvocation("MediaType", []interface{}{})
 	fake.mediaTypeMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -402,7 +402,7 @@ func (fake *FakeImageIndex) RawManifest() ([]byte, error) {
 	}{})
 	stub := fake.RawManifestStub
 	fakeReturns := fake.rawManifestReturns
-	fake.recordInvocation("RawManifest", []any{})
+	fake.recordInvocation("RawManifest", []interface{}{})
 	fake.rawManifestMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -458,7 +458,7 @@ func (fake *FakeImageIndex) Size() (int64, error) {
 	}{})
 	stub := fake.SizeStub
 	fakeReturns := fake.sizeReturns
-	fake.recordInvocation("Size", []any{})
+	fake.recordInvocation("Size", []interface{}{})
 	fake.sizeMutex.Unlock()
 	if stub != nil {
 		return stub()
@@ -507,7 +507,7 @@ func (fake *FakeImageIndex) SizeReturnsOnCall(i int, result1 int64, result2 erro
 	}{result1, result2}
 }
 
-func (fake *FakeImageIndex) Invocations() map[string][][]any {
+func (fake *FakeImageIndex) Invocations() map[string][][]interface{} {
 	fake.invocationsMutex.RLock()
 	defer fake.invocationsMutex.RUnlock()
 	fake.digestMutex.RLock()
@@ -524,21 +524,21 @@ func (fake *FakeImageIndex) Invocations() map[string][][]any {
 	defer fake.rawManifestMutex.RUnlock()
 	fake.sizeMutex.RLock()
 	defer fake.sizeMutex.RUnlock()
-	copiedInvocations := map[string][][]any{}
+	copiedInvocations := map[string][][]interface{}{}
 	for key, value := range fake.invocations {
 		copiedInvocations[key] = value
 	}
 	return copiedInvocations
 }
 
-func (fake *FakeImageIndex) recordInvocation(key string, args []any) {
+func (fake *FakeImageIndex) recordInvocation(key string, args []interface{}) {
 	fake.invocationsMutex.Lock()
 	defer fake.invocationsMutex.Unlock()
 	if fake.invocations == nil {
-		fake.invocations = map[string][][]any{}
+		fake.invocations = map[string][][]interface{}{}
 	}
 	if fake.invocations[key] == nil {
-		fake.invocations[key] = [][]any{}
+		fake.invocations[key] = [][]interface{}{}
 	}
 	fake.invocations[key] = append(fake.invocations[key], args)
 }
diff --git a/pkg/v1/hash.go b/pkg/v1/hash.go
index e9630087..f78a5fa8 100644
--- a/pkg/v1/hash.go
+++ b/pkg/v1/hash.go
@@ -15,7 +15,7 @@
 package v1
 
 import (
-	"crypto/sha256"
+	"crypto"
 	"encoding/hex"
 	"encoding/json"
 	"fmt"
@@ -78,7 +78,7 @@ func (h *Hash) UnmarshalText(text []byte) error {
 func Hasher(name string) (hash.Hash, error) {
 	switch name {
 	case "sha256":
-		return sha256.New(), nil
+		return crypto.SHA256.New(), nil
 	default:
 		return nil, fmt.Errorf("unsupported hash: %q", name)
 	}
@@ -111,7 +111,7 @@ func (h *Hash) parse(unquoted string) error {
 
 // SHA256 computes the Hash of the provided io.Reader's content.
 func SHA256(r io.Reader) (Hash, int64, error) {
-	hasher := sha256.New()
+	hasher := crypto.SHA256.New()
 	n, err := io.Copy(hasher, r)
 	if err != nil {
 		return Hash{}, 0, err
diff --git a/pkg/v1/manifest.go b/pkg/v1/manifest.go
index 8ded1ee5..22d483f3 100644
--- a/pkg/v1/manifest.go
+++ b/pkg/v1/manifest.go
@@ -28,6 +28,7 @@ type Manifest struct {
 	Config        Descriptor        `json:"config"`
 	Layers        []Descriptor      `json:"layers"`
 	Annotations   map[string]string `json:"annotations,omitempty"`
+	Subject       *Descriptor       `json:"subject,omitempty"`
 }
 
 // IndexManifest represents an OCI image index in a structured way.
@@ -36,17 +37,19 @@ type IndexManifest struct {
 	MediaType     types.MediaType   `json:"mediaType,omitempty"`
 	Manifests     []Descriptor      `json:"manifests"`
 	Annotations   map[string]string `json:"annotations,omitempty"`
+	Subject       *Descriptor       `json:"subject,omitempty"`
 }
 
 // Descriptor holds a reference from the manifest to one of its constituent elements.
 type Descriptor struct {
-	MediaType   types.MediaType   `json:"mediaType"`
-	Size        int64             `json:"size"`
-	Digest      Hash              `json:"digest"`
-	Data        []byte            `json:"data,omitempty"`
-	URLs        []string          `json:"urls,omitempty"`
-	Annotations map[string]string `json:"annotations,omitempty"`
-	Platform    *Platform         `json:"platform,omitempty"`
+	MediaType    types.MediaType   `json:"mediaType"`
+	Size         int64             `json:"size"`
+	Digest       Hash              `json:"digest"`
+	Data         []byte            `json:"data,omitempty"`
+	URLs         []string          `json:"urls,omitempty"`
+	Annotations  map[string]string `json:"annotations,omitempty"`
+	Platform     *Platform         `json:"platform,omitempty"`
+	ArtifactType string            `json:"artifactType,omitempty"`
 }
 
 // ParseManifest parses the io.Reader's contents into a Manifest.
diff --git a/pkg/v1/mutate/image.go b/pkg/v1/mutate/image.go
index 93c230e3..727abe29 100644
--- a/pkg/v1/mutate/image.go
+++ b/pkg/v1/mutate/image.go
@@ -37,6 +37,7 @@ type image struct {
 	configMediaType *types.MediaType
 	diffIDMap       map[v1.Hash]v1.Layer
 	digestMap       map[v1.Hash]v1.Layer
+	subject         *v1.Descriptor
 }
 
 var _ v1.Image = (*image)(nil)
@@ -153,6 +154,7 @@ func (i *image) compute() error {
 			manifest.Annotations[k] = v
 		}
 	}
+	manifest.Subject = i.subject
 
 	i.configFile = configFile
 	i.manifest = manifest
diff --git a/pkg/v1/mutate/index.go b/pkg/v1/mutate/index.go
index 841995f1..ba062f9f 100644
--- a/pkg/v1/mutate/index.go
+++ b/pkg/v1/mutate/index.go
@@ -70,6 +70,7 @@ type index struct {
 	imageMap    map[v1.Hash]v1.Image
 	indexMap    map[v1.Hash]v1.ImageIndex
 	layerMap    map[v1.Hash]v1.Layer
+	subject     *v1.Descriptor
 }
 
 var _ v1.ImageIndex = (*index)(nil)
@@ -142,6 +143,7 @@ func (i *index) compute() error {
 			manifest.Annotations[k] = v
 		}
 	}
+	manifest.Subject = i.subject
 
 	i.manifest = manifest
 	i.computed = true
diff --git a/pkg/v1/mutate/mutate.go b/pkg/v1/mutate/mutate.go
index 022fc818..e4a0e527 100644
--- a/pkg/v1/mutate/mutate.go
+++ b/pkg/v1/mutate/mutate.go
@@ -115,9 +115,33 @@ func Config(base v1.Image, cfg v1.Config) (v1.Image, error) {
 	return ConfigFile(base, cf)
 }
 
-// Annotatable represents a manifest that can carry annotations.
-type Annotatable interface {
-	partial.WithRawManifest
+// Subject mutates the subject on an image or index manifest.
+//
+// The input is expected to be a v1.Image or v1.ImageIndex, and
+// returns the same type. You can type-assert the result like so:
+//
+//	img := Subject(empty.Image, subj).(v1.Image)
+//
+// Or for an index:
+//
+//	idx := Subject(empty.Index, subj).(v1.ImageIndex)
+//
+// If the input is not an Image or ImageIndex, the result will
+// attempt to lazily annotate the raw manifest.
+func Subject(f partial.WithRawManifest, subject v1.Descriptor) partial.WithRawManifest {
+	if img, ok := f.(v1.Image); ok {
+		return &image{
+			base:    img,
+			subject: &subject,
+		}
+	}
+	if idx, ok := f.(v1.ImageIndex); ok {
+		return &index{
+			base:    idx,
+			subject: &subject,
+		}
+	}
+	return arbitraryRawManifest{a: f, subject: &subject}
 }
 
 // Annotations mutates the annotations on an annotatable image or index manifest.
@@ -137,7 +161,7 @@ type Annotatable interface {
 //
 // If the input Annotatable is not an Image or ImageIndex, the result will
 // attempt to lazily annotate the raw manifest.
-func Annotations(f Annotatable, anns map[string]string) Annotatable {
+func Annotations(f partial.WithRawManifest, anns map[string]string) partial.WithRawManifest {
 	if img, ok := f.(v1.Image); ok {
 		return &image{
 			base:        img,
@@ -150,12 +174,13 @@ func Annotations(f Annotatable, anns map[string]string) Annotatable {
 			annotations: anns,
 		}
 	}
-	return arbitraryRawManifest{f, anns}
+	return arbitraryRawManifest{a: f, anns: anns}
 }
 
 type arbitraryRawManifest struct {
-	a    Annotatable
-	anns map[string]string
+	a       partial.WithRawManifest
+	anns    map[string]string
+	subject *v1.Descriptor
 }
 
 func (a arbitraryRawManifest) RawManifest() ([]byte, error) {
@@ -178,6 +203,9 @@ func (a arbitraryRawManifest) RawManifest() ([]byte, error) {
 	} else {
 		m["annotations"] = a.anns
 	}
+	if a.subject != nil {
+		m["subject"] = a.subject
+	}
 	return json.Marshal(m)
 }
 
@@ -437,6 +465,13 @@ func layerTime(layer v1.Layer, t time.Time) (v1.Layer, error) {
 		}
 
 		header.ModTime = t
+
+		//PAX and GNU Format support additional timestamps in the header
+		if header.Format == tar.FormatPAX || header.Format == tar.FormatGNU {
+			header.AccessTime = t
+			header.ChangeTime = t
+		}
+
 		if err := tarWriter.WriteHeader(header); err != nil {
 			return nil, fmt.Errorf("writing tar header: %w", err)
 		}
@@ -500,6 +535,8 @@ func MediaType(img v1.Image, mt types.MediaType) v1.Image {
 }
 
 // ConfigMediaType modifies the MediaType() of the given image's Config.
+//
+// If !mt.IsConfig(), this will be the image's artifactType in any indexes it's a part of.
 func ConfigMediaType(img v1.Image, mt types.MediaType) v1.Image {
 	return &image{
 		base:            img,
diff --git a/pkg/v1/partial/with.go b/pkg/v1/partial/with.go
index 3ad49928..c8b22b3f 100644
--- a/pkg/v1/partial/with.go
+++ b/pkg/v1/partial/with.go
@@ -328,10 +328,28 @@ func Descriptor(d Describable) (*v1.Descriptor, error) {
 	if desc.MediaType, err = d.MediaType(); err != nil {
 		return nil, err
 	}
+	if wat, ok := d.(withArtifactType); ok {
+		if desc.ArtifactType, err = wat.ArtifactType(); err != nil {
+			return nil, err
+		}
+	} else {
+		if wrm, ok := d.(WithRawManifest); ok && desc.MediaType.IsImage() {
+			mf, _ := Manifest(wrm)
+			// Failing to parse as a manifest should just be ignored.
+			// The manifest might not be valid, and that's okay.
+			if mf != nil && !mf.Config.MediaType.IsConfig() {
+				desc.ArtifactType = string(mf.Config.MediaType)
+			}
+		}
+	}
 
 	return &desc, nil
 }
 
+type withArtifactType interface {
+	ArtifactType() (string, error)
+}
+
 type withUncompressedSize interface {
 	UncompressedSize() (int64, error)
 }
@@ -399,3 +417,20 @@ func unwrap(i any) any {
 	}
 	return i
 }
+
+// ArtifactType returns the artifact type for the given manifest.
+//
+// If the manifest reports its own artifact type, that's returned, otherwise
+// the manifest is parsed and, if successful, its config.mediaType is returned.
+func ArtifactType(w WithManifest) (string, error) {
+	if wat, ok := w.(withArtifactType); ok {
+		return wat.ArtifactType()
+	}
+	mf, _ := w.Manifest()
+	// Failing to parse as a manifest should just be ignored.
+	// The manifest might not be valid, and that's okay.
+	if mf != nil && !mf.Config.MediaType.IsConfig() {
+		return string(mf.Config.MediaType), nil
+	}
+	return "", nil
+}
diff --git a/pkg/v1/platform.go b/pkg/v1/platform.go
index 9ee91ee2..59ca4026 100644
--- a/pkg/v1/platform.go
+++ b/pkg/v1/platform.go
@@ -85,6 +85,47 @@ func (p Platform) Equals(o Platform) bool {
 		stringSliceEqualIgnoreOrder(p.Features, o.Features)
 }
 
+// Satisfies returns true if this Platform "satisfies" the given spec Platform.
+//
+// Note that this is different from Equals and that Satisfies is not reflexive.
+//
+// The given spec represents "requirements" such that any missing values in the
+// spec are not compared.
+//
+// For OSFeatures and Features, Satisfies will return true if this Platform's
+// fields contain a superset of the values in the spec's fields (order ignored).
+func (p Platform) Satisfies(spec Platform) bool {
+	return satisfies(spec.OS, p.OS) &&
+		satisfies(spec.Architecture, p.Architecture) &&
+		satisfies(spec.Variant, p.Variant) &&
+		satisfies(spec.OSVersion, p.OSVersion) &&
+		satisfiesList(spec.OSFeatures, p.OSFeatures) &&
+		satisfiesList(spec.Features, p.Features)
+}
+
+func satisfies(want, have string) bool {
+	return want == "" || want == have
+}
+
+func satisfiesList(want, have []string) bool {
+	if len(want) == 0 {
+		return true
+	}
+
+	set := map[string]struct{}{}
+	for _, h := range have {
+		set[h] = struct{}{}
+	}
+
+	for _, w := range want {
+		if _, ok := set[w]; !ok {
+			return false
+		}
+	}
+
+	return true
+}
+
 // stringSliceEqual compares 2 string slices and returns if their contents are identical.
 func stringSliceEqual(a, b []string) bool {
 	if len(a) != len(b) {
diff --git a/pkg/v1/random/image.go b/pkg/v1/random/image.go
index f5474627..33625bd7 100644
--- a/pkg/v1/random/image.go
+++ b/pkg/v1/random/image.go
@@ -17,8 +17,8 @@ package random
 import (
 	"archive/tar"
 	"bytes"
+	"crypto"
 	"crypto/rand"
-	"crypto/sha256"
 	"encoding/hex"
 	"fmt"
 	"io"
@@ -84,7 +84,7 @@ func Layer(byteSize int64, mt types.MediaType) (v1.Layer, error) {
 
 	// Hash the contents as we write it out to the buffer.
 	var b bytes.Buffer
-	hasher := sha256.New()
+	hasher := crypto.SHA256.New()
 	mw := io.MultiWriter(&b, hasher)
 
 	// Write a single file with a random name and random contents.
diff --git a/pkg/v1/remote/delete.go b/pkg/v1/remote/delete.go
index 3b902271..74a06fd2 100644
--- a/pkg/v1/remote/delete.go
+++ b/pkg/v1/remote/delete.go
@@ -54,4 +54,8 @@ func Delete(ref name.Reference, options ...Option) error {
 	defer resp.Body.Close()
 
 	return transport.CheckError(resp, http.StatusOK, http.StatusAccepted)
+
+	// TODO(jason): If the manifest had a `subject`, and if the registry
+	// doesn't support Referrers, update the index pointed to by the
+	// subject's fallback tag to remove the descriptor for this manifest.
 }
diff --git a/pkg/v1/remote/descriptor.go b/pkg/v1/remote/descriptor.go
index 938005ca..78919d7a 100644
--- a/pkg/v1/remote/descriptor.go
+++ b/pkg/v1/remote/descriptor.go
@@ -17,6 +17,8 @@ package remote
 import (
 	"bytes"
 	"context"
+	"encoding/json"
+	"errors"
 	"fmt"
 	"io"
 	"net/http"
@@ -59,7 +61,7 @@ type Descriptor struct {
 	v1.Descriptor
 	Manifest []byte
 
-	// So we can share this implementation with Image..
+	// So we can share this implementation with Image.
 	platform v1.Platform
 }
 
@@ -209,28 +211,6 @@ func (d *Descriptor) remoteIndex() *remoteIndex {
 	}
 }
 
-// https://github.com/docker/hub-feedback/issues/2107#issuecomment-1371293316
-//
-// DockerHub supports plugins, which look like normal manifests, but will
-// return a 401 with an incorrect challenge if you attempt to fetch them.
-//
-// They require you send, e.g.:
-// 'repository(plugin):vieux/sshfs:pull' not 'repository:vieux/sshfs:pull'.
-//
-// Hack around this by always including the plugin-ified version in the initial
-// scopes. The request will succeed with the correct subset, so it is safe to
-// have extraneous scopes here.
-func fixPluginScopes(ref name.Reference, scopes []string) []string {
-	if ref.Context().Registry.String() == name.DefaultRegistry {
-		for _, scope := range scopes {
-			if strings.HasPrefix(scope, "repository") {
-				scopes = append(scopes, strings.Replace(scope, "repository", "repository(plugin)", 1))
-			}
-		}
-	}
-	return scopes
-}
-
 // fetcher implements methods for reading from a registry.
 type fetcher struct {
 	Ref     name.Reference
@@ -239,10 +219,7 @@ type fetcher struct {
 }
 
 func makeFetcher(ref name.Reference, o *options) (*fetcher, error) {
-	scopes := []string{ref.Scope(transport.PullScope)}
-	scopes = fixPluginScopes(ref, scopes)
-
-	tr, err := transport.NewWithContext(o.context, ref.Context().Registry, o.auth, o.transport, scopes)
+	tr, err := transport.NewWithContext(o.context, ref.Context().Registry, o.auth, o.transport, []string{ref.Scope(transport.PullScope)})
 	if err != nil {
 		return nil, err
 	}
@@ -262,6 +239,56 @@ func (f *fetcher) url(resource, identifier string) url.URL {
 	}
 }
 
+// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#referrers-tag-schema
+func fallbackTag(d name.Digest) name.Tag {
+	return d.Context().Tag(strings.Replace(d.DigestStr(), ":", "-", 1))
+}
+
+func (f *fetcher) fetchReferrers(ctx context.Context, filter map[string]string, d name.Digest) (*v1.IndexManifest, error) {
+	// Check the Referrers API endpoint first.
+	u := f.url("referrers", d.DigestStr())
+	req, err := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+	req.Header.Set("Accept", string(types.OCIImageIndex))
+
+	resp, err := f.Client.Do(req)
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+
+	if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound, http.StatusBadRequest); err != nil {
+		return nil, err
+	}
+	if resp.StatusCode == http.StatusOK {
+		var im v1.IndexManifest
+		if err := json.NewDecoder(resp.Body).Decode(&im); err != nil {
+			return nil, err
+		}
+		return filterReferrersResponse(filter, &im), nil
+	}
+
+	// The registry doesn't support the Referrers API endpoint, so we'll use the fallback tag scheme.
+	b, _, err := f.fetchManifest(fallbackTag(d), []types.MediaType{types.OCIImageIndex})
+	if err != nil {
+		return nil, err
+	}
+	var terr *transport.Error
+	if ok := errors.As(err, &terr); ok && terr.StatusCode == http.StatusNotFound {
+		// Not found just means there are no attachments yet. Start with an empty manifest.
+		return &v1.IndexManifest{MediaType: types.OCIImageIndex}, nil
+	}
+
+	var im v1.IndexManifest
+	if err := json.Unmarshal(b, &im); err != nil {
+		return nil, err
+	}
+
+	return filterReferrersResponse(filter, &im), nil
+}
+
 func (f *fetcher) fetchManifest(ref name.Reference, acceptable []types.MediaType) ([]byte, *v1.Descriptor, error) {
 	u := f.url("manifests", ref.Identifier())
 	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
@@ -308,6 +335,15 @@ func (f *fetcher) fetchManifest(ref name.Reference, acceptable []types.MediaType
 			return nil, nil, fmt.Errorf("manifest digest: %q does not match requested digest: %q for %q", digest, dgst.DigestStr(), f.Ref)
 		}
 	}
+
+	var artifactType string
+	mf, _ := v1.ParseManifest(bytes.NewReader(manifest))
+	// Failing to parse as a manifest should just be ignored.
+	// The manifest might not be valid, and that's okay.
+	if mf != nil && !mf.Config.MediaType.IsConfig() {
+		artifactType = string(mf.Config.MediaType)
+	}
+
 	// Do nothing for tags; I give up.
 	//
 	// We'd like to validate that the "Docker-Content-Digest" header matches what is returned by the registry,
@@ -318,9 +354,10 @@ func (f *fetcher) fetchManifest(ref name.Reference, acceptable []types.MediaType
 
 	// Return all this info since we have to calculate it anyway.
 	desc := v1.Descriptor{
-		Digest:    digest,
-		Size:      size,
-		MediaType: mediaType,
+		Digest:       digest,
+		Size:         size,
+		MediaType:    mediaType,
+		ArtifactType: artifactType,
 	}
 
 	return manifest, &desc, nil
@@ -453,3 +490,22 @@ func (f *fetcher) blobExists(h v1.Hash) (bool, error) {
 
 	return resp.StatusCode == http.StatusOK, nil
 }
+
+// If filter applied, filter out by artifactType.
+// See https://github.com/opencontainers/distribution-spec/blob/main/spec.md#listing-referrers
+func filterReferrersResponse(filter map[string]string, origIndex *v1.IndexManifest) *v1.IndexManifest {
+	newIndex := origIndex
+	if filter == nil {
+		return newIndex
+	}
+	if v, ok := filter["artifactType"]; ok {
+		tmp := []v1.Descriptor{}
+		for _, desc := range newIndex.Manifests {
+			if desc.ArtifactType == v {
+				tmp = append(tmp, desc)
+			}
+		}
+		newIndex.Manifests = tmp
+	}
+	return newIndex
+}
diff --git a/pkg/v1/remote/image.go b/pkg/v1/remote/image.go
index 4e17de76..fde61427 100644
--- a/pkg/v1/remote/image.go
+++ b/pkg/v1/remote/image.go
@@ -46,6 +46,15 @@ type remoteImage struct {
 	descriptor   *v1.Descriptor
 }
 
+func (r *remoteImage) ArtifactType() (string, error) {
+	// kind of a hack, but RawManifest does appropriate locking/memoization
+	// and makes sure r.descriptor is populated.
+	if _, err := r.RawManifest(); err != nil {
+		return "", err
+	}
+	return r.descriptor.ArtifactType, nil
+}
+
 var _ partial.CompressedImageCore = (*remoteImage)(nil)
 
 // Image provides access to a remote image reference.
diff --git a/pkg/v1/remote/index.go b/pkg/v1/remote/index.go
index a4ee74c0..0939947e 100644
--- a/pkg/v1/remote/index.go
+++ b/pkg/v1/remote/index.go
@@ -250,6 +250,16 @@ func (r *remoteIndex) childDescriptor(child v1.Descriptor, platform v1.Platform)
 			return nil, err
 		}
 	}
+
+	if child.MediaType.IsImage() {
+		mf, _ := v1.ParseManifest(bytes.NewReader(manifest))
+		// Failing to parse as a manifest should just be ignored.
+		// The manifest might not be valid, and that's okay.
+		if mf != nil && !mf.Config.MediaType.IsConfig() {
+			child.ArtifactType = string(mf.Config.MediaType)
+		}
+	}
+
 	return &Descriptor{
 		fetcher: fetcher{
 			Ref:     ref,
diff --git a/pkg/v1/remote/options.go b/pkg/v1/remote/options.go
index 91b9005c..54a0af22 100644
--- a/pkg/v1/remote/options.go
+++ b/pkg/v1/remote/options.go
@@ -46,6 +46,7 @@ type options struct {
 	pageSize                       int
 	retryBackoff                   Backoff
 	retryPredicate                 retry.Predicate
+	filter                         map[string]string
 }
 
 var defaultPlatform = v1.Platform{
@@ -303,3 +304,14 @@ func WithRetryPredicate(predicate retry.Predicate) Option {
 		return nil
 	}
 }
+
+// WithFilter sets the filter querystring for HTTP operations.
+func WithFilter(key string, value string) Option {
+	return func(o *options) error {
+		if o.filter == nil {
+			o.filter = map[string]string{}
+		}
+		o.filter[key] = value
+		return nil
+	}
+}
diff --git a/pkg/v1/remote/referrers.go b/pkg/v1/remote/referrers.go
new file mode 100644
index 00000000..b3db863d
--- /dev/null
+++ b/pkg/v1/remote/referrers.go
@@ -0,0 +1,35 @@
+// Copyright 2023 Google LLC All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package remote
+
+import (
+	"github.com/google/go-containerregistry/pkg/name"
+	v1 "github.com/google/go-containerregistry/pkg/v1"
+)
+
+// Referrers returns a list of descriptors that refer to the given manifest digest.
+//
+// The subject manifest doesn't have to exist in the registry for there to be descriptors that refer to it.
+func Referrers(d name.Digest, options ...Option) (*v1.IndexManifest, error) {
+	o, err := makeOptions(d.Context(), options...)
+	if err != nil {
+		return nil, err
+	}
+	f, err := makeFetcher(d, o)
+	if err != nil {
+		return nil, err
+	}
+	return f.fetchReferrers(o.context, o.filter, d)
+}
diff --git a/pkg/v1/remote/write.go b/pkg/v1/remote/write.go
index 99e9fe83..5dbaa7c2 100644
--- a/pkg/v1/remote/write.go
+++ b/pkg/v1/remote/write.go
@@ -17,11 +17,13 @@ package remote
 import (
 	"bytes"
 	"context"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
 	"net/http"
 	"net/url"
+	"sort"
 	"strings"
 
 	"github.com/google/go-containerregistry/internal/redact"
@@ -577,8 +579,113 @@ func unpackTaggable(t Taggable) ([]byte, *v1.Descriptor, error) {
 	}, nil
 }
 
+// commitSubjectReferrers is responsible for updating the fallback tag manifest to track descriptors referring to a subject for registries that don't yet support the Referrers API.
+// TODO: use conditional requests to avoid race conditions
+func (w *writer) commitSubjectReferrers(ctx context.Context, sub name.Digest, add v1.Descriptor) error {
+	// Check if the registry supports Referrers API.
+	// TODO: This should be done once per registry, not once per subject.
+	u := w.url(fmt.Sprintf("/v2/%s/referrers/%s", w.repo.RepositoryStr(), sub.DigestStr()))
+	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
+	if err != nil {
+		return err
+	}
+	req.Header.Set("Accept", string(types.OCIImageIndex))
+	resp, err := w.client.Do(req.WithContext(ctx))
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+
+	if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound, http.StatusBadRequest); err != nil {
+		return err
+	}
+	if resp.StatusCode == http.StatusOK {
+		// The registry supports Referrers API. The registry is responsible for updating the referrers list.
+		return nil
+	}
+
+	// The registry doesn't support Referrers API, we need to update the manifest tagged with the fallback tag.
+	// Make the request to GET the current manifest.
+	t := fallbackTag(sub)
+	u = w.url(fmt.Sprintf("/v2/%s/manifests/%s", w.repo.RepositoryStr(), t.Identifier()))
+	req, err = http.NewRequest(http.MethodGet, u.String(), nil)
+	if err != nil {
+		return err
+	}
+	req.Header.Set("Accept", string(types.OCIImageIndex))
+	resp, err = w.client.Do(req.WithContext(ctx))
+	if err != nil {
+		return err
+	}
+	defer resp.Body.Close()
+
+	var im v1.IndexManifest
+	if err := transport.CheckError(resp, http.StatusOK, http.StatusNotFound); err != nil {
+		return err
+	} else if resp.StatusCode == http.StatusNotFound {
+		// Not found just means there are no attachments. Start with an empty index.
+		im = v1.IndexManifest{
+			SchemaVersion: 2,
+			MediaType:     types.OCIImageIndex,
+			Manifests:     []v1.Descriptor{add},
+		}
+	} else {
+		if err := json.NewDecoder(resp.Body).Decode(&im); err != nil {
+			return err
+		}
+		if im.SchemaVersion != 2 {
+			return fmt.Errorf("fallback tag manifest is not a schema version 2: %d", im.SchemaVersion)
+		}
+		if im.MediaType != types.OCIImageIndex {
+			return fmt.Errorf("fallback tag manifest is not an OCI image index: %s", im.MediaType)
+		}
+		for _, desc := range im.Manifests {
+			if desc.Digest == add.Digest {
+				// The digest is already attached, nothing to do.
+				logs.Progress.Printf("fallback tag %s already had referrer", t.Identifier())
+				return nil
+			}
+		}
+		// Append the new descriptor to the index.
+		im.Manifests = append(im.Manifests, add)
+	}
+
+	// Sort the manifests for reproducibility.
+	sort.Slice(im.Manifests, func(i, j int) bool {
+		return im.Manifests[i].Digest.String() < im.Manifests[j].Digest.String()
+	})
+	logs.Progress.Printf("updating fallback tag %s with new referrer", t.Identifier())
+	if err := w.commitManifest(ctx, fallbackTaggable{im}, t); err != nil {
+		return err
+	}
+	return nil
+}
+
+type fallbackTaggable struct {
+	im v1.IndexManifest
+}
+
+func (f fallbackTaggable) RawManifest() ([]byte, error)        { return json.Marshal(f.im) }
+func (f fallbackTaggable) MediaType() (types.MediaType, error) { return types.OCIImageIndex, nil }
+
 // commitManifest does a PUT of the image's manifest.
 func (w *writer) commitManifest(ctx context.Context, t Taggable, ref name.Reference) error {
+	// If the manifest refers to a subject, we need to check whether we need to update the fallback tag manifest.
+	raw, err := t.RawManifest()
+	if err != nil {
+		return err
+	}
+	var mf struct {
+		MediaType types.MediaType `json:"mediaType"`
+		Subject   *v1.Descriptor  `json:"subject,omitempty"`
+		Config    struct {
+			MediaType types.MediaType `json:"mediaType"`
+		} `json:"config"`
+	}
+	if err := json.Unmarshal(raw, &mf); err != nil {
+		return err
+	}
+
 	tryUpload := func() error {
 		ctx := retry.Never(ctx)
 		raw, desc, err := unpackTaggable(t)
@@ -605,6 +712,26 @@ func (w *writer) commitManifest(ctx context.Context, t Taggable, ref name.Refere
 			return err
 		}
 
+		// If the manifest referred to a subject, we may need to update the fallback tag manifest.
+		// TODO: If this fails, we'll retry the whole upload. We should retry just this part.
+		if mf.Subject != nil {
+			h, size, err := v1.SHA256(bytes.NewReader(raw))
+			if err != nil {
+				return err
+			}
+			desc := v1.Descriptor{
+				ArtifactType: string(mf.Config.MediaType),
+				MediaType:    mf.MediaType,
+				Digest:       h,
+				Size:         size,
+			}
+			if err := w.commitSubjectReferrers(ctx,
+				ref.Context().Digest(mf.Subject.Digest.String()),
+				desc); err != nil {
+				return err
+			}
+		}
+
 		// The image was successfully pushed!
 		logs.Progress.Printf("%v: digest: %v size: %d", ref, desc.Digest, desc.Size)
 		w.incrProgress(int64(len(raw)))
diff --git a/pkg/v1/stream/layer.go b/pkg/v1/stream/layer.go
index 9dd3e5ff..d6f2df8f 100644
--- a/pkg/v1/stream/layer.go
+++ b/pkg/v1/stream/layer.go
@@ -18,7 +18,7 @@ package stream
 import (
 	"bufio"
 	"compress/gzip"
-	"crypto/sha256"
+	"crypto"
 	"encoding/hex"
 	"errors"
 	"hash"
@@ -166,8 +166,8 @@ type compressedReader struct {
 func newCompressedReader(l *Layer) (*compressedReader, error) {
 	// Collect digests of compressed and uncompressed stream and size of
 	// compressed stream.
-	h := sha256.New()
-	zh := sha256.New()
+	h := crypto.SHA256.New()
+	zh := crypto.SHA256.New()
 	count := &countWriter{}
 
 	// gzip.Writer writes to the output stream via pipe, a hasher to
diff --git a/pkg/v1/tarball/write.go b/pkg/v1/tarball/write.go
index 7d7a0b3b..e607df16 100644
--- a/pkg/v1/tarball/write.go
+++ b/pkg/v1/tarball/write.go
@@ -69,7 +69,7 @@ func Write(ref name.Reference, img v1.Image, w io.Writer, opts ...WriteOption) e
 	return MultiRefWrite(map[name.Reference]v1.Image{ref: img}, w, opts...)
 }
 
-// MultiWrite writes the contents of each image to the provided reader, in the compressed format.
+// MultiWrite writes the contents of each image to the provided writer, in the compressed format.
 // The contents are written in the following format:
 // One manifest.json file at the top level containing information about several images.
 // One file for each layer, named after the layer's SHA.
@@ -82,7 +82,7 @@ func MultiWrite(tagToImage map[name.Tag]v1.Image, w io.Writer, opts ...WriteOpti
 	return MultiRefWrite(refToImage, w, opts...)
 }
 
-// MultiRefWrite writes the contents of each image to the provided reader, in the compressed format.
+// MultiRefWrite writes the contents of each image to the provided writer, in the compressed format.
 // The contents are written in the following format:
 // One manifest.json file at the top level containing information about several images.
 // One file for each layer, named after the layer's SHA.
@@ -98,12 +98,13 @@ func MultiRefWrite(refToImage map[name.Reference]v1.Image, w io.Writer, opts ...
 		}
 	}
 
-	size, mBytes, err := getSizeAndManifest(refToImage)
+	imageToTags := dedupRefToImage(refToImage)
+	size, mBytes, err := getSizeAndManifest(imageToTags)
 	if err != nil {
 		return sendUpdateReturn(o, err)
 	}
 
-	return writeImagesToTar(refToImage, mBytes, size, w, o)
+	return writeImagesToTar(imageToTags, mBytes, size, w, o)
 }
 
 // sendUpdateReturn return the passed in error message, also sending on update channel, if it exists
@@ -125,11 +126,10 @@ func sendProgressWriterReturn(pw *progressWriter, err error) error {
 }
 
 // writeImagesToTar writes the images to the tarball
-func writeImagesToTar(refToImage map[name.Reference]v1.Image, m []byte, size int64, w io.Writer, o *writeOptions) (err error) {
+func writeImagesToTar(imageToTags map[v1.Image][]string, m []byte, size int64, w io.Writer, o *writeOptions) (err error) {
 	if w == nil {
 		return sendUpdateReturn(o, errors.New("must pass valid writer"))
 	}
-	imageToTags := dedupRefToImage(refToImage)
 
 	tw := w
 	var pw *progressWriter
@@ -219,9 +219,7 @@ func writeImagesToTar(refToImage map[name.Reference]v1.Image, m []byte, size int
 }
 
 // calculateManifest calculates the manifest and optionally the size of the tar file
-func calculateManifest(refToImage map[name.Reference]v1.Image) (m Manifest, err error) {
-	imageToTags := dedupRefToImage(refToImage)
-
+func calculateManifest(imageToTags map[v1.Image][]string) (m Manifest, err error) {
 	if len(imageToTags) == 0 {
 		return nil, errors.New("set of images is empty")
 	}
@@ -290,12 +288,13 @@ func calculateManifest(refToImage map[name.Reference]v1.Image) (m Manifest, err
 
 // CalculateSize calculates the expected complete size of the output tar file
 func CalculateSize(refToImage map[name.Reference]v1.Image) (size int64, err error) {
-	size, _, err = getSizeAndManifest(refToImage)
+	imageToTags := dedupRefToImage(refToImage)
+	size, _, err = getSizeAndManifest(imageToTags)
 	return size, err
 }
 
-func getSizeAndManifest(refToImage map[name.Reference]v1.Image) (int64, []byte, error) {
-	m, err := calculateManifest(refToImage)
+func getSizeAndManifest(imageToTags map[v1.Image][]string) (int64, []byte, error) {
+	m, err := calculateManifest(imageToTags)
 	if err != nil {
 		return 0, nil, fmt.Errorf("unable to calculate manifest: %w", err)
 	}
@@ -304,7 +303,7 @@ func getSizeAndManifest(refToImage map[name.Reference]v1.Image) (int64, []byte,
 		return 0, nil, fmt.Errorf("could not marshall manifest to bytes: %w", err)
 	}
 
-	size, err := calculateTarballSize(refToImage, mBytes)
+	size, err := calculateTarballSize(imageToTags, mBytes)
 	if err != nil {
 		return 0, nil, fmt.Errorf("error calculating tarball size: %w", err)
 	}
@@ -312,9 +311,7 @@ func getSizeAndManifest(refToImage map[name.Reference]v1.Image) (int64, []byte,
 }
 
 // calculateTarballSize calculates the size of the tar file
-func calculateTarballSize(refToImage map[name.Reference]v1.Image, mBytes []byte) (size int64, err error) {
-	imageToTags := dedupRefToImage(refToImage)
-
+func calculateTarballSize(imageToTags map[v1.Image][]string, mBytes []byte) (size int64, err error) {
 	seenLayerDigests := make(map[string]struct{})
 	for img, name := range imageToTags {
 		manifest, err := img.Manifest()
@@ -386,7 +383,8 @@ func writeTarEntry(tf *tar.Writer, path string, r io.Reader, size int64) error {
 // ComputeManifest get the manifest.json that will be written to the tarball
 // for multiple references
 func ComputeManifest(refToImage map[name.Reference]v1.Image) (Manifest, error) {
-	return calculateManifest(refToImage)
+	imageToTags := dedupRefToImage(refToImage)
+	return calculateManifest(imageToTags)
 }
 
 // WriteOption a function option to pass to Write()
diff --git a/pkg/v1/types/types.go b/pkg/v1/types/types.go
index 363e989f..efc6bd6b 100644
--- a/pkg/v1/types/types.go
+++ b/pkg/v1/types/types.go
@@ -71,3 +71,12 @@ func (m MediaType) IsIndex() bool {
 	}
 	return false
 }
+
+// IsConfig returns true if the mediaType represents a config, as opposed to something else, like an image.
+func (m MediaType) IsConfig() bool {
+	switch m {
+	case OCIConfigJSON, DockerConfigJSON:
+		return true
+	}
+	return false
+}
diff --git a/pkg/v1/validate/layer.go b/pkg/v1/validate/layer.go
index fce7d1bd..fdd8f38c 100644
--- a/pkg/v1/validate/layer.go
+++ b/pkg/v1/validate/layer.go
@@ -17,7 +17,7 @@ package validate
 import (
 	"archive/tar"
 	"compress/gzip"
-	"crypto/sha256"
+	"crypto"
 	"encoding/hex"
 	"errors"
 	"fmt"
@@ -104,7 +104,7 @@ func computeLayer(layer v1.Layer) (*computedLayer, error) {
 	}
 
 	// Keep track of compressed digest.
-	digester := sha256.New()
+	digester := crypto.SHA256.New()
 	// Everything read from compressed is written to digester to compute digest.
 	hashCompressed := io.TeeReader(compressed, digester)
 
@@ -132,7 +132,7 @@ func computeLayer(layer v1.Layer) (*computedLayer, error) {
 	if err != nil {
 		return nil, err
 	}
-	diffider := sha256.New()
+	diffider := crypto.SHA256.New()
 	hashUncompressed := io.TeeReader(uncompressed, diffider)
 
 	// Ensure there aren't duplicate file paths.
diff --git a/pkg/v1/zz_deepcopy_generated.go b/pkg/v1/zz_deepcopy_generated.go
index 0cb1586f..a47b7475 100644
--- a/pkg/v1/zz_deepcopy_generated.go
+++ b/pkg/v1/zz_deepcopy_generated.go
@@ -99,6 +99,11 @@ func (in *ConfigFile) DeepCopyInto(out *ConfigFile) {
 	}
 	in.RootFS.DeepCopyInto(&out.RootFS)
 	in.Config.DeepCopyInto(&out.Config)
+	if in.OSFeatures != nil {
+		in, out := &in.OSFeatures, &out.OSFeatures
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
 	return
 }
 
@@ -222,6 +227,11 @@ func (in *IndexManifest) DeepCopyInto(out *IndexManifest) {
 			(*out)[key] = val
 		}
 	}
+	if in.Subject != nil {
+		in, out := &in.Subject, &out.Subject
+		*out = new(Descriptor)
+		(*in).DeepCopyInto(*out)
+	}
 	return
 }
 
@@ -253,6 +263,11 @@ func (in *Manifest) DeepCopyInto(out *Manifest) {
 			(*out)[key] = val
 		}
 	}
+	if in.Subject != nil {
+		in, out := &in.Subject, &out.Subject
+		*out = new(Descriptor)
+		(*in).DeepCopyInto(*out)
+	}
 	return
 }
 


Test patch:
diff --git a/pkg/authn/kubernetes/keychain_test.go b/pkg/authn/kubernetes/keychain_test.go
index 2e14b32a..e0157711 100644
--- a/pkg/authn/kubernetes/keychain_test.go
+++ b/pkg/authn/kubernetes/keychain_test.go
@@ -28,6 +28,7 @@ import (
 	"github.com/google/go-containerregistry/pkg/name"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	fakeclient "k8s.io/client-go/kubernetes/fake"
 )
 
@@ -131,7 +132,7 @@ func TestServiceAccountNotFound(t *testing.T) {
 	testResolve(t, kc, registry(t, "fake.registry.io"), authn.Anonymous)
 }
 
-func TestAttachedServiceAccount(t *testing.T) {
+func TestImagePullSecretAttachedServiceAccount(t *testing.T) {
 	username, password := "foo", "bar"
 	client := fakeclient.NewSimpleClientset(&corev1.ServiceAccount{
 		ObjectMeta: metav1.ObjectMeta{
@@ -160,6 +161,73 @@ func TestAttachedServiceAccount(t *testing.T) {
 		&authn.Basic{Username: username, Password: password})
 }
 
+func TestSecretAttachedServiceAccount(t *testing.T) {
+	username, password := "foo", "bar"
+
+	cases := []struct {
+		name            string
+		createSecret    bool
+		useMountSecrets bool
+		expected        authn.Authenticator
+	}{
+		{
+			name:            "resolved successfully",
+			createSecret:    true,
+			useMountSecrets: true,
+			expected:        &authn.Basic{Username: username, Password: password},
+		},
+		{
+			name:            "missing secret skipped",
+			createSecret:    false,
+			useMountSecrets: true,
+			expected:        &authn.Basic{},
+		},
+		{
+			name:            "skip option",
+			createSecret:    true,
+			useMountSecrets: false,
+			expected:        &authn.Basic{},
+		},
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+
+			objs := []runtime.Object{
+				&corev1.ServiceAccount{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "svcacct",
+						Namespace: "ns",
+					},
+					Secrets: []corev1.ObjectReference{{
+						Name: "secret",
+					}},
+				},
+			}
+			if c.createSecret {
+				objs = append(objs, dockerCfgSecretType.Create(
+					t, "ns", "secret", "fake.registry.io", authn.AuthConfig{
+						Username: username,
+						Password: password,
+					}))
+			}
+			client := fakeclient.NewSimpleClientset(objs...)
+
+			kc, err := New(context.Background(), client, Options{
+				Namespace:          "ns",
+				ServiceAccountName: "svcacct",
+				UseMountSecrets:    c.useMountSecrets,
+			})
+			if err != nil {
+				t.Fatalf("New() = %v", err)
+			}
+
+			testResolve(t, kc, registry(t, "fake.registry.io"), c.expected)
+		})
+	}
+
+}
+
 // Prioritze picking the first secret
 func TestSecretPriority(t *testing.T) {
 	secrets := []corev1.Secret{
diff --git a/pkg/crane/options_test.go b/pkg/crane/options_test.go
new file mode 100644
index 00000000..98d73964
--- /dev/null
+++ b/pkg/crane/options_test.go
@@ -0,0 +1,58 @@
+// Copyright 2023 Google LLC All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package crane
+
+import (
+	"errors"
+	"net/http"
+	"testing"
+
+	"github.com/google/go-containerregistry/pkg/v1/remote"
+)
+
+func TestInsecureOptionTracking(t *testing.T) {
+	want := true
+	opts := GetOptions(Insecure)
+
+	if got := opts.insecure; got != want {
+		t.Errorf("got %t\nwant: %t", got, want)
+	}
+}
+
+func TestTransportSetting(t *testing.T) {
+	opts := GetOptions(WithTransport(remote.DefaultTransport))
+
+	if opts.transport == nil {
+		t.Error("expected crane transport to be set when user passes WithTransport")
+	}
+}
+
+func TestInsecureTransport(t *testing.T) {
+	want := true
+	opts := GetOptions(Insecure)
+	var transport *http.Transport
+	var ok bool
+	if transport, ok = opts.transport.(*http.Transport); !ok {
+		t.Fatal("Unable to successfully assert default transport")
+	}
+
+	if transport.TLSClientConfig == nil {
+		t.Fatal(errors.New("TLSClientConfig was nil and should be set"))
+	}
+
+	if got := transport.TLSClientConfig.InsecureSkipVerify; got != want {
+		t.Errorf("got: %t\nwant: %t", got, want)
+	}
+}
diff --git a/pkg/registry/registry_test.go b/pkg/registry/registry_test.go
index d6d4fba5..0ee492ed 100644
--- a/pkg/registry/registry_test.go
+++ b/pkg/registry/registry_test.go
@@ -15,8 +15,6 @@
 package registry_test
 
 import (
-	"crypto/sha256"
-	"encoding/hex"
 	"fmt"
 	"io"
 	"log"
@@ -27,6 +25,7 @@ import (
 	"testing"
 
 	"github.com/google/go-containerregistry/pkg/registry"
+	v1 "github.com/google/go-containerregistry/pkg/v1"
 )
 
 const (
@@ -47,8 +46,8 @@ const (
 )
 
 func sha256String(s string) string {
-	h := sha256.Sum256([]byte(s))
-	return hex.EncodeToString(h[:])
+	h, _, _ := v1.SHA256(strings.NewReader(s))
+	return h.Hex
 }
 
 func TestCalls(t *testing.T) {
@@ -438,6 +437,53 @@ func TestCalls(t *testing.T) {
 			URL:         "/v2/_catalog?n=1000",
 			Code:        http.StatusOK,
 		},
+		{
+			Description: "fetch references",
+			Method:      "GET",
+			URL:         "/v2/foo/referrers/sha256:" + sha256String("foo"),
+			Code:        http.StatusOK,
+			Manifests: map[string]string{
+				"foo/manifests/image":           "foo",
+				"foo/manifests/points-to-image": "{\"subject\": {\"digest\": \"sha256:" + sha256String("foo") + "\"}}",
+			},
+		},
+		{
+			Description: "fetch references, subject pointing elsewhere",
+			Method:      "GET",
+			URL:         "/v2/foo/referrers/sha256:" + sha256String("foo"),
+			Code:        http.StatusOK,
+			Manifests: map[string]string{
+				"foo/manifests/image":           "foo",
+				"foo/manifests/points-to-image": "{\"subject\": {\"digest\": \"sha256:" + sha256String("nonexistant") + "\"}}",
+			},
+		},
+		{
+			Description: "fetch references, no results",
+			Method:      "GET",
+			URL:         "/v2/foo/referrers/sha256:" + sha256String("foo"),
+			Code:        http.StatusOK,
+			Manifests: map[string]string{
+				"foo/manifests/image": "foo",
+			},
+		},
+		{
+			Description: "fetch references, missing repo",
+			Method:      "GET",
+			URL:         "/v2/does-not-exist/referrers/sha256:" + sha256String("foo"),
+			Code:        http.StatusNotFound,
+		},
+		{
+			Description: "fetch references, bad target (tag vs. digest)",
+			Method:      "GET",
+			URL:         "/v2/foo/referrers/latest",
+			Code:        http.StatusBadRequest,
+		},
+		{
+			Description: "fetch references, bad method",
+			Method:      "POST",
+			URL:         "/v2/foo/referrers/sha256:" + sha256String("foo"),
+			Code:        http.StatusBadRequest,
+		},
 	}
 
 	for _, tc := range tcs {
@@ -445,10 +491,11 @@ func TestCalls(t *testing.T) {
 		var logger *log.Logger
 		testf := func(t *testing.T) {
 
-			r := registry.New()
+			opts := []registry.Option{registry.WithReferrersSupport(true)}
 			if logger != nil {
-				r = registry.New(registry.Logger(logger))
+				opts = append(opts, registry.Logger(logger))
 			}
+			r := registry.New(opts...)
 			s := httptest.NewServer(r)
 			defer s.Close()
 
diff --git a/pkg/v1/mutate/index_test.go b/pkg/v1/mutate/index_test.go
index 04df52f9..1f542d13 100644
--- a/pkg/v1/mutate/index_test.go
+++ b/pkg/v1/mutate/index_test.go
@@ -23,6 +23,7 @@ import (
 	v1 "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/empty"
 	"github.com/google/go-containerregistry/pkg/v1/mutate"
+	"github.com/google/go-containerregistry/pkg/v1/partial"
 	"github.com/google/go-containerregistry/pkg/v1/random"
 	"github.com/google/go-containerregistry/pkg/v1/types"
 	"github.com/google/go-containerregistry/pkg/v1/validate"
@@ -180,3 +181,55 @@ func TestIndexImmutability(t *testing.T) {
 		}
 	})
 }
+
+// TestAppend_ArtifactType tests that appending an image manifest that has a
+// non-standard config.mediaType to an index, results in the image's
+// config.mediaType being hoisted into the descriptor inside the index,
+// as artifactType.
+func TestAppend_ArtifactType(t *testing.T) {
+	for _, c := range []struct {
+		desc, configMediaType, wantArtifactType string
+	}{{
+		desc:             "standard config.mediaType, no artifactType",
+		configMediaType:  string(types.DockerConfigJSON),
+		wantArtifactType: "",
+	}, {
+		desc:             "non-standard config.mediaType, want artifactType",
+		configMediaType:  "application/vnd.custom.something",
+		wantArtifactType: "application/vnd.custom.something",
+	}} {
+		t.Run(c.desc, func(t *testing.T) {
+			img, err := random.Image(1, 1)
+			if err != nil {
+				t.Fatalf("random.Image: %v", err)
+			}
+			img = mutate.ConfigMediaType(img, types.MediaType(c.configMediaType))
+			idx := mutate.AppendManifests(empty.Index, mutate.IndexAddendum{
+				Add: img,
+			})
+			mf, err := idx.IndexManifest()
+			if err != nil {
+				t.Fatalf("IndexManifest: %v", err)
+			}
+			if got := mf.Manifests[0].ArtifactType; got != c.wantArtifactType {
+				t.Errorf("manifest artifactType: got %q, want %q", got, c.wantArtifactType)
+			}
+
+			desc, err := partial.Descriptor(img)
+			if err != nil {
+				t.Fatalf("partial.Descriptor: %v", err)
+			}
+			if got := desc.ArtifactType; got != c.wantArtifactType {
+				t.Errorf("descriptor artifactType: got %q, want %q", got, c.wantArtifactType)
+			}
+
+			gotAT, err := partial.ArtifactType(img)
+			if err != nil {
+				t.Fatalf("partial.ArtifactType: %v", err)
+			}
+			if gotAT != c.wantArtifactType {
+				t.Errorf("partial.ArtifactType: got %q, want %q", gotAT, c.wantArtifactType)
+			}
+		})
+	}
+}
diff --git a/pkg/v1/mutate/mutate_test.go b/pkg/v1/mutate/mutate_test.go
index fb9f271b..c4fdba63 100644
--- a/pkg/v1/mutate/mutate_test.go
+++ b/pkg/v1/mutate/mutate_test.go
@@ -32,6 +32,7 @@ import (
 	"github.com/google/go-containerregistry/pkg/v1/empty"
 	"github.com/google/go-containerregistry/pkg/v1/match"
 	"github.com/google/go-containerregistry/pkg/v1/mutate"
+	"github.com/google/go-containerregistry/pkg/v1/partial"
 	"github.com/google/go-containerregistry/pkg/v1/random"
 	"github.com/google/go-containerregistry/pkg/v1/stream"
 	"github.com/google/go-containerregistry/pkg/v1/tarball"
@@ -279,7 +280,7 @@ func TestAnnotations(t *testing.T) {
 
 	for _, c := range []struct {
 		desc string
-		in   mutate.Annotatable
+		in   partial.WithRawManifest
 		want string
 	}{{
 		desc: "image",
@@ -288,7 +289,7 @@ func TestAnnotations(t *testing.T) {
 	}, {
 		desc: "index",
 		in:   empty.Index,
-		want: `{"schemaVersion":2,"manifests":null,"annotations":{"foo":"bar"}}`,
+		want: `{"schemaVersion":2,"mediaType":"application/vnd.oci.image.index.v1+json","manifests":null,"annotations":{"foo":"bar"}}`,
 	}, {
 		desc: "arbitrary",
 		in:   arbitrary{},
diff --git a/pkg/v1/platform_test.go b/pkg/v1/platform_test.go
index 8b02f639..80c67edb 100644
--- a/pkg/v1/platform_test.go
+++ b/pkg/v1/platform_test.go
@@ -149,3 +149,87 @@ func TestPlatformEquals(t *testing.T) {
 		}
 	}
 }
+
+func TestPlatformSatisfies(t *testing.T) {
+	tests := []struct {
+		have, spec v1.Platform
+		sat        bool
+	}{{
+		v1.Platform{Architecture: "amd64", OS: "linux"},
+		v1.Platform{Architecture: "amd64", OS: "linux"},
+		true,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux"},
+		v1.Platform{Architecture: "arm64", OS: "linux"},
+		false,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux"},
+		v1.Platform{Architecture: "amd64", OS: "darwin"},
+		false,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", OSVersion: "5.0"},
+		v1.Platform{Architecture: "amd64", OS: "linux"},
+		true,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", OSVersion: "5.0"},
+		v1.Platform{Architecture: "amd64", OS: "linux", OSVersion: "3.6"},
+		false,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", Variant: "pios"},
+		v1.Platform{Architecture: "amd64", OS: "linux"},
+		true,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", Variant: "pios"},
+		v1.Platform{Architecture: "amd64", OS: "linux", Variant: "ubuntu"},
+		false,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", Variant: "pios"},
+		v1.Platform{Architecture: "amd64", OS: "linux", Variant: "pios"},
+		true,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux"},
+		v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}},
+		false,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}},
+		v1.Platform{Architecture: "amd64", OS: "linux"},
+		true,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}},
+		v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}},
+		true,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}},
+		v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"ac", "bd"}},
+		false,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}},
+		v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"b", "a"}},
+		true,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux"},
+		v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}},
+		false,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}},
+		v1.Platform{Architecture: "amd64", OS: "linux"},
+		true,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}},
+		v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}},
+		true,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}},
+		v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"ac", "bd"}},
+		false,
+	}, {
+		v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}},
+		v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"b", "a"}},
+		true,
+	}}
+	for i, tt := range tests {
+		if sat := tt.have.Satisfies(tt.spec); sat != tt.sat {
+			t.Errorf("%d: mismatched was %v expected %v; original (-want +got) %s", i, sat, tt.sat, cmp.Diff(tt.have, tt.spec))
+		}
+	}
+}
diff --git a/pkg/v1/remote/referrers_test.go b/pkg/v1/remote/referrers_test.go
new file mode 100644
index 00000000..91f9edcc
--- /dev/null
+++ b/pkg/v1/remote/referrers_test.go
@@ -0,0 +1,183 @@
+// Copyright 2023 Google LLC All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package remote_test
+
+import (
+	"fmt"
+	"net/http/httptest"
+	"net/url"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-containerregistry/pkg/name"
+	"github.com/google/go-containerregistry/pkg/registry"
+	v1 "github.com/google/go-containerregistry/pkg/v1"
+	"github.com/google/go-containerregistry/pkg/v1/mutate"
+	"github.com/google/go-containerregistry/pkg/v1/random"
+	"github.com/google/go-containerregistry/pkg/v1/remote"
+	"github.com/google/go-containerregistry/pkg/v1/types"
+)
+
+func TestReferrers(t *testing.T) {
+	// Run all tests against:
+	//
+	//   (1) A OCI 1.0 registry (without referrers API)
+	//   (2) An OCI 1.1+ registry (with referrers API)
+	//
+	for _, leg := range []struct {
+		server      *httptest.Server
+		tryFallback bool
+	}{
+		{
+			server:      httptest.NewServer(registry.New(registry.WithReferrersSupport(false))),
+			tryFallback: true,
+		},
+		{
+			server:      httptest.NewServer(registry.New(registry.WithReferrersSupport(true))),
+			tryFallback: false,
+		},
+	} {
+		s := leg.server
+		defer s.Close()
+		u, err := url.Parse(s.URL)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		descriptor := func(img v1.Image) v1.Descriptor {
+			d, err := img.Digest()
+			if err != nil {
+				t.Fatal(err)
+			}
+			sz, err := img.Size()
+			if err != nil {
+				t.Fatal(err)
+			}
+			mt, err := img.MediaType()
+			if err != nil {
+				t.Fatal(err)
+			}
+			return v1.Descriptor{
+				Digest:       d,
+				Size:         sz,
+				MediaType:    mt,
+				ArtifactType: "application/testing123",
+			}
+		}
+
+		// Push an image we'll attach things to.
+		// We'll copy from src to dst.
+		rootRef, err := name.ParseReference(fmt.Sprintf("%s/repo:root", u.Host))
+		if err != nil {
+			t.Fatal(err)
+		}
+		rootImg, err := random.Image(10, 10)
+		if err != nil {
+			t.Fatal(err)
+		}
+		rootImg = mutate.ConfigMediaType(rootImg, types.MediaType("application/testing123"))
+		if err := remote.Write(rootRef, rootImg); err != nil {
+			t.Fatal(err)
+		}
+		rootDesc := descriptor(rootImg)
+		t.Logf("root image is %s", rootDesc.Digest)
+
+		// Push an image that refers to the root image as its subject.
+		leafRef, err := name.ParseReference(fmt.Sprintf("%s/repo:leaf", u.Host))
+		if err != nil {
+			t.Fatal(err)
+		}
+		leafImg, err := random.Image(20, 20)
+		if err != nil {
+			t.Fatal(err)
+		}
+		leafImg = mutate.ConfigMediaType(leafImg, types.MediaType("application/testing123"))
+		leafImg = mutate.Subject(leafImg, rootDesc).(v1.Image)
+		if err := remote.Write(leafRef, leafImg); err != nil {
+			t.Fatal(err)
+		}
+		leafDesc := descriptor(leafImg)
+		t.Logf("leaf image is %s", leafDesc.Digest)
+
+		// Get the referrers of the root image, by digest.
+		rootRefDigest := rootRef.Context().Digest(rootDesc.Digest.String())
+		index, err := remote.Referrers(rootRefDigest)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if d := cmp.Diff([]v1.Descriptor{leafDesc}, index.Manifests); d != "" {
+			t.Fatalf("referrers diff (-want,+got): %s", d)
+		}
+
+		if leg.tryFallback {
+			// Get the referrers by querying the root image's fallback tag directly.
+			tag, err := name.ParseReference(fmt.Sprintf("%s/repo:sha256-%s", u.Host, rootDesc.Digest.Hex))
+			if err != nil {
+				t.Fatal(err)
+			}
+			idx, err := remote.Index(tag)
+			if err != nil {
+				t.Fatal(err)
+			}
+			mf, err := idx.IndexManifest()
+			if err != nil {
+				t.Fatal(err)
+			}
+			if d := cmp.Diff(index.Manifests, mf.Manifests); d != "" {
+				t.Fatalf("fallback tag diff (-want,+got): %s", d)
+			}
+		}
+
+		// Push the leaf image again, this time with a different tag.
+		// This shouldn't add another item to the root image's referrers,
+		// because it's the same digest.
+		// Push an image that refers to the root image as its subject.
+		leaf2Ref, err := name.ParseReference(fmt.Sprintf("%s/repo:leaf2", u.Host))
+		if err != nil {
+			t.Fatal(err)
+		}
+		if err := remote.Write(leaf2Ref, leafImg); err != nil {
+			t.Fatal(err)
+		}
+		// Get the referrers of the root image again, which should only have one entry.
+		rootRefDigest = rootRef.Context().Digest(rootDesc.Digest.String())
+		index, err = remote.Referrers(rootRefDigest)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if d := cmp.Diff([]v1.Descriptor{leafDesc}, index.Manifests); d != "" {
+			t.Fatalf("referrers diff after second push (-want,+got): %s", d)
+		}
+
+		// Try applying filters and verify number of manifests and and annotations
+		index, err = remote.Referrers(rootRefDigest,
+			remote.WithFilter("artifactType", "application/testing123"))
+		if err != nil {
+			t.Fatal(err)
+		}
+		if numManifests := len(index.Manifests); numManifests == 0 {
+			t.Fatal("index contained 0 manifests")
+		}
+
+		index, err = remote.Referrers(rootRefDigest,
+			remote.WithFilter("artifactType", "application/testing123BADDDD"))
+		if err != nil {
+			t.Fatal(err)
+		}
+		if numManifests := len(index.Manifests); numManifests != 0 {
+			t.Fatalf("expected index to contain 0 manifests, but had %d", numManifests)
+		}
+	}
+}
diff --git a/pkg/v1/remote/write_test.go b/pkg/v1/remote/write_test.go
index 726b00a5..7235c96a 100644
--- a/pkg/v1/remote/write_test.go
+++ b/pkg/v1/remote/write_test.go
@@ -17,7 +17,7 @@ package remote
 import (
 	"bytes"
 	"context"
-	"crypto/sha256"
+	"crypto"
 	"encoding/hex"
 	"errors"
 	"fmt"
@@ -659,7 +659,7 @@ func TestStreamLayer(t *testing.T) {
 			t.Errorf("URL; got %v, want %v", r.URL.Path, expectedPath)
 		}
 
-		h := sha256.New()
+		h := crypto.SHA256.New()
 		s, err := io.Copy(h, r.Body)
 		if err != nil {
 			t.Errorf("Reading body: %v", err)