diff --git a/.circleci/main.yml b/.circleci/main.yml
index efae6d9458764929ffe183cdae3d153605cd120a..08e176656f6bc7a32e88925228f0e7452d308e52 100644
--- a/.circleci/main.yml
+++ b/.circleci/main.yml
@@ -32,7 +32,6 @@ default_environment: &default_environment
   CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
   CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
   GIT_PAGER: cat
-  IPFS_CHECK_RCMGR_DEFAULTS: 1
 
 executors:
   golang:
@@ -160,7 +159,7 @@ jobs:
         tar xfz go1.19.1.linux-amd64.tar.gz
         echo "export PATH=$(pwd)/go/bin:\$PATH" >> ~/.bashrc
     - run: go version
-    - run: sudo apt install socat net-tools
+    - run: sudo apt install socat net-tools fish
     - checkout
 
     - run:
@@ -225,7 +224,7 @@ jobs:
     docker:
       - image: cimg/go:1.19.1-node
     parallelism: 4
-    resource_class: large
+    resource_class: 2xlarge+
     steps:
       - *make_out_dirs
       - attach_workspace:
@@ -325,6 +324,7 @@ jobs:
             - ~/.cache/go-build/
   ipfs-webui:
     executor: node-browsers
+    resource_class: 2xlarge+
     steps:
       - *make_out_dirs
       - attach_workspace:
diff --git a/.github/config.yml b/.github/config.yml
deleted file mode 100644
index ed26646a0f7cdda6cf10ede2b0b98cac89cf67b0..0000000000000000000000000000000000000000
--- a/.github/config.yml
+++ /dev/null
@@ -1,68 +0,0 @@
-# Configuration for welcome - https://github.com/behaviorbot/welcome
-
-# Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome
-# Comment to be posted to on first time issues
-newIssueWelcomeComment: >
-  Thank you for submitting your first issue to this repository! A maintainer
-  will be here shortly to triage and review.
-
-  In the meantime, please double-check that you have provided all the
-  necessary information to make this process easy! Any information that can
-  help save additional round trips is useful! We currently aim to give
-  initial feedback within **two business days**. If this does not happen, feel
-  free to leave a comment.
-
-  Please keep an eye on how this issue will be labeled, as labels give an
-  overview of priorities, assignments and additional actions requested by the
-  maintainers:
-
-    - "Priority" labels will show how urgent this is for the team.
-    - "Status" labels will show if this is ready to be worked on, blocked, or in progress.
-    - "Need" labels will indicate if additional input or analysis is required.
-
-  Finally, remember to use https://discuss.ipfs.io if you just need general
-  support.
-
-# Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome
-# Comment to be posted to on PRs from first time contributors in your repository
-newPRWelcomeComment: >
-  Thank you for submitting this PR!
-
-  A maintainer will be here shortly to review it.
-
-  We are super grateful, but we are also overloaded! Help us by making sure
-  that:
-
-    * The context for this PR is clear, with relevant discussion, decisions
-      and stakeholders linked/mentioned.
-
-    * Your contribution itself is clear (code comments, self-review for the
-      rest) and in its best form. Follow the [code contribution
-      guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md#code-contribution-guidelines)
-      if they apply.
-
-  Getting other community members to do a review would be great help too on
-  complex PRs (you can ask in the chats/forums). If you are unsure about
-  something, just leave us a comment.
-
-  Next steps:
-
-    * A maintainer will triage and assign priority to this PR, commenting on
-      any missing things and potentially assigning a reviewer for high
-      priority items.
-
-    * The PR gets reviews, discussed and approvals as needed.
-
-    * The PR is merged by maintainers when it has been approved and comments addressed.
-
-  We currently aim to provide initial feedback/triaging within **two business
-  days**. Please keep an eye on any labelling actions, as these will indicate
-  priorities and status of your contribution.
-
-  We are very grateful for your contribution!
-
-
-# Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge
-# Comment to be posted to on pull requests merged by a first time user
-# Currently disabled
-#firstPRMergeComment: ""
diff --git a/.golangci.yml b/.golangci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..de174afc44c118f71c8ef71f4a8474d3560eaf64
--- /dev/null
+++ b/.golangci.yml
@@ -0,0 +1,3 @@
+linters:
+  enable:
+    - stylecheck
diff --git a/README.md b/README.md
index 43a1fd47bdfe19ed0f7d1c9cfdf015581f9f9d2d..4cb4c49dac86f39d7b7cd5dc150b96e3cec292e4 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@
 
 ## What is Kubo?
 
-Kubo was the first is the most widely used IPFS implementation today. Implementing the *Interplanetary Filesystem* - the Web3 standard and contender to replace https. Thus powered by IPLD's data models and the libp2p for network communication. Kubo is written in Go.
+Kubo was the first IPFS implementation and is the most widely used one today. Implementing the *Interplanetary Filesystem* - the Web3 standard and contender to replace https. Thus powered by IPLD's data models and the libp2p for network communication. Kubo is written in Go.
 
 Featureset
 - Runs an IPFS-Node as a network service
@@ -274,11 +274,11 @@ PS> choco install go-ipfs
 
 #### Scoop
 
-Scoop provides kubo as `go-ipfs` in its 'extras' bucket.
+Scoop provides kubo as `kubo` in its 'extras' bucket.
 
 ```Powershell
 PS> scoop bucket add extras
-PS> scoop install go-ipfs
+PS> scoop install kubo
 ```
 
 ### Unofficial macOS packages
diff --git a/cmd/ipfs/add_migrations.go b/cmd/ipfs/add_migrations.go
index 95e0d10dab13cd357fcf21e1a200b5ff08e07bf5..6d62b4d6bae6115719d9d4abbb9e1ab3451b83d5 100644
--- a/cmd/ipfs/add_migrations.go
+++ b/cmd/ipfs/add_migrations.go
@@ -56,7 +56,7 @@ func addMigrations(ctx context.Context, node *core.IpfsNode, fetcher migrations.
 				}
 			}
 		default:
-			return errors.New("Cannot get migrations from unknown fetcher type")
+			return errors.New("cannot get migrations from unknown fetcher type")
 		}
 	}
 
@@ -118,9 +118,9 @@ func addMigrationPaths(ctx context.Context, node *core.IpfsNode, peerInfo peer.A
 	fmt.Printf("connected to migration peer %q\n", peerInfo)
 
 	if pin {
-		pinApi := ipfs.Pin()
+		pinAPI := ipfs.Pin()
 		for _, ipfsPath := range paths {
-			err := pinApi.Add(ctx, ipfsPath)
+			err := pinAPI.Add(ctx, ipfsPath)
 			if err != nil {
 				return err
 			}
diff --git a/cmd/ipfs/daemon.go b/cmd/ipfs/daemon.go
index 34762e74798edd19bf29265c5abff7e4f99247bc..1bfb9d6f113767fcff374a1cc7dd5f555932f30d 100644
--- a/cmd/ipfs/daemon.go
+++ b/cmd/ipfs/daemon.go
@@ -62,7 +62,7 @@ const (
 	routingOptionCustomKwd    = "custom"
 	routingOptionDefaultKwd   = "default"
 	unencryptTransportKwd     = "disable-transport-encryption"
-	unrestrictedApiAccessKwd  = "unrestricted-api"
+	unrestrictedAPIAccessKwd  = "unrestricted-api"
 	writableKwd               = "writable"
 	enablePubSubKwd           = "enable-pubsub-experiment"
 	enableIPNSPubSubKwd       = "enable-namesys-pubsub"
@@ -174,7 +174,7 @@ Headers.
 		cmds.BoolOption(writableKwd, "Enable writing objects (with POST, PUT and DELETE)"),
 		cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount). Defaults to config setting."),
 		cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount). Defaults to config setting."),
-		cmds.BoolOption(unrestrictedApiAccessKwd, "Allow API access to unlisted hashes"),
+		cmds.BoolOption(unrestrictedAPIAccessKwd, "Allow API access to unlisted hashes"),
 		cmds.BoolOption(unencryptTransportKwd, "Disable transport encryption (for debugging protocols)"),
 		cmds.BoolOption(enableGCKwd, "Enable automatic periodic repo garbage collection"),
 		cmds.BoolOption(adjustFDLimitKwd, "Check and raise file descriptor limits if needed").WithDefault(true),
@@ -654,7 +654,7 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error
 	// because this would open up the api to scripting vulnerabilities.
 	// only the webui objects are allowed.
 	// if you know what you're doing, go ahead and pass --unrestricted-api.
-	unrestricted, _ := req.Options[unrestrictedApiAccessKwd].(bool)
+	unrestricted, _ := req.Options[unrestrictedAPIAccessKwd].(bool)
 	gatewayOpt := corehttp.GatewayOption(false, corehttp.WebUIPaths...)
 	if unrestricted {
 		gatewayOpt = corehttp.GatewayOption(true, "/ipfs", "/ipns")
diff --git a/cmd/ipfs/init.go b/cmd/ipfs/init.go
index b312fcb49112cc982c190ed8ee21a7df0898f89a..745ceb3e23dff96dbc91ea6d63ae0a6ef91835fc 100644
--- a/cmd/ipfs/init.go
+++ b/cmd/ipfs/init.go
@@ -32,8 +32,9 @@ const (
 	profileOptionName   = "profile"
 )
 
+// nolint
 var errRepoExists = errors.New(`ipfs configuration file already exists!
-Reinitializing would overwrite your keys.
+Reinitializing would overwrite your keys
 `)
 
 var initCmd = &cmds.Command{
diff --git a/cmd/ipfs/ipfs.go b/cmd/ipfs/ipfs.go
index 6b30a0fc8aae7af33ec5c9ed56123e8456079147..24aea66c7860c98e4b9aa3b05a0cc91a324e26ec 100644
--- a/cmd/ipfs/ipfs.go
+++ b/cmd/ipfs/ipfs.go
@@ -6,7 +6,7 @@ import (
 	cmds "github.com/ipfs/go-ipfs-cmds"
 )
 
-// This is the CLI root, used for executing commands accessible to CLI clients.
+// Root is the CLI root, used for executing commands accessible to CLI clients.
 // Some subcommands (like 'ipfs daemon' or 'ipfs init') are only accessible here,
 // and can't be called through the HTTP API.
 var Root = &cmds.Command{
diff --git a/cmd/ipfs/pinmfs_test.go b/cmd/ipfs/pinmfs_test.go
index 1f0c3636ac97b408c1a8475999be04a81d02e5ec..78c7a863bd79d18bf60fdb3c3b2cab13335cb1c3 100644
--- a/cmd/ipfs/pinmfs_test.go
+++ b/cmd/ipfs/pinmfs_test.go
@@ -98,7 +98,7 @@ func TestPinMFSRootNodeError(t *testing.T) {
 }
 
 func TestPinMFSService(t *testing.T) {
-	cfg_invalid_interval := &config.Config{
+	cfgInvalidInterval := &config.Config{
 		Pinning: config.Pinning{
 			RemoteServices: map[string]config.RemotePinningService{
 				"disabled": {
@@ -119,7 +119,7 @@ func TestPinMFSService(t *testing.T) {
 			},
 		},
 	}
-	cfg_valid_unnamed := &config.Config{
+	cfgValidUnnamed := &config.Config{
 		Pinning: config.Pinning{
 			RemoteServices: map[string]config.RemotePinningService{
 				"valid_unnamed": {
@@ -134,7 +134,7 @@ func TestPinMFSService(t *testing.T) {
 			},
 		},
 	}
-	cfg_valid_named := &config.Config{
+	cfgValidNamed := &config.Config{
 		Pinning: config.Pinning{
 			RemoteServices: map[string]config.RemotePinningService{
 				"valid_named": {
@@ -149,9 +149,9 @@ func TestPinMFSService(t *testing.T) {
 			},
 		},
 	}
-	testPinMFSServiceWithError(t, cfg_invalid_interval, "remote pinning service \"invalid_interval\" has invalid MFS.RepinInterval")
-	testPinMFSServiceWithError(t, cfg_valid_unnamed, "error while listing remote pins: empty response from remote pinning service")
-	testPinMFSServiceWithError(t, cfg_valid_named, "error while listing remote pins: empty response from remote pinning service")
+	testPinMFSServiceWithError(t, cfgInvalidInterval, "remote pinning service \"invalid_interval\" has invalid MFS.RepinInterval")
+	testPinMFSServiceWithError(t, cfgValidUnnamed, "error while listing remote pins: empty response from remote pinning service")
+	testPinMFSServiceWithError(t, cfgValidNamed, "error while listing remote pins: empty response from remote pinning service")
 }
 
 func testPinMFSServiceWithError(t *testing.T, cfg *config.Config, expectedErrorPrefix string) {
diff --git a/config/experiments.go b/config/experiments.go
index a576a5b618a8173f688d80688119d93a7c89bd97..7ad87c85303bbef63fa5da59609373e83b9d30c8 100644
--- a/config/experiments.go
+++ b/config/experiments.go
@@ -6,7 +6,7 @@ type Experiments struct {
 	ShardingEnabled      bool `json:",omitempty"` // deprecated by autosharding: https://github.com/ipfs/kubo/pull/8527
 	GraphsyncEnabled     bool
 	Libp2pStreamMounting bool
-	P2pHttpProxy         bool
+	P2pHttpProxy         bool //nolint
 	StrategicProviding   bool
 	AcceleratedDHTClient bool
 }
diff --git a/config/gateway.go b/config/gateway.go
index 71b57dca6799435e1a2d513d5a4ea1037696830b..8b8c65d1db516e064eafe0f705a28b369de58ccc 100644
--- a/config/gateway.go
+++ b/config/gateway.go
@@ -1,5 +1,7 @@
 package config
 
+const DefaultInlineDNSLink = false
+
 type GatewaySpec struct {
 	// Paths is explicit list of path prefixes that should be handled by
 	// this gateway. Example: `["/ipfs", "/ipns", "/api"]`
@@ -18,6 +20,11 @@ type GatewaySpec struct {
 	// NoDNSLink configures this gateway to _not_ resolve DNSLink for the FQDN
 	// provided in `Host` HTTP header.
 	NoDNSLink bool
+
+	// InlineDNSLink configures this gateway to always inline DNSLink names
+	// (FQDN) into a single DNS label in order to interop with wildcard TLS certs
+	// and Origin per CID isolation provided by rules like https://publicsuffix.org
+	InlineDNSLink Flag
 }
 
 // Gateway contains options for the HTTP gateway server.
diff --git a/config/init.go b/config/init.go
index cc2351f1f4e35f652ff203ed776e8a037a8e7457..f86317369f5579b53c0909507ef5a732fa4cd222 100644
--- a/config/init.go
+++ b/config/init.go
@@ -79,14 +79,6 @@ func InitWithIdentity(identity Identity) (*Config, error) {
 			Interval: "12h",
 			Strategy: "all",
 		},
-		Swarm: SwarmConfig{
-			ConnMgr: ConnMgr{
-				LowWater:    DefaultConnMgrLowWater,
-				HighWater:   DefaultConnMgrHighWater,
-				GracePeriod: DefaultConnMgrGracePeriod.String(),
-				Type:        "basic",
-			},
-		},
 		Pinning: Pinning{
 			RemoteServices: map[string]RemotePinningService{},
 		},
@@ -114,6 +106,10 @@ const DefaultConnMgrLowWater = 600
 // grace period
 const DefaultConnMgrGracePeriod = time.Second * 20
 
+// DefaultConnMgrType is the default value for the connection managers
+// type.
+const DefaultConnMgrType = "basic"
+
 func addressesConfig() Addresses {
 	return Addresses{
 		Swarm: []string{
diff --git a/config/internal.go b/config/internal.go
index dcd834e701c3d14856b7f276b1c0e7ecf6b07333..a860c02c9ccac6e435c2b70a9bd92198d7d30f52 100644
--- a/config/internal.go
+++ b/config/internal.go
@@ -12,4 +12,5 @@ type InternalBitswap struct {
 	EngineBlockstoreWorkerCount OptionalInteger
 	EngineTaskWorkerCount       OptionalInteger
 	MaxOutstandingBytesPerPeer  OptionalInteger
+	ProviderSearchDelay         OptionalDuration
 }
diff --git a/config/profile.go b/config/profile.go
index cbc7c9764532a1050fc55956ffe226e233b12457..6748b5fb2b73b9549f311150fa9001b27124692d 100644
--- a/config/profile.go
+++ b/config/profile.go
@@ -178,9 +178,13 @@ fetching may be degraded.
 			c.AutoNAT.ServiceMode = AutoNATServiceDisabled
 			c.Reprovider.Interval = "0"
 
-			c.Swarm.ConnMgr.LowWater = 20
-			c.Swarm.ConnMgr.HighWater = 40
-			c.Swarm.ConnMgr.GracePeriod = time.Minute.String()
+			lowWater := int64(20)
+			highWater := int64(40)
+			gracePeriod := time.Minute
+			c.Swarm.ConnMgr.Type = NewOptionalString("basic")
+			c.Swarm.ConnMgr.LowWater = &OptionalInteger{value: &lowWater}
+			c.Swarm.ConnMgr.HighWater = &OptionalInteger{value: &highWater}
+			c.Swarm.ConnMgr.GracePeriod = &OptionalDuration{&gracePeriod}
 			return nil
 		},
 	},
diff --git a/config/swarm.go b/config/swarm.go
index 01181f36ee54808c433ace45ecbc2faf3b29d8fd..d8fd17e946d85cb3288afd2ce201a259e3d37dba 100644
--- a/config/swarm.go
+++ b/config/swarm.go
@@ -131,10 +131,10 @@ type Transports struct {
 
 // ConnMgr defines configuration options for the libp2p connection manager
 type ConnMgr struct {
-	Type        string
-	LowWater    int
-	HighWater   int
-	GracePeriod string
+	Type        *OptionalString   `json:",omitempty"`
+	LowWater    *OptionalInteger  `json:",omitempty"`
+	HighWater   *OptionalInteger  `json:",omitempty"`
+	GracePeriod *OptionalDuration `json:",omitempty"`
 }
 
 // ResourceMgr defines configuration options for the libp2p Network Resource Manager
@@ -143,6 +143,10 @@ type ResourceMgr struct {
 	// Enables the Network Resource Manager feature, default to on.
 	Enabled Flag               `json:",omitempty"`
 	Limits  *rcmgr.LimitConfig `json:",omitempty"`
+
+	MaxMemory          *OptionalString  `json:",omitempty"`
+	MaxFileDescriptors *OptionalInteger `json:",omitempty"`
+
 	// A list of multiaddrs that can bypass normal system limits (but are still
 	// limited by the allowlist scope). Convenience config around
 	// https://pkg.go.dev/github.com/libp2p/go-libp2p/p2p/host/resource-manager#Allowlist.Add
diff --git a/core/commands/cid.go b/core/commands/cid.go
index 0cf0e9dc05daa60d568e984b89944e37047b2261..a6406a3e0f532ab20fbfcc2efced994b6f30f832 100644
--- a/core/commands/cid.go
+++ b/core/commands/cid.go
@@ -13,7 +13,6 @@ import (
 	verifcid "github.com/ipfs/go-verifcid"
 	ipldmulticodec "github.com/ipld/go-ipld-prime/multicodec"
 	mbase "github.com/multiformats/go-multibase"
-	"github.com/multiformats/go-multicodec"
 	mc "github.com/multiformats/go-multicodec"
 	mhash "github.com/multiformats/go-multihash"
 )
@@ -71,7 +70,7 @@ The optional format string is a printf style format string:
 		opts.fmtStr = fmtStr
 
 		if codecStr != "" {
-			var codec multicodec.Code
+			var codec mc.Code
 			err := codec.Set(codecStr)
 			if err != nil {
 				return err
diff --git a/core/commands/cmdenv/env.go b/core/commands/cmdenv/env.go
index 48cbf2b034b5a336c9a88ab8309ceb494cdbe30d..2c58458907dc79224d589026d318cd8d3ff76957 100644
--- a/core/commands/cmdenv/env.go
+++ b/core/commands/cmdenv/env.go
@@ -27,7 +27,7 @@ func GetNode(env interface{}) (*core.IpfsNode, error) {
 }
 
 // GetApi extracts CoreAPI instance from the environment.
-func GetApi(env cmds.Environment, req *cmds.Request) (coreiface.CoreAPI, error) {
+func GetApi(env cmds.Environment, req *cmds.Request) (coreiface.CoreAPI, error) { //nolint
 	ctx, ok := env.(*commands.Context)
 	if !ok {
 		return nil, fmt.Errorf("expected env to be of type %T, got %T", ctx, env)
diff --git a/core/commands/commands.go b/core/commands/commands.go
index 5f49c8b0da89766a97b9c027fa3c87c302b1e1c2..794e1a5920db06f0a15d6348eb87b51e8490f468 100644
--- a/core/commands/commands.go
+++ b/core/commands/commands.go
@@ -169,6 +169,33 @@ To install the completions permanently, they can be moved to
 					return res.Emit(&buf)
 				},
 			},
+			"fish": {
+				Helptext: cmds.HelpText{
+					Tagline:          "Generate fish shell completions.",
+					ShortDescription: "Generates command completions for the fish shell.",
+					LongDescription: `
+Generates command completions for the fish shell.
+
+The simplest way to see it working is write the completions
+to a file and then source it:
+
+  > ipfs commands completion fish > ipfs-completion.fish
+  > source ./ipfs-completion.fish
+
+To install the completions permanently, they can be moved to
+/etc/fish/completions or ~/.config/fish/completions or sourced from your ~/.config/fish/config.fish file.
+`,
+				},
+				NoRemote: true,
+				Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
+					var buf bytes.Buffer
+					if err := writeFishCompletions(root, &buf); err != nil {
+						return err
+					}
+					res.SetLength(uint64(buf.Len()))
+					return res.Emit(&buf)
+				},
+			},
 		},
 	}
 }
diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go
index 562b17022cecbb1de9acd454240271c41a8d4aa7..ec4cc1bb6dc47244bf25a392dea70968da899d42 100644
--- a/core/commands/commands_test.go
+++ b/core/commands/commands_test.go
@@ -24,6 +24,7 @@ func TestROCommands(t *testing.T) {
 		"/commands",
 		"/commands/completion",
 		"/commands/completion/bash",
+		"/commands/completion/fish",
 		"/dag",
 		"/dag/get",
 		"/dag/resolve",
@@ -99,6 +100,7 @@ func TestCommands(t *testing.T) {
 		"/commands",
 		"/commands/completion",
 		"/commands/completion/bash",
+		"/commands/completion/fish",
 		"/config",
 		"/config/edit",
 		"/config/profile",
@@ -227,6 +229,7 @@ func TestCommands(t *testing.T) {
 		"/repo/stat",
 		"/repo/verify",
 		"/repo/version",
+		"/repo/ls",
 		"/resolve",
 		"/shutdown",
 		"/stats",
diff --git a/core/commands/completion.go b/core/commands/completion.go
index ed1865d2677f1c10172f820f951828f1c638220b..c4fb1ba4142d868f467f18c870bc741a8b4aa123 100644
--- a/core/commands/completion.go
+++ b/core/commands/completion.go
@@ -10,41 +10,62 @@ import (
 
 type completionCommand struct {
 	Name         string
+	FullName     string
+	Description  string
 	Subcommands  []*completionCommand
+	Flags        []*singleOption
+	Options      []*singleOption
 	ShortFlags   []string
 	ShortOptions []string
 	LongFlags    []string
 	LongOptions  []string
+	IsFinal      bool
 }
 
-func commandToCompletions(name string, cmd *cmds.Command) *completionCommand {
+type singleOption struct {
+	LongNames   []string
+	ShortNames  []string
+	Description string
+}
+
+func commandToCompletions(name string, fullName string, cmd *cmds.Command) *completionCommand {
 	parsed := &completionCommand{
-		Name: name,
+		Name:        name,
+		FullName:    fullName,
+		Description: cmd.Helptext.Tagline,
+		IsFinal:     len(cmd.Subcommands) == 0,
 	}
 	for name, subCmd := range cmd.Subcommands {
-		parsed.Subcommands = append(parsed.Subcommands, commandToCompletions(name, subCmd))
+		parsed.Subcommands = append(parsed.Subcommands,
+			commandToCompletions(name, fullName+" "+name, subCmd))
 	}
 	sort.Slice(parsed.Subcommands, func(i, j int) bool {
 		return parsed.Subcommands[i].Name < parsed.Subcommands[j].Name
 	})
 
 	for _, opt := range cmd.Options {
+		flag := &singleOption{Description: opt.Description()}
+		flag.LongNames = append(flag.LongNames, opt.Name())
 		if opt.Type() == cmds.Bool {
 			parsed.LongFlags = append(parsed.LongFlags, opt.Name())
 			for _, name := range opt.Names() {
 				if len(name) == 1 {
 					parsed.ShortFlags = append(parsed.ShortFlags, name)
+					flag.ShortNames = append(flag.ShortNames, name)
 					break
 				}
 			}
+			parsed.Flags = append(parsed.Flags, flag)
 		} else {
 			parsed.LongOptions = append(parsed.LongOptions, opt.Name())
 			for _, name := range opt.Names() {
 				if len(name) == 1 {
 					parsed.ShortOptions = append(parsed.ShortOptions, name)
+					flag.ShortNames = append(flag.ShortNames, name)
 					break
 				}
 			}
+			parsed.Options = append(parsed.Options, flag)
 		}
 	}
 	sort.Slice(parsed.LongFlags, func(i, j int) bool {
@@ -62,7 +83,7 @@ func commandToCompletions(name string, cmd *cmds.Command) *completionCommand {
 	return parsed
 }
 
-var bashCompletionTemplate *template.Template
+var bashCompletionTemplate, fishCompletionTemplate *template.Template
 
 func init() {
 	commandTemplate := template.Must(template.New("command").Parse(`
@@ -133,10 +154,71 @@ _ipfs() {
 }
 complete -o nosort -o nospace -o default -F _ipfs ipfs
 `))
+
+	fishCommandTemplate := template.Must(template.New("command").Parse(`
+{{- if .IsFinal -}}
+complete -c ipfs -n '__fish_ipfs_seen_all_subcommands_from{{ .FullName }}' -F
+{{ end -}}
+{{- range .Flags -}}
+    complete -c ipfs -n '__fish_ipfs_seen_all_subcommands_from{{ $.FullName }}' {{ range .ShortNames }}-s {{.}} {{end}}{{ range .LongNames }}-l {{.}} {{end}}-d "{{ .Description }}"
+{{ end -}}
+{{- range .Options -}}
+    complete -c ipfs -n '__fish_ipfs_seen_all_subcommands_from{{ $.FullName }}' -r {{ range .ShortNames }}-s {{.}} {{end}}{{ range .LongNames }}-l {{.}} {{end}}-d "{{ .Description }}"
+{{ end -}}
+
+{{- range .Subcommands }}
+#{{ .FullName }}
+complete -c ipfs -n '__fish_ipfs_use_subcommand{{ .FullName }}' -a {{ .Name }} -d "{{ .Description }}"
+{{ template "command" . }}
+{{ end -}}
+	`))
+	fishCompletionTemplate = template.Must(fishCommandTemplate.New("root").Parse(`#!/usr/bin/env fish
+function __fish_ipfs_seen_all_subcommands_from
+     set -l cmd (commandline -poc)
+     set -e cmd[1]
+     for c in $argv
+         if not contains -- $c $cmd
+               return 1
+        end
+     end
+     return 0
+end
+
+function __fish_ipfs_use_subcommand
+	set -e argv[-1]
+	set -l cmd (commandline -poc)
+	set -e cmd[1]
+	for i in $cmd
+	    switch $i
+		    case '-*'
+			    continue
+            case $argv[1]
+                set argv $argv[2..]
+                continue
+            case '*'
+                return 1
+        end
+	end
+	test -z "$argv"
+end
+
+complete -c ipfs -l help -d "Show the full command help text."
+
+complete -c ipfs --keep-order --no-files
+
+{{ template "command" . }}
+`))
+
 }
 
 // writeBashCompletions generates a bash completion script for the given command tree.
 func writeBashCompletions(cmd *cmds.Command, out io.Writer) error {
-	cmds := commandToCompletions("ipfs", cmd)
+	cmds := commandToCompletions("ipfs", "", cmd)
 	return bashCompletionTemplate.Execute(out, cmds)
 }
+
+// writeFishCompletions generates a fish completion script for the given command tree.
+func writeFishCompletions(cmd *cmds.Command, out io.Writer) error {
+	cmds := commandToCompletions("ipfs", "", cmd)
+	return fishCompletionTemplate.Execute(out, cmds)
+}
diff --git a/core/commands/dag/dag.go b/core/commands/dag/dag.go
index 77c9ee4b590e8b4a11d98ed5ab235eca267b0784..df7762c0062f8b8b639976ea76d27bde44c75e91 100644
--- a/core/commands/dag/dag.go
+++ b/core/commands/dag/dag.go
@@ -223,7 +223,7 @@ Specification of CAR formats: https://ipld.io/specs/transport/car/
 			// event should have only one of `Root` or `Stats` set, not both
 			if event.Root == nil {
 				if event.Stats == nil {
-					return fmt.Errorf("Unexpected message from DAG import")
+					return fmt.Errorf("unexpected message from DAG import")
 				}
 				stats, _ := req.Options[statsOptionName].(bool)
 				if stats {
@@ -233,7 +233,7 @@ Specification of CAR formats: https://ipld.io/specs/transport/car/
 			}
 
 			if event.Stats != nil {
-				return fmt.Errorf("Unexpected message from DAG import")
+				return fmt.Errorf("unexpected message from DAG import")
 			}
 
 			enc, err := cmdenv.GetLowLevelCidEncoder(req)
diff --git a/core/commands/dag/export.go b/core/commands/dag/export.go
index 14e982bcc03b4a23b54dc44eccb93dee106806af..e9d120e8794f7d5642997d1036b603ca16aad812 100644
--- a/core/commands/dag/export.go
+++ b/core/commands/dag/export.go
@@ -85,7 +85,7 @@ func finishCLIExport(res cmds.Response, re cmds.ResponseEmitter) error {
 	if !specified {
 		// default based on TTY availability
 		errStat, _ := os.Stderr.Stat()
-		if 0 != (errStat.Mode() & os.ModeCharDevice) {
+		if (errStat.Mode() & os.ModeCharDevice) != 0 {
 			showProgress = true
 		}
 	} else if val.(bool) {
diff --git a/core/commands/dag/import.go b/core/commands/dag/import.go
index 35b3e4764d5c45a2aa8b9df10631132e66abc3aa..87daaae119846907d561e7bce2e7a299a14693e5 100644
--- a/core/commands/dag/import.go
+++ b/core/commands/dag/import.go
@@ -8,6 +8,7 @@ import (
 	cid "github.com/ipfs/go-cid"
 	files "github.com/ipfs/go-ipfs-files"
 	ipld "github.com/ipfs/go-ipld-format"
+	ipldlegacy "github.com/ipfs/go-ipld-legacy"
 	iface "github.com/ipfs/interface-go-ipfs-core"
 	"github.com/ipfs/interface-go-ipfs-core/options"
 	"github.com/ipfs/kubo/core/commands/cmdenv"
@@ -90,7 +91,7 @@ func dagImport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment
 
 			if block, err := node.Blockstore.Get(req.Context, c); err != nil {
 				ret.PinErrorMsg = err.Error()
-			} else if nd, err := ipld.Decode(block); err != nil {
+			} else if nd, err := ipldlegacy.DecodeNode(req.Context, block); err != nil {
 				ret.PinErrorMsg = err.Error()
 			} else if err := node.Pinning.Pin(req.Context, nd, true); err != nil {
 				ret.PinErrorMsg = err.Error()
@@ -181,7 +182,7 @@ func importWorker(req *cmds.Request, re cmds.ResponseEmitter, api iface.CoreAPI,
 				}
 
 				// the double-decode is suboptimal, but we need it for batching
-				nd, err := ipld.Decode(block)
+				nd, err := ipldlegacy.DecodeNode(req.Context, block)
 				if err != nil {
 					return err
 				}
diff --git a/core/commands/id.go b/core/commands/id.go
index b3fc076fb4a8f30ce192fd94e32efb3686777cdb..887f93efe311de247b07a4cd733cd623f44170db 100644
--- a/core/commands/id.go
+++ b/core/commands/id.go
@@ -23,9 +23,9 @@ import (
 	identify "github.com/libp2p/go-libp2p/p2p/protocol/identify"
 )
 
-const offlineIdErrorMessage = "'ipfs id' cannot query information on remote peers without a running daemon; if you only want to convert --peerid-base, pass --offline option."
+const offlineIDErrorMessage = "'ipfs id' cannot query information on remote peers without a running daemon; if you only want to convert --peerid-base, pass --offline option"
 
-type IdOutput struct {
+type IdOutput struct { //nolint
 	ID              string
 	PublicKey       string
 	Addresses       []string
@@ -98,7 +98,7 @@ EXAMPLE:
 
 		offline, _ := req.Options[OfflineOption].(bool)
 		if !offline && !n.IsOnline {
-			return errors.New(offlineIdErrorMessage)
+			return errors.New(offlineIDErrorMessage)
 		}
 
 		if !offline {
@@ -107,7 +107,7 @@ EXAMPLE:
 			switch err {
 			case nil:
 			case kb.ErrLookupFailure:
-				return errors.New(offlineIdErrorMessage)
+				return errors.New(offlineIDErrorMessage)
 			default:
 				return err
 			}
diff --git a/core/commands/keystore.go b/core/commands/keystore.go
index e931c38ea2a496ce46df9f7be73bd82a20024694..f530d5eda93bee45994209c3ab4ebdb563bd328e 100644
--- a/core/commands/keystore.go
+++ b/core/commands/keystore.go
@@ -56,7 +56,7 @@ publish'.
 
 type KeyOutput struct {
 	Name string
-	Id   string
+	Id   string //nolint
 }
 
 type KeyOutputList struct {
@@ -67,7 +67,7 @@ type KeyOutputList struct {
 type KeyRenameOutput struct {
 	Was       string
 	Now       string
-	Id        string
+	Id        string //nolint
 	Overwrite bool
 }
 
diff --git a/core/commands/log.go b/core/commands/log.go
index 96366d4c4ee915d14f3580627abdfdd41e700335..d2cb4a1a168f9d693dd935f7859d642cc82776ba 100644
--- a/core/commands/log.go
+++ b/core/commands/log.go
@@ -110,6 +110,8 @@ var logTailCmd = &cmds.Command{
 		Tagline: "Read the event log.",
 		ShortDescription: `
 Outputs event log messages (not other log messages) as they are generated.
+
+Currently broken. Follow https://github.com/ipfs/kubo/issues/9245 for updates.
 `,
 	},
 
diff --git a/core/commands/multibase.go b/core/commands/multibase.go
index be8d613f29cbca9d9e7ee2688f37f0b26fdea7be..d985aebf0c2358c61e793f16f429b3129870e74a 100644
--- a/core/commands/multibase.go
+++ b/core/commands/multibase.go
@@ -105,11 +105,11 @@ This command expects multibase inside of a file or via stdin:
 		if err != nil {
 			return fmt.Errorf("failed to access file: %w", err)
 		}
-		encoded_data, err := io.ReadAll(file)
+		encodedData, err := io.ReadAll(file)
 		if err != nil {
 			return fmt.Errorf("failed to read file contents: %w", err)
 		}
-		_, data, err := mbase.Decode(string(encoded_data))
+		_, data, err := mbase.Decode(string(encodedData))
 		if err != nil {
 			return fmt.Errorf("failed to decode multibase: %w", err)
 		}
@@ -156,11 +156,11 @@ but one can customize used base with -b:
 		if err != nil {
 			return fmt.Errorf("failed to access file: %w", err)
 		}
-		encoded_data, err := io.ReadAll(file)
+		encodedData, err := io.ReadAll(file)
 		if err != nil {
 			return fmt.Errorf("failed to read file contents: %w", err)
 		}
-		_, data, err := mbase.Decode(string(encoded_data))
+		_, data, err := mbase.Decode(string(encodedData))
 		if err != nil {
 			return fmt.Errorf("failed to decode multibase: %w", err)
 		}
diff --git a/core/commands/pin/remotepin.go b/core/commands/pin/remotepin.go
index 49db55147cc32dea33aaa4ea6bedc3176f5c5e89..95b17a315e8cf418ded4e8899d95f225381339cd 100644
--- a/core/commands/pin/remotepin.go
+++ b/core/commands/pin/remotepin.go
@@ -129,7 +129,7 @@ NOTE: a comma-separated notation is supported in CLI for convenience:
 	},
 
 	Arguments: []cmds.Argument{
-		cmds.StringArg("ipfs-path", true, false, "Path to object(s) to be pinned."),
+		cmds.StringArg("ipfs-path", true, false, "CID or Path to be pinned."),
 	},
 	Options: []cmds.Option{
 		pinServiceNameOption,
@@ -214,27 +214,27 @@ NOTE: a comma-separated notation is supported in CLI for convenience:
 
 		// Block unless --background=true is passed
 		if !req.Options[pinBackgroundOptionName].(bool) {
-			requestId := ps.GetRequestId()
+			requestID := ps.GetRequestId()
 			for {
-				ps, err = c.GetStatusByID(ctx, requestId)
+				ps, err = c.GetStatusByID(ctx, requestID)
 				if err != nil {
-					return fmt.Errorf("failed to check pin status for requestid=%q due to error: %v", requestId, err)
+					return fmt.Errorf("failed to check pin status for requestid=%q due to error: %v", requestID, err)
 				}
-				if ps.GetRequestId() != requestId {
-					return fmt.Errorf("failed to check pin status for requestid=%q, remote service sent unexpected requestid=%q", requestId, ps.GetRequestId())
+				if ps.GetRequestId() != requestID {
+					return fmt.Errorf("failed to check pin status for requestid=%q, remote service sent unexpected requestid=%q", requestID, ps.GetRequestId())
 				}
 				s := ps.GetStatus()
 				if s == pinclient.StatusPinned {
 					break
 				}
 				if s == pinclient.StatusFailed {
-					return fmt.Errorf("remote service failed to pin requestid=%q", requestId)
+					return fmt.Errorf("remote service failed to pin requestid=%q", requestID)
 				}
 				tmr := time.NewTimer(time.Second / 2)
 				select {
 				case <-tmr.C:
 				case <-ctx.Done():
-					return fmt.Errorf("waiting for pin interrupted, requestid=%q remains on remote service", requestId)
+					return fmt.Errorf("waiting for pin interrupted, requestid=%q remains on remote service", requestID)
 				}
 			}
 		}
@@ -665,8 +665,8 @@ TIP: pass '--enc=json' for more useful JSON output.
 
 type ServiceDetails struct {
 	Service     string
-	ApiEndpoint string
-	Stat        *Stat `json:",omitempty"` // present only when --stat not passed
+	ApiEndpoint string //nolint
+	Stat        *Stat  `json:",omitempty"` // present only when --stat not passed
 }
 
 type Stat struct {
diff --git a/core/commands/refs.go b/core/commands/refs.go
index ad982aaed0fef8a1f6ba35d780ef67a440fb7487..81c13cbda7aac453706c0d436882478040821cc5 100644
--- a/core/commands/refs.go
+++ b/core/commands/refs.go
@@ -52,7 +52,9 @@ with the following format:
 
   <link base58 hash>
 
-NOTE: List all references recursively by using the flag '-r'.
+List all references recursively by using the flag '-r'.
+
+NOTE: Like most other commands, Kubo will try to fetch the blocks of the passed path if they can't be found in the local store if it is running in online mode.
 `,
 	},
 	Subcommands: map[string]*cmds.Command{
diff --git a/core/commands/repo.go b/core/commands/repo.go
index b1a4c918008bd644381d65fec2d12b893edf2a6f..5c4c7b947ed86132b3b99c738da00880b182729b 100644
--- a/core/commands/repo.go
+++ b/core/commands/repo.go
@@ -43,6 +43,7 @@ var RepoCmd = &cmds.Command{
 		"version": repoVersionCmd,
 		"verify":  repoVerifyCmd,
 		"migrate": repoMigrateCmd,
+		"ls":      RefsLocalCmd,
 	},
 }
 
diff --git a/core/commands/root.go b/core/commands/root.go
index 8a14b1c1a3a9c06b8e10f2ddfd5b8f2d7b659f02..cfa2708e6421153ac609ff1ffe6bfc022dcb851d 100644
--- a/core/commands/root.go
+++ b/core/commands/root.go
@@ -25,7 +25,7 @@ const (
 	DebugOption      = "debug"
 	LocalOption      = "local" // DEPRECATED: use OfflineOption
 	OfflineOption    = "offline"
-	ApiOption        = "api"
+	ApiOption        = "api" //nolint
 )
 
 var Root = &cmds.Command{
@@ -118,7 +118,6 @@ The CLI will exit with one of the following values:
 	},
 }
 
-// commandsDaemonCmd is the "ipfs commands" command for daemon
 var CommandsDaemonCmd = CommandsCmd(Root)
 
 var rootSubcommands = map[string]*cmds.Command{
diff --git a/core/commands/stat.go b/core/commands/stat.go
index 0c4331fd4eced4cff7cb7a5cef3e52bc271a74b7..a09c70ea1c102caf16a4b5f896f0e7b45415c15a 100644
--- a/core/commands/stat.go
+++ b/core/commands/stat.go
@@ -132,8 +132,8 @@ Example:
 					return err
 				}
 			} else if tfound {
-				protoId := protocol.ID(tstr)
-				stats := nd.Reporter.GetBandwidthForProtocol(protoId)
+				protoID := protocol.ID(tstr)
+				stats := nd.Reporter.GetBandwidthForProtocol(protoID)
 				if err := res.Emit(&stats); err != nil {
 					return err
 				}
diff --git a/core/commands/swarm.go b/core/commands/swarm.go
index a070291d0c889c48dcee8f6f3fc6ac9013242465..1508efcb8a8fe3859c3f50fb9e15456e85b50106 100644
--- a/core/commands/swarm.go
+++ b/core/commands/swarm.go
@@ -63,10 +63,12 @@ ipfs peers in the internet.
 }
 
 const (
-	swarmVerboseOptionName   = "verbose"
-	swarmStreamsOptionName   = "streams"
-	swarmLatencyOptionName   = "latency"
-	swarmDirectionOptionName = "direction"
+	swarmVerboseOptionName           = "verbose"
+	swarmStreamsOptionName           = "streams"
+	swarmLatencyOptionName           = "latency"
+	swarmDirectionOptionName         = "direction"
+	swarmResetLimitsOptionName       = "reset"
+	swarmUsedResourcesPercentageName = "min-used-limit-perc"
 )
 
 type peeringResult struct {
@@ -122,6 +124,9 @@ var swarmPeeringAddCmd = &cmds.Command{
 		if err != nil {
 			return err
 		}
+		if !node.IsOnline {
+			return ErrNotOnline
+		}
 
 		for _, addrinfo := range addInfos {
 			node.Peering.AddPeer(addrinfo)
@@ -153,6 +158,10 @@ var swarmPeeringLsCmd = &cmds.Command{
 		if err != nil {
 			return err
 		}
+		if !node.IsOnline {
+			return ErrNotOnline
+		}
+
 		peers := node.Peering.ListPeers()
 		return cmds.EmitOnce(res, addrInfos{Peers: peers})
 	},
@@ -189,6 +198,9 @@ var swarmPeeringRmCmd = &cmds.Command{
 		if err != nil {
 			return err
 		}
+		if !node.IsOnline {
+			return ErrNotOnline
+		}
 
 		for _, arg := range req.Arguments {
 			id, err := peer.Decode(arg)
@@ -206,7 +218,7 @@ var swarmPeeringRmCmd = &cmds.Command{
 	Type: peeringResult{},
 	Encoders: cmds.EncoderMap{
 		cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, pr *peeringResult) error {
-			fmt.Fprintf(w, "add %s %s\n", pr.ID.String(), pr.Status)
+			fmt.Fprintf(w, "remove %s %s\n", pr.ID.String(), pr.Status)
 			return nil
 		}),
 	},
@@ -329,6 +341,9 @@ The output of this command is JSON.
 	Arguments: []cmds.Argument{
 		cmds.StringArg("scope", true, false, "scope of the stat report"),
 	},
+	Options: []cmds.Option{
+		cmds.IntOption(swarmUsedResourcesPercentageName, "Display only resources that are using above the specified percentage"),
+	},
 	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
 		node, err := cmdenv.GetNode(env)
 		if err != nil {
@@ -336,14 +351,16 @@ The output of this command is JSON.
 		}
 
 		if node.ResourceManager == nil {
-			return libp2p.NoResourceMgrError
+			return libp2p.ErrNoResourceMgr
 		}
 
 		if len(req.Arguments) != 1 {
 			return fmt.Errorf("must specify exactly one scope")
 		}
+
+		percentage, _ := req.Options[swarmUsedResourcesPercentageName].(int)
 		scope := req.Arguments[0]
-		result, err := libp2p.NetStat(node.ResourceManager, scope)
+		result, err := libp2p.NetStat(node.ResourceManager, scope, percentage)
 		if err != nil {
 			return err
 		}
@@ -367,6 +384,7 @@ var swarmLimitCmd = &cmds.Command{
 		Tagline: "Get or set resource limits for a scope.",
 		LongDescription: `Get or set resource limits for a scope.
 The scope can be one of the following:
+- all           -- all limits actually being applied.
 - system        -- limits for the system aggregate resource usage.
 - transient     -- limits for the transient resource usage.
 - svc:<service> -- limits for the resource usage of a specific service.
@@ -387,6 +405,9 @@ Changes made via command line are persisted in the Swarm.ResourceMgr.Limits fiel
 		cmds.StringArg("scope", true, false, "scope of the limit"),
 		cmds.FileArg("limit.json", false, false, "limits to be set").EnableStdin(),
 	},
+	Options: []cmds.Option{
+		cmds.BoolOption(swarmResetLimitsOptionName, "reset limit to default"),
+	},
 	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
 		node, err := cmdenv.GetNode(env)
 		if err != nil {
@@ -394,7 +415,7 @@ Changes made via command line are persisted in the Swarm.ResourceMgr.Limits fiel
 		}
 
 		if node.ResourceManager == nil {
-			return libp2p.NoResourceMgrError
+			return libp2p.ErrNoResourceMgr
 		}
 
 		scope := req.Arguments[0]
@@ -408,7 +429,10 @@ Changes made via command line are persisted in the Swarm.ResourceMgr.Limits fiel
 				if file == nil {
 					return errors.New("expected a JSON file")
 				}
-				if err := json.NewDecoder(file).Decode(&newLimit); err != nil {
+
+				r := io.LimitReader(file, 32*1024*1024) // 32MiB
+
+				if err := json.NewDecoder(r).Decode(&newLimit); err != nil {
 					return fmt.Errorf("decoding JSON as ResourceMgrScopeConfig: %w", err)
 				}
 				return libp2p.NetSetLimit(node.ResourceManager, node.Repo, scope, newLimit)
@@ -418,8 +442,17 @@ Changes made via command line are persisted in the Swarm.ResourceMgr.Limits fiel
 			}
 		}
 
-		// get scope limit
-		result, err := libp2p.NetLimit(node.ResourceManager, scope)
+		var result interface{}
+		_, reset := req.Options[swarmResetLimitsOptionName]
+		if reset {
+			result, err = libp2p.NetResetLimit(node.ResourceManager, node.Repo, scope)
+		} else if scope == "all" {
+			result, err = libp2p.NetLimitAll(node.ResourceManager)
+		} else {
+			// get scope limit
+			result, err = libp2p.NetLimit(node.ResourceManager, scope)
+		}
+
 		if err != nil {
 			return err
 		}
@@ -840,7 +873,7 @@ Filters default to those specified under the "Swarm.AddrFilters" config key.
 			return err
 		}
 
-		if n.PeerHost == nil {
+		if !n.IsOnline {
 			return ErrNotOnline
 		}
 
@@ -876,7 +909,7 @@ var swarmFiltersAddCmd = &cmds.Command{
 			return err
 		}
 
-		if n.PeerHost == nil {
+		if !n.IsOnline {
 			return ErrNotOnline
 		}
 
@@ -932,7 +965,7 @@ var swarmFiltersRmCmd = &cmds.Command{
 			return err
 		}
 
-		if n.PeerHost == nil {
+		if !n.IsOnline {
 			return ErrNotOnline
 		}
 
diff --git a/core/core_test.go b/core/core_test.go
index 9a9075ae7266b7b68641ee09b1b42180c23e5295..488eb8421ec1e5cabb78602c62c6f867f2a611e2 100644
--- a/core/core_test.go
+++ b/core/core_test.go
@@ -85,18 +85,18 @@ var errNotSupported = errors.New("method not supported")
 func TestDelegatedRoutingSingle(t *testing.T) {
 	require := require.New(t)
 
-	pId1, priv1, err := GeneratePeerID()
+	pID1, priv1, err := GeneratePeerID()
 	require.NoError(err)
 
-	pId2, _, err := GeneratePeerID()
+	pID2, _, err := GeneratePeerID()
 	require.NoError(err)
 
-	theID := path.Join("/ipns", string(pId1))
-	theErrorID := path.Join("/ipns", string(pId2))
+	theID := path.Join("/ipns", string(pID1))
+	theErrorID := path.Join("/ipns", string(pID2))
 
 	d := &delegatedRoutingService{
-		goodPeerID: pId1,
-		badPeerID:  pId2,
+		goodPeerID: pID1,
+		badPeerID:  pID2,
 		pk1:        priv1,
 	}
 
@@ -122,18 +122,18 @@ func TestDelegatedRoutingSingle(t *testing.T) {
 func TestDelegatedRoutingMulti(t *testing.T) {
 	require := require.New(t)
 
-	pId1, priv1, err := GeneratePeerID()
+	pID1, priv1, err := GeneratePeerID()
 	require.NoError(err)
 
-	pId2, priv2, err := GeneratePeerID()
+	pID2, priv2, err := GeneratePeerID()
 	require.NoError(err)
 
-	theID1 := path.Join("/ipns", string(pId1))
-	theID2 := path.Join("/ipns", string(pId2))
+	theID1 := path.Join("/ipns", string(pID1))
+	theID2 := path.Join("/ipns", string(pID2))
 
 	d1 := &delegatedRoutingService{
-		goodPeerID: pId1,
-		badPeerID:  pId2,
+		goodPeerID: pID1,
+		badPeerID:  pID2,
 		pk1:        priv1,
 		serviceID:  1,
 	}
@@ -141,8 +141,8 @@ func TestDelegatedRoutingMulti(t *testing.T) {
 	url1 := StartRoutingServer(t, d1)
 
 	d2 := &delegatedRoutingService{
-		goodPeerID: pId2,
-		badPeerID:  pId1,
+		goodPeerID: pID2,
+		badPeerID:  pID1,
 		pk1:        priv2,
 		serviceID:  2,
 	}
diff --git a/core/coreapi/coreapi.go b/core/coreapi/coreapi.go
index 515da6930fa632902a63dc409106d5823497fc91..fb549171a6cee0be26aebee21d7fb8f2f6c40ed3 100644
--- a/core/coreapi/coreapi.go
+++ b/core/coreapi/coreapi.go
@@ -158,7 +158,7 @@ func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, e
 
 	n := api.nd
 
-	subApi := &CoreAPI{
+	subAPI := &CoreAPI{
 		nctx: n.Context(),
 
 		identity:   n.Identity,
@@ -190,14 +190,14 @@ func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, e
 		parentOpts: settings,
 	}
 
-	subApi.checkOnline = func(allowOffline bool) error {
+	subAPI.checkOnline = func(allowOffline bool) error {
 		if !n.IsOnline && !allowOffline {
 			return coreiface.ErrOffline
 		}
 		return nil
 	}
 
-	subApi.checkPublishAllowed = func() error {
+	subAPI.checkPublishAllowed = func() error {
 		if n.Mounts.Ipns != nil && n.Mounts.Ipns.IsActive() {
 			return errors.New("cannot manually publish while IPNS is mounted")
 		}
@@ -218,39 +218,39 @@ func (api *CoreAPI) WithOptions(opts ...options.ApiOption) (coreiface.CoreAPI, e
 			return nil, fmt.Errorf("cannot specify negative resolve cache size")
 		}
 
-		subApi.routing = offlineroute.NewOfflineRouter(subApi.repo.Datastore(), subApi.recordValidator)
+		subAPI.routing = offlineroute.NewOfflineRouter(subAPI.repo.Datastore(), subAPI.recordValidator)
 
-		subApi.namesys, err = namesys.NewNameSystem(subApi.routing,
-			namesys.WithDatastore(subApi.repo.Datastore()),
-			namesys.WithDNSResolver(subApi.dnsResolver),
+		subAPI.namesys, err = namesys.NewNameSystem(subAPI.routing,
+			namesys.WithDatastore(subAPI.repo.Datastore()),
+			namesys.WithDNSResolver(subAPI.dnsResolver),
 			namesys.WithCache(cs))
 		if err != nil {
 			return nil, fmt.Errorf("error constructing namesys: %w", err)
 		}
 
-		subApi.provider = provider.NewOfflineProvider()
+		subAPI.provider = provider.NewOfflineProvider()
 
-		subApi.peerstore = nil
-		subApi.peerHost = nil
-		subApi.recordValidator = nil
+		subAPI.peerstore = nil
+		subAPI.peerHost = nil
+		subAPI.recordValidator = nil
 	}
 
 	if settings.Offline || !settings.FetchBlocks {
-		subApi.exchange = offlinexch.Exchange(subApi.blockstore)
-		subApi.blocks = bserv.New(subApi.blockstore, subApi.exchange)
-		subApi.dag = dag.NewDAGService(subApi.blocks)
+		subAPI.exchange = offlinexch.Exchange(subAPI.blockstore)
+		subAPI.blocks = bserv.New(subAPI.blockstore, subAPI.exchange)
+		subAPI.dag = dag.NewDAGService(subAPI.blocks)
 	}
 
-	return subApi, nil
+	return subAPI, nil
 }
 
 // getSession returns new api backed by the same node with a read-only session DAG
 func (api *CoreAPI) getSession(ctx context.Context) *CoreAPI {
-	sesApi := *api
+	sesAPI := *api
 
 	// TODO: We could also apply this to api.blocks, and compose into writable api,
 	// but this requires some changes in blockservice/merkledag
-	sesApi.dag = dag.NewReadOnlyDagService(dag.NewSession(ctx, api.dag))
+	sesAPI.dag = dag.NewReadOnlyDagService(dag.NewSession(ctx, api.dag))
 
-	return &sesApi
+	return &sesAPI
 }
diff --git a/core/coreapi/pubsub.go b/core/coreapi/pubsub.go
index a1c9a07f03931c1f2b99531f8c2edea6fcadd03e..af29afecc847f3e4dee6f03aff8f89cd74b81d61 100644
--- a/core/coreapi/pubsub.go
+++ b/core/coreapi/pubsub.go
@@ -97,7 +97,7 @@ func (api *PubSubAPI) Subscribe(ctx context.Context, topic string, opts ...caopt
 
 func (api *PubSubAPI) checkNode() (routing.Routing, error) {
 	if api.pubSub == nil {
-		return nil, errors.New("experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use.")
+		return nil, errors.New("experimental pubsub feature not enabled, run daemon with --enable-pubsub-experiment to use")
 	}
 
 	err := api.checkOnline(false)
diff --git a/core/coreapi/unixfs.go b/core/coreapi/unixfs.go
index e0fe0eb137d3ebbb3254b877186d5119a2d9a1c7..d43bfe3132bf325e2531eb03ac6c202f57503765 100644
--- a/core/coreapi/unixfs.go
+++ b/core/coreapi/unixfs.go
@@ -19,7 +19,6 @@ import (
 	bstore "github.com/ipfs/go-ipfs-blockstore"
 	files "github.com/ipfs/go-ipfs-files"
 	ipld "github.com/ipfs/go-ipld-format"
-	dag "github.com/ipfs/go-merkledag"
 	merkledag "github.com/ipfs/go-merkledag"
 	dagtest "github.com/ipfs/go-merkledag/test"
 	mfs "github.com/ipfs/go-mfs"
@@ -117,7 +116,7 @@ func (api *UnixfsAPI) Add(ctx context.Context, files files.Node, opts ...options
 	}
 
 	bserv := blockservice.New(addblockstore, exch) // hash security 001
-	dserv := dag.NewDAGService(bserv)
+	dserv := merkledag.NewDAGService(bserv)
 
 	// add a sync call to the DagService
 	// this ensures that data written to the DagService is persisted to the underlying datastore
diff --git a/core/coredag/cbor.go b/core/coredag/cbor.go
deleted file mode 100644
index 42372f50afb84c5ea1756e543baa06bed3a24ece..0000000000000000000000000000000000000000
--- a/core/coredag/cbor.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package coredag
-
-import (
-	"io"
-
-	ipldcbor "github.com/ipfs/go-ipld-cbor"
-	ipld "github.com/ipfs/go-ipld-format"
-)
-
-func cborJSONParser(r io.Reader, mhType uint64, mhLen int) ([]ipld.Node, error) {
-	nd, err := ipldcbor.FromJSON(r, mhType, mhLen)
-	if err != nil {
-		return nil, err
-	}
-
-	return []ipld.Node{nd}, nil
-}
-
-func cborRawParser(r io.Reader, mhType uint64, mhLen int) ([]ipld.Node, error) {
-	data, err := io.ReadAll(r)
-	if err != nil {
-		return nil, err
-	}
-
-	nd, err := ipldcbor.Decode(data, mhType, mhLen)
-	if err != nil {
-		return nil, err
-	}
-
-	return []ipld.Node{nd}, nil
-}
diff --git a/core/coredag/dagpb.go b/core/coredag/dagpb.go
deleted file mode 100644
index d1c7bcf0963cafba543f41b66cb6760bfa555def..0000000000000000000000000000000000000000
--- a/core/coredag/dagpb.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package coredag
-
-import (
-	"io"
-	"math"
-
-	"github.com/ipfs/go-merkledag"
-
-	cid "github.com/ipfs/go-cid"
-	ipld "github.com/ipfs/go-ipld-format"
-	mh "github.com/multiformats/go-multihash"
-)
-
-func dagpbJSONParser(r io.Reader, mhType uint64, mhLen int) ([]ipld.Node, error) {
-	data, err := io.ReadAll(r)
-	if err != nil {
-		return nil, err
-	}
-
-	nd := &merkledag.ProtoNode{}
-
-	err = nd.UnmarshalJSON(data)
-	if err != nil {
-		return nil, err
-	}
-
-	nd.SetCidBuilder(cidPrefix(mhType, mhLen))
-
-	return []ipld.Node{nd}, nil
-}
-
-func dagpbRawParser(r io.Reader, mhType uint64, mhLen int) ([]ipld.Node, error) {
-	data, err := io.ReadAll(r)
-	if err != nil {
-		return nil, err
-	}
-
-	nd, err := merkledag.DecodeProtobuf(data)
-	if err != nil {
-		return nil, err
-	}
-
-	nd.SetCidBuilder(cidPrefix(mhType, mhLen))
-
-	return []ipld.Node{nd}, nil
-}
-
-func cidPrefix(mhType uint64, mhLen int) *cid.Prefix {
-	if mhType == math.MaxUint64 {
-		mhType = mh.SHA2_256
-	}
-
-	prefix := &cid.Prefix{
-		MhType:   mhType,
-		MhLength: mhLen,
-		Version:  1,
-		Codec:    cid.DagProtobuf,
-	}
-
-	if mhType == mh.SHA2_256 {
-		prefix.Version = 0
-	}
-
-	return prefix
-}
diff --git a/core/coredag/dagtransl.go b/core/coredag/dagtransl.go
deleted file mode 100644
index 67a79246334318205fcdbdfd63547dda9c38a472..0000000000000000000000000000000000000000
--- a/core/coredag/dagtransl.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package coredag
-
-import (
-	"fmt"
-	"io"
-
-	ipld "github.com/ipfs/go-ipld-format"
-)
-
-// DagParser is function used for parsing stream into Node
-type DagParser func(r io.Reader, mhType uint64, mhLen int) ([]ipld.Node, error)
-
-// FormatParsers is used for mapping format descriptors to DagParsers
-type FormatParsers map[string]DagParser
-
-// InputEncParsers is used for mapping input encodings to FormatParsers
-type InputEncParsers map[string]FormatParsers
-
-// DefaultInputEncParsers is InputEncParser that is used everywhere
-var DefaultInputEncParsers = InputEncParsers{
-	"json":     defaultJSONParsers,
-	"raw":      defaultRawParsers,
-	"cbor":     defaultCborParsers,
-	"protobuf": defaultProtobufParsers,
-}
-
-var defaultJSONParsers = FormatParsers{
-	"cbor":     cborJSONParser,
-	"dag-cbor": cborJSONParser,
-
-	"protobuf": dagpbJSONParser,
-	"dag-pb":   dagpbJSONParser,
-}
-
-var defaultRawParsers = FormatParsers{
-	"cbor":     cborRawParser,
-	"dag-cbor": cborRawParser,
-
-	"protobuf": dagpbRawParser,
-	"dag-pb":   dagpbRawParser,
-
-	"raw": rawRawParser,
-}
-
-var defaultCborParsers = FormatParsers{
-	"cbor":     cborRawParser,
-	"dag-cbor": cborRawParser,
-}
-
-var defaultProtobufParsers = FormatParsers{
-	"protobuf": dagpbRawParser,
-	"dag-pb":   dagpbRawParser,
-}
-
-// ParseInputs uses DefaultInputEncParsers to parse io.Reader described by
-// input encoding and format to an instance of ipld Node
-func ParseInputs(ienc, format string, r io.Reader, mhType uint64, mhLen int) ([]ipld.Node, error) {
-	return DefaultInputEncParsers.ParseInputs(ienc, format, r, mhType, mhLen)
-}
-
-// AddParser adds DagParser under give input encoding and format
-func (iep InputEncParsers) AddParser(ienc, format string, f DagParser) {
-	m, ok := iep[ienc]
-	if !ok {
-		m = make(FormatParsers)
-		iep[ienc] = m
-	}
-
-	m[format] = f
-}
-
-// ParseInputs parses io.Reader described by input encoding and format to
-// an instance of ipld Node
-func (iep InputEncParsers) ParseInputs(ienc, format string, r io.Reader, mhType uint64, mhLen int) ([]ipld.Node, error) {
-	parsers, ok := iep[ienc]
-	if !ok {
-		return nil, fmt.Errorf("no input parser for %q", ienc)
-	}
-
-	parser, ok := parsers[format]
-	if !ok {
-		return nil, fmt.Errorf("no parser for format %q using input type %q", format, ienc)
-	}
-
-	return parser(r, mhType, mhLen)
-}
diff --git a/core/coredag/raw.go b/core/coredag/raw.go
deleted file mode 100644
index 41dc495ecf3921ff855fb06f7493984161cd375d..0000000000000000000000000000000000000000
--- a/core/coredag/raw.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package coredag
-
-import (
-	"io"
-	"math"
-
-	"github.com/ipfs/go-merkledag"
-
-	block "github.com/ipfs/go-block-format"
-	cid "github.com/ipfs/go-cid"
-	ipld "github.com/ipfs/go-ipld-format"
-	mh "github.com/multiformats/go-multihash"
-)
-
-func rawRawParser(r io.Reader, mhType uint64, mhLen int) ([]ipld.Node, error) {
-	if mhType == math.MaxUint64 {
-		mhType = mh.SHA2_256
-	}
-
-	data, err := io.ReadAll(r)
-	if err != nil {
-		return nil, err
-	}
-
-	h, err := mh.Sum(data, mhType, mhLen)
-	if err != nil {
-		return nil, err
-	}
-	c := cid.NewCidV1(cid.Raw, h)
-	blk, err := block.NewBlockWithCid(data, c)
-	if err != nil {
-		return nil, err
-	}
-	nd := &merkledag.RawNode{Block: blk}
-	return []ipld.Node{nd}, nil
-}
diff --git a/core/corehttp/gateway.go b/core/corehttp/gateway.go
index b9fff49b571bc1100ac3381b33dffeb76f485c07..0d0a234d946fb0e8df146c5d1b2f5708aa2c6b3c 100644
--- a/core/corehttp/gateway.go
+++ b/core/corehttp/gateway.go
@@ -77,7 +77,7 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
 
 		AddAccessControlHeaders(headers)
 
-		offlineApi, err := api.WithOptions(options.Api.Offline(true))
+		offlineAPI, err := api.WithOptions(options.Api.Offline(true))
 		if err != nil {
 			return nil, err
 		}
@@ -86,7 +86,7 @@ func GatewayOption(writable bool, paths ...string) ServeOption {
 			Headers:               headers,
 			Writable:              writable,
 			FastDirIndexThreshold: int(cfg.Gateway.FastDirIndexThreshold.WithDefault(100)),
-		}, api, offlineApi)
+		}, api, offlineAPI)
 
 		gateway = otelhttp.NewHandler(gateway, "Gateway.Request")
 
diff --git a/core/corehttp/gateway_handler.go b/core/corehttp/gateway_handler.go
index 585ca89cb1701e4f528eddf266a51313942b94a6..7f0f11885e69df8eaefbb7431c9237ce15bb82ae 100644
--- a/core/corehttp/gateway_handler.go
+++ b/core/corehttp/gateway_handler.go
@@ -39,7 +39,7 @@ const (
 )
 
 var (
-	onlyAscii = regexp.MustCompile("[[:^ascii:]]")
+	onlyASCII = regexp.MustCompile("[[:^ascii:]]")
 	noModtime = time.Unix(0, 0) // disables Last-Modified header if passed as modtime
 )
 
@@ -68,7 +68,7 @@ type redirectTemplateData struct {
 type gatewayHandler struct {
 	config     GatewayConfig
 	api        NodeAPI
-	offlineApi NodeAPI
+	offlineAPI NodeAPI
 
 	// generic metrics
 	firstContentBlockGetMetric *prometheus.HistogramVec
@@ -214,15 +214,15 @@ func newGatewayHistogramMetric(name string, help string) *prometheus.HistogramVe
 
 // NewGatewayHandler returns an http.Handler that can act as a gateway to IPFS content
 // offlineApi is a version of the API that should not make network requests for missing data
-func NewGatewayHandler(c GatewayConfig, api NodeAPI, offlineApi NodeAPI) http.Handler {
-	return newGatewayHandler(c, api, offlineApi)
+func NewGatewayHandler(c GatewayConfig, api NodeAPI, offlineAPI NodeAPI) http.Handler {
+	return newGatewayHandler(c, api, offlineAPI)
 }
 
-func newGatewayHandler(c GatewayConfig, api NodeAPI, offlineApi NodeAPI) *gatewayHandler {
+func newGatewayHandler(c GatewayConfig, api NodeAPI, offlineAPI NodeAPI) *gatewayHandler {
 	i := &gatewayHandler{
 		config:     c,
 		api:        api,
-		offlineApi: offlineApi,
+		offlineAPI: offlineAPI,
 		// Improved Metrics
 		// ----------------------------
 		// Time till the first content block (bar in /ipfs/cid/foo/bar)
@@ -430,6 +430,10 @@ func (i *gatewayHandler) getOrHeadHandler(w http.ResponseWriter, r *http.Request
 		carVersion := formatParams["version"]
 		i.serveCAR(r.Context(), w, r, resolvedPath, contentPath, carVersion, begin)
 		return
+	case "application/x-tar":
+		logger.Debugw("serving tar file", "path", contentPath)
+		i.serveTAR(r.Context(), w, r, resolvedPath, contentPath, begin, logger)
+		return
 	default: // catch-all for unsuported application/vnd.*
 		err := fmt.Errorf("unsupported format %q", responseFormat)
 		webError(w, "failed respond with requested content type", err, http.StatusBadRequest)
@@ -683,7 +687,7 @@ func addContentDispositionHeader(w http.ResponseWriter, r *http.Request, content
 // Set Content-Disposition to arbitrary filename and disposition
 func setContentDispositionHeader(w http.ResponseWriter, filename string, disposition string) {
 	utf8Name := url.PathEscape(filename)
-	asciiName := url.PathEscape(onlyAscii.ReplaceAllLiteralString(filename, "_"))
+	asciiName := url.PathEscape(onlyASCII.ReplaceAllLiteralString(filename, "_"))
 	w.Header().Set("Content-Disposition", fmt.Sprintf("%s; filename=\"%s\"; filename*=UTF-8''%s", disposition, asciiName, utf8Name))
 }
 
@@ -842,9 +846,10 @@ func getEtag(r *http.Request, cid cid.Cid) string {
 	responseFormat, _, err := customResponseFormat(r)
 	if err == nil && responseFormat != "" {
 		// application/vnd.ipld.foo → foo
-		f := responseFormat[strings.LastIndex(responseFormat, ".")+1:]
-		// Etag: "cid.foo" (gives us nice compression together with Content-Disposition in block (raw) and car responses)
-		suffix = `.` + f + suffix
+		// application/x-bar → x-bar
+		shortFormat := responseFormat[strings.LastIndexAny(responseFormat, "/.")+1:]
+		// Etag: "cid.shortFmt" (gives us nice compression together with Content-Disposition in block (raw) and car responses)
+		suffix = `.` + shortFormat + suffix
 	}
 	// TODO: include selector suffix when https://github.com/ipfs/kubo/issues/8769 lands
 	return prefix + cid.String() + suffix
@@ -859,14 +864,17 @@ func customResponseFormat(r *http.Request) (mediaType string, params map[string]
 			return "application/vnd.ipld.raw", nil, nil
 		case "car":
 			return "application/vnd.ipld.car", nil, nil
+		case "tar":
+			return "application/x-tar", nil, nil
 		}
 	}
 	// Browsers and other user agents will send Accept header with generic types like:
 	// Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
-	// We only care about explciit, vendor-specific content-types.
+	// We only care about explicit, vendor-specific content-types.
 	for _, accept := range r.Header.Values("Accept") {
 		// respond to the very first ipld content type
-		if strings.HasPrefix(accept, "application/vnd.ipld") {
+		if strings.HasPrefix(accept, "application/vnd.ipld") ||
+			strings.HasPrefix(accept, "application/x-tar") {
 			mediatype, params, err := mime.ParseMediaType(accept)
 			if err != nil {
 				return "", nil, err
@@ -933,7 +941,7 @@ func (i *gatewayHandler) handlePathResolution(w http.ResponseWriter, r *http.Req
 // https://github.com/ipfs/specs/blob/main/http-gateways/PATH_GATEWAY.md#cache-control-request-header
 func (i *gatewayHandler) handleOnlyIfCached(w http.ResponseWriter, r *http.Request, contentPath ipath.Path, logger *zap.SugaredLogger) (requestHandled bool) {
 	if r.Header.Get("Cache-Control") == "only-if-cached" {
-		_, err := i.offlineApi.Block().Stat(r.Context(), contentPath)
+		_, err := i.offlineAPI.Block().Stat(r.Context(), contentPath)
 		if err != nil {
 			if r.Method == http.MethodHead {
 				w.WriteHeader(http.StatusPreconditionFailed)
diff --git a/core/corehttp/gateway_handler_tar.go b/core/corehttp/gateway_handler_tar.go
new file mode 100644
index 0000000000000000000000000000000000000000..532d887576040ad0aac3802e35a0378b8000fb66
--- /dev/null
+++ b/core/corehttp/gateway_handler_tar.go
@@ -0,0 +1,92 @@
+package corehttp
+
+import (
+	"context"
+	"html"
+	"net/http"
+	"time"
+
+	files "github.com/ipfs/go-ipfs-files"
+	ipath "github.com/ipfs/interface-go-ipfs-core/path"
+	"github.com/ipfs/kubo/tracing"
+	"go.opentelemetry.io/otel/attribute"
+	"go.opentelemetry.io/otel/trace"
+	"go.uber.org/zap"
+)
+
+var unixEpochTime = time.Unix(0, 0)
+
+func (i *gatewayHandler) serveTAR(ctx context.Context, w http.ResponseWriter, r *http.Request, resolvedPath ipath.Resolved, contentPath ipath.Path, begin time.Time, logger *zap.SugaredLogger) {
+	ctx, span := tracing.Span(ctx, "Gateway", "ServeTAR", trace.WithAttributes(attribute.String("path", resolvedPath.String())))
+	defer span.End()
+
+	ctx, cancel := context.WithCancel(ctx)
+	defer cancel()
+
+	// Get Unixfs file
+	file, err := i.api.Unixfs().Get(ctx, resolvedPath)
+	if err != nil {
+		webError(w, "ipfs cat "+html.EscapeString(contentPath.String()), err, http.StatusBadRequest)
+		return
+	}
+	defer file.Close()
+
+	rootCid := resolvedPath.Cid()
+
+	// Set Cache-Control and read optional Last-Modified time
+	modtime := addCacheControlHeaders(w, r, contentPath, rootCid)
+
+	// Weak Etag W/ because we can't guarantee byte-for-byte identical
+	// responses, but still want to benefit from HTTP Caching. Two TAR
+	// responses for the same CID will be logically equivalent,
+	// but when TAR is streamed, then in theory, files and directories
+	// may arrive in different order (depends on TAR lib and filesystem/inodes).
+	etag := `W/` + getEtag(r, rootCid)
+	w.Header().Set("Etag", etag)
+
+	// Finish early if Etag match
+	if r.Header.Get("If-None-Match") == etag {
+		w.WriteHeader(http.StatusNotModified)
+		return
+	}
+
+	// Set Content-Disposition
+	var name string
+	if urlFilename := r.URL.Query().Get("filename"); urlFilename != "" {
+		name = urlFilename
+	} else {
+		name = rootCid.String() + ".tar"
+	}
+	setContentDispositionHeader(w, name, "attachment")
+
+	// Construct the TAR writer
+	tarw, err := files.NewTarWriter(w)
+	if err != nil {
+		webError(w, "could not build tar writer", err, http.StatusInternalServerError)
+		return
+	}
+	defer tarw.Close()
+
+	// Sets correct Last-Modified header. This code is borrowed from the standard
+	// library (net/http/server.go) as we cannot use serveFile without throwing the entire
+	// TAR into the memory first.
+	if !(modtime.IsZero() || modtime.Equal(unixEpochTime)) {
+		w.Header().Set("Last-Modified", modtime.UTC().Format(http.TimeFormat))
+	}
+
+	w.Header().Set("Content-Type", "application/x-tar")
+	w.Header().Set("X-Content-Type-Options", "nosniff") // no funny business in the browsers :^)
+
+	// The TAR has a top-level directory (or file) named by the CID.
+	if err := tarw.WriteFile(file, rootCid.String()); err != nil {
+		w.Header().Set("X-Stream-Error", err.Error())
+		// Trailer headers do not work in web browsers
+		// (see https://github.com/mdn/browser-compat-data/issues/14703)
+		// and we have limited options around error handling in browser contexts.
+		// To improve UX/DX, we finish response stream with error message, allowing client to
+		// (1) detect error by having corrupted TAR
+		// (2) be able to reason what went wrong by instecting the tail of TAR stream
+		_, _ = w.Write([]byte(err.Error()))
+		return
+	}
+}
diff --git a/core/corehttp/gateway_handler_unixfs_dir.go b/core/corehttp/gateway_handler_unixfs_dir.go
index 511066f435827faa511bd7020dc93173f365ef9e..1c803b13b34637cd4a85dfbded46a98a5dce3634 100644
--- a/core/corehttp/gateway_handler_unixfs_dir.go
+++ b/core/corehttp/gateway_handler_unixfs_dir.go
@@ -39,10 +39,10 @@ func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWrit
 		webError(w, "failed to parse request path", err, http.StatusInternalServerError)
 		return
 	}
-	originalUrlPath := requestURI.Path
+	originalURLPath := requestURI.Path
 
 	// Ensure directory paths end with '/'
-	if originalUrlPath[len(originalUrlPath)-1] != '/' {
+	if originalURLPath[len(originalURLPath)-1] != '/' {
 		// don't redirect to trailing slash if it's go get
 		// https://github.com/ipfs/kubo/pull/3963
 		goget := r.URL.Query().Get("go-get") == "1"
@@ -53,7 +53,7 @@ func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWrit
 				suffix = suffix + "?" + r.URL.RawQuery
 			}
 			// /ipfs/cid/foo?bar must be redirected to /ipfs/cid/foo/?bar
-			redirectURL := originalUrlPath + suffix
+			redirectURL := originalURLPath + suffix
 			logger.Debugw("directory location moved permanently", "status", http.StatusMovedPermanently)
 			http.Redirect(w, r, redirectURL, http.StatusMovedPermanently)
 			return
@@ -125,7 +125,7 @@ func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWrit
 		di := directoryItem{
 			Size:      "", // no size because we did not fetch child nodes
 			Name:      link.Name,
-			Path:      gopath.Join(originalUrlPath, link.Name),
+			Path:      gopath.Join(originalURLPath, link.Name),
 			Hash:      hash,
 			ShortHash: shortHash(hash),
 		}
@@ -149,7 +149,7 @@ func (i *gatewayHandler) serveDirectory(ctx context.Context, w http.ResponseWrit
 
 	// construct the correct back link
 	// https://github.com/ipfs/kubo/issues/1365
-	var backLink string = originalUrlPath
+	backLink := originalURLPath
 
 	// don't go further up than /ipfs/$hash/
 	pathSplit := path.SplitList(contentPath.String())
diff --git a/core/corehttp/gateway_indexPage.go b/core/corehttp/gateway_indexPage.go
index 1168d65560cfa69a6ffe1c1aab5ee2d608241dc5..19e444da3f05289e0c8abf15db8c198bcda17e2b 100644
--- a/core/corehttp/gateway_indexPage.go
+++ b/core/corehttp/gateway_indexPage.go
@@ -117,8 +117,8 @@ func init() {
 
 	// custom template-escaping function to escape a full path, including '#' and '?'
 	urlEscape := func(rawUrl string) string {
-		pathUrl := url.URL{Path: rawUrl}
-		return pathUrl.String()
+		pathURL := url.URL{Path: rawUrl}
+		return pathURL.String()
 	}
 
 	// Directory listing template
diff --git a/core/corehttp/hostname.go b/core/corehttp/hostname.go
index 5445740e6349c85fe0fe16cb61bad624c3d522b9..39e857aadfb53cb908abaff3c7f08a4ef4619d0f 100644
--- a/core/corehttp/hostname.go
+++ b/core/corehttp/hostname.go
@@ -84,7 +84,8 @@ func HostnameOption() ServeOption {
 					if gw.UseSubdomains {
 						// Yes, redirect if applicable
 						// Example: dweb.link/ipfs/{cid} → {cid}.ipfs.dweb.link
-						newURL, err := toSubdomainURL(host, r.URL.Path, r, coreAPI)
+						useInlinedDNSLink := gw.InlineDNSLink.WithDefault(config.DefaultInlineDNSLink)
+						newURL, err := toSubdomainURL(host, r.URL.Path, r, useInlinedDNSLink, coreAPI)
 						if err != nil {
 							http.Error(w, err.Error(), http.StatusBadRequest)
 							return
@@ -132,6 +133,9 @@ func HostnameOption() ServeOption {
 				// Assemble original path prefix.
 				pathPrefix := "/" + ns + "/" + rootID
 
+				// Retrieve whether or not we should inline DNSLink.
+				useInlinedDNSLink := gw.InlineDNSLink.WithDefault(config.DefaultInlineDNSLink)
+
 				// Does this gateway _handle_ subdomains AND this path?
 				if !(gw.UseSubdomains && hasPrefix(pathPrefix, gw.Paths...)) {
 					// If not, resource does not exist, return 404
@@ -149,7 +153,7 @@ func HostnameOption() ServeOption {
 					}
 					if !strings.HasPrefix(r.Host, dnsCID) {
 						dnsPrefix := "/" + ns + "/" + dnsCID
-						newURL, err := toSubdomainURL(gwHostname, dnsPrefix+r.URL.Path, r, coreAPI)
+						newURL, err := toSubdomainURL(gwHostname, dnsPrefix+r.URL.Path, r, useInlinedDNSLink, coreAPI)
 						if err != nil {
 							http.Error(w, err.Error(), http.StatusBadRequest)
 							return
@@ -165,7 +169,7 @@ func HostnameOption() ServeOption {
 					// Do we need to fix multicodec in PeerID represented as CIDv1?
 					if isPeerIDNamespace(ns) {
 						if rootCID.Type() != cid.Libp2pKey {
-							newURL, err := toSubdomainURL(gwHostname, pathPrefix+r.URL.Path, r, coreAPI)
+							newURL, err := toSubdomainURL(gwHostname, pathPrefix+r.URL.Path, r, useInlinedDNSLink, coreAPI)
 							if err != nil {
 								http.Error(w, err.Error(), http.StatusBadRequest)
 								return
@@ -451,7 +455,7 @@ func toDNSLinkFQDN(dnsLabel string) (fqdn string) {
 }
 
 // Converts a hostname/path to a subdomain-based URL, if applicable.
-func toSubdomainURL(hostname, path string, r *http.Request, ipfs iface.CoreAPI) (redirURL string, err error) {
+func toSubdomainURL(hostname, path string, r *http.Request, inlineDNSLink bool, ipfs iface.CoreAPI) (redirURL string, err error) {
 	var scheme, ns, rootID, rest string
 
 	query := r.URL.RawQuery
@@ -554,7 +558,7 @@ func toSubdomainURL(hostname, path string, r *http.Request, ipfs iface.CoreAPI)
 		// can be loaded from a subdomain gateway with a wildcard TLS cert if
 		// represented as a single DNS label:
 		// https://my-v--long-example-com.ipns.dweb.link
-		if isHTTPS && ns == "ipns" && strings.Contains(rootID, ".") {
+		if (inlineDNSLink || isHTTPS) && ns == "ipns" && strings.Contains(rootID, ".") {
 			if isDNSLinkName(r.Context(), ipfs, rootID) {
 				// my.v-long.example.com → my-v--long-example-com
 				dnsLabel, err := toDNSLinkDNSLabel(rootID)
diff --git a/core/corehttp/hostname_test.go b/core/corehttp/hostname_test.go
index 60b537239949fcdc2987514f7168d7f462765c89..6f0713528bcadafa591c087c8424da4bca36920d 100644
--- a/core/corehttp/hostname_test.go
+++ b/core/corehttp/hostname_test.go
@@ -36,35 +36,39 @@ func TestToSubdomainURL(t *testing.T) {
 
 	for _, test := range []struct {
 		// in:
-		request    *http.Request
-		gwHostname string
-		path       string
+		request       *http.Request
+		gwHostname    string
+		inlineDNSLink bool
+		path          string
 		// out:
 		url string
 		err error
 	}{
 		// DNSLink
-		{httpRequest, "localhost", "/ipns/dnslink.io", "http://dnslink.io.ipns.localhost/", nil},
+		{httpRequest, "localhost", false, "/ipns/dnslink.io", "http://dnslink.io.ipns.localhost/", nil},
 		// Hostname with port
-		{httpRequest, "localhost:8080", "/ipns/dnslink.io", "http://dnslink.io.ipns.localhost:8080/", nil},
+		{httpRequest, "localhost:8080", false, "/ipns/dnslink.io", "http://dnslink.io.ipns.localhost:8080/", nil},
 		// CIDv0 → CIDv1base32
-		{httpRequest, "localhost", "/ipfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.ipfs.localhost/", nil},
+		{httpRequest, "localhost", false, "/ipfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.ipfs.localhost/", nil},
 		// CIDv1 with long sha512
-		{httpRequest, "localhost", "/ipfs/bafkrgqe3ohjcjplc6n4f3fwunlj6upltggn7xqujbsvnvyw764srszz4u4rshq6ztos4chl4plgg4ffyyxnayrtdi5oc4xb2332g645433aeg", "", errors.New("CID incompatible with DNS label length limit of 63: kf1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5oj")},
+		{httpRequest, "localhost", false, "/ipfs/bafkrgqe3ohjcjplc6n4f3fwunlj6upltggn7xqujbsvnvyw764srszz4u4rshq6ztos4chl4plgg4ffyyxnayrtdi5oc4xb2332g645433aeg", "", errors.New("CID incompatible with DNS label length limit of 63: kf1siqrebi3vir8sab33hu5vcy008djegvay6atmz91ojesyjs8lx350b7y7i1nvyw2haytfukfyu2f2x4tocdrfa0zgij6p4zpl4u5oj")},
 		// PeerID as CIDv1 needs to have libp2p-key multicodec
-		{httpRequest, "localhost", "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", "http://k2k4r8n0flx3ra0y5dr8fmyvwbzy3eiztmtq6th694k5a3rznayp3e4o.ipns.localhost/", nil},
-		{httpRequest, "localhost", "/ipns/bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", "http://k2k4r8l9ja7hkzynavdqup76ou46tnvuaqegbd04a4o1mpbsey0meucb.ipns.localhost/", nil},
+		{httpRequest, "localhost", false, "/ipns/QmY3hE8xgFCjGcz6PHgnvJz5HZi1BaKRfPkn1ghZUcYMjD", "http://k2k4r8n0flx3ra0y5dr8fmyvwbzy3eiztmtq6th694k5a3rznayp3e4o.ipns.localhost/", nil},
+		{httpRequest, "localhost", false, "/ipns/bafybeickencdqw37dpz3ha36ewrh4undfjt2do52chtcky4rxkj447qhdm", "http://k2k4r8l9ja7hkzynavdqup76ou46tnvuaqegbd04a4o1mpbsey0meucb.ipns.localhost/", nil},
 		// PeerID: ed25519+identity multihash → CIDv1Base36
-		{httpRequest, "localhost", "/ipns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", "http://k51qzi5uqu5di608geewp3nqkg0bpujoasmka7ftkyxgcm3fh1aroup0gsdrna.ipns.localhost/", nil},
-		{httpRequest, "sub.localhost", "/ipfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.ipfs.sub.localhost/", nil},
+		{httpRequest, "localhost", false, "/ipns/12D3KooWFB51PRY9BxcXSH6khFXw1BZeszeLDy7C8GciskqCTZn5", "http://k51qzi5uqu5di608geewp3nqkg0bpujoasmka7ftkyxgcm3fh1aroup0gsdrna.ipns.localhost/", nil},
+		{httpRequest, "sub.localhost", false, "/ipfs/QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n", "http://bafybeif7a7gdklt6hodwdrmwmxnhksctcuav6lfxlcyfz4khzl3qfmvcgu.ipfs.sub.localhost/", nil},
 		// HTTPS requires DNSLink name to fit in a single DNS label – see "Option C" from https://github.com/ipfs/in-web-browsers/issues/169
-		{httpRequest, "dweb.link", "/ipns/dnslink.long-name.example.com", "http://dnslink.long-name.example.com.ipns.dweb.link/", nil},
-		{httpsRequest, "dweb.link", "/ipns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.ipns.dweb.link/", nil},
-		{httpsProxiedRequest, "dweb.link", "/ipns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.ipns.dweb.link/", nil},
+		{httpRequest, "dweb.link", false, "/ipns/dnslink.long-name.example.com", "http://dnslink.long-name.example.com.ipns.dweb.link/", nil},
+		{httpsRequest, "dweb.link", false, "/ipns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.ipns.dweb.link/", nil},
+		{httpsProxiedRequest, "dweb.link", false, "/ipns/dnslink.long-name.example.com", "https://dnslink-long--name-example-com.ipns.dweb.link/", nil},
+		// HTTP requests can also be converted to fit into a single DNS label - https://github.com/ipfs/kubo/issues/9243
+		{httpRequest, "localhost", true, "/ipns/dnslink.long-name.example.com", "http://dnslink-long--name-example-com.ipns.localhost/", nil},
+		{httpRequest, "dweb.link", true, "/ipns/dnslink.long-name.example.com", "http://dnslink-long--name-example-com.ipns.dweb.link/", nil},
 	} {
-		url, err := toSubdomainURL(test.gwHostname, test.path, test.request, coreAPI)
+		url, err := toSubdomainURL(test.gwHostname, test.path, test.request, test.inlineDNSLink, coreAPI)
 		if url != test.url || !equalError(err, test.err) {
-			t.Errorf("(%s, %s) returned (%s, %v), expected (%s, %v)", test.gwHostname, test.path, url, err, test.url, test.err)
+			t.Errorf("(%s, %v, %s) returned (%s, %v), expected (%s, %v)", test.gwHostname, test.inlineDNSLink, test.path, url, err, test.url, test.err)
 		}
 	}
 }
diff --git a/core/corehttp/lazyseek_test.go b/core/corehttp/lazyseek_test.go
index 2733acafb3867fea5cc969af4944510fb8fae85f..49aca0a0e2a1c8ee606a0e1d720239d143ab821a 100644
--- a/core/corehttp/lazyseek_test.go
+++ b/core/corehttp/lazyseek_test.go
@@ -11,14 +11,14 @@ type badSeeker struct {
 	io.ReadSeeker
 }
 
-var badSeekErr = fmt.Errorf("I'm a bad seeker")
+var errBadSeek = fmt.Errorf("bad seeker")
 
 func (bs badSeeker) Seek(offset int64, whence int) (int64, error) {
 	off, err := bs.ReadSeeker.Seek(0, io.SeekCurrent)
 	if err != nil {
 		panic(err)
 	}
-	return off, badSeekErr
+	return off, errBadSeek
 }
 
 func TestLazySeekerError(t *testing.T) {
@@ -73,7 +73,7 @@ func TestLazySeekerError(t *testing.T) {
 	if err == nil {
 		t.Fatalf("expected an error, got output %s", string(b))
 	}
-	if err != badSeekErr {
+	if err != errBadSeek {
 		t.Fatalf("expected a bad seek error, got %s", err)
 	}
 	if len(b) != 0 {
diff --git a/core/corehttp/metrics.go b/core/corehttp/metrics.go
index da2d576a21b1a20779bd4d1319003b0b28fd3286..e26be1ca9f376efbcd22f1f082adcd5919d945f2 100644
--- a/core/corehttp/metrics.go
+++ b/core/corehttp/metrics.go
@@ -164,7 +164,7 @@ type IpfsNodeCollector struct {
 	Node *core.IpfsNode
 }
 
-func (_ IpfsNodeCollector) Describe(ch chan<- *prometheus.Desc) {
+func (IpfsNodeCollector) Describe(ch chan<- *prometheus.Desc) {
 	ch <- peersTotalMetric
 }
 
diff --git a/core/corehttp/p2p_proxy.go b/core/corehttp/p2p_proxy.go
index 44c20c2ad5c70d5b74bb2d72064e4c5fc8e13d78..e239f47cd0472cfa76675902e9379c0bdefae60c 100644
--- a/core/corehttp/p2p_proxy.go
+++ b/core/corehttp/p2p_proxy.go
@@ -58,11 +58,11 @@ func parseRequest(request *http.Request) (*proxyRequest, error) {
 
 	split := strings.SplitN(path, "/", 5)
 	if len(split) < 5 {
-		return nil, fmt.Errorf("Invalid request path '%s'", path)
+		return nil, fmt.Errorf("invalid request path '%s'", path)
 	}
 
 	if _, err := peer.Decode(split[2]); err != nil {
-		return nil, fmt.Errorf("Invalid request path '%s'", path)
+		return nil, fmt.Errorf("invalid request path '%s'", path)
 	}
 
 	if split[3] == "http" {
@@ -71,7 +71,7 @@ func parseRequest(request *http.Request) (*proxyRequest, error) {
 
 	split = strings.SplitN(path, "/", 7)
 	if len(split) < 7 || split[3] != "x" || split[5] != "http" {
-		return nil, fmt.Errorf("Invalid request path '%s'", path)
+		return nil, fmt.Errorf("invalid request path '%s'", path)
 	}
 
 	return &proxyRequest{split[2], protocol.ID("/x/" + split[4] + "/http"), split[6]}, nil
diff --git a/core/corehttp/redirect.go b/core/corehttp/redirect.go
index 5af596bd6f37e83307b49883663ebad7acbb9b3c..bcd536d235ba9dd273be727cba8a8b01297e13ed 100644
--- a/core/corehttp/redirect.go
+++ b/core/corehttp/redirect.go
@@ -24,5 +24,5 @@ type redirectHandler struct {
 }
 
 func (i *redirectHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	http.Redirect(w, r, i.path, 302)
+	http.Redirect(w, r, i.path, http.StatusFound)
 }
diff --git a/core/corehttp/webui.go b/core/corehttp/webui.go
index 4952dc16fa5e16bef0f1d1cc1600b4a1bc5fb146..c196c0eef7a9cb2a3253b6c0cea96536ba1304ff 100644
--- a/core/corehttp/webui.go
+++ b/core/corehttp/webui.go
@@ -1,11 +1,13 @@
 package corehttp
 
 // TODO: move to IPNS
-const WebUIPath = "/ipfs/bafybeiageaoxg6d7npaof6eyzqbwvbubyler7bq44hayik2hvqcggg7d2y" // v2.18.1
+const WebUIPath = "/ipfs/bafybeibjbq3tmmy7wuihhhwvbladjsd3gx3kfjepxzkq6wylik6wc3whzy" // v2.20.0
 
-// this is a list of all past webUI paths.
+// WebUIPaths is a list of all past webUI paths.
 var WebUIPaths = []string{
 	WebUIPath,
+	"/ipfs/bafybeiavrvt53fks6u32n5p2morgblcmck4bh4ymf4rrwu7ah5zsykmqqa",
+	"/ipfs/bafybeiageaoxg6d7npaof6eyzqbwvbubyler7bq44hayik2hvqcggg7d2y",
 	"/ipfs/bafybeidb5eryh72zajiokdggzo7yct2d6hhcflncji5im2y5w26uuygdsm",
 	"/ipfs/bafybeibozpulxtpv5nhfa2ue3dcjx23ndh3gwr5vwllk7ptoyfwnfjjr4q",
 	"/ipfs/bafybeiednzu62vskme5wpoj4bjjikeg3xovfpp4t7vxk5ty2jxdi4mv4bu",
diff --git a/core/node/bitswap.go b/core/node/bitswap.go
index 2b9cf641a1b98ae55d9456ed70ad2c63575640e1..42b948f7520a47bd648abcad784035f0d3458328 100644
--- a/core/node/bitswap.go
+++ b/core/node/bitswap.go
@@ -2,6 +2,7 @@ package node
 
 import (
 	"context"
+	"time"
 
 	"github.com/ipfs/go-bitswap"
 	"github.com/ipfs/go-bitswap/network"
@@ -21,6 +22,7 @@ const (
 	DefaultTaskWorkerCount             = 8
 	DefaultEngineTaskWorkerCount       = 8
 	DefaultMaxOutstandingBytesPerPeer  = 1 << 20
+	DefaultProviderSearchDelay         = 1000 * time.Millisecond
 )
 
 type bitswapOptionsOut struct {
@@ -40,6 +42,7 @@ func BitswapOptions(cfg *config.Config, provide bool) interface{} {
 
 		opts := []bitswap.Option{
 			bitswap.ProvideEnabled(provide),
+			bitswap.ProviderSearchDelay(internalBsCfg.ProviderSearchDelay.WithDefault(DefaultProviderSearchDelay)), // See https://github.com/ipfs/go-ipfs/issues/8807 for rationale
 			bitswap.EngineBlockstoreWorkerCount(int(internalBsCfg.EngineBlockstoreWorkerCount.WithDefault(DefaultEngineBlockstoreWorkerCount))),
 			bitswap.TaskWorkerCount(int(internalBsCfg.TaskWorkerCount.WithDefault(DefaultTaskWorkerCount))),
 			bitswap.EngineTaskWorkerCount(int(internalBsCfg.EngineTaskWorkerCount.WithDefault(DefaultEngineTaskWorkerCount))),
diff --git a/core/node/groups.go b/core/node/groups.go
index fca984650d937af043a8c5355989ba283cedadec..5c576fc447f81f3603673c3c16ab0f0de7d85cc7 100644
--- a/core/node/groups.go
+++ b/core/node/groups.go
@@ -38,33 +38,20 @@ var BaseLibP2P = fx.Options(
 )
 
 func LibP2P(bcfg *BuildCfg, cfg *config.Config) fx.Option {
-	// parse ConnMgr config
-
-	grace := config.DefaultConnMgrGracePeriod
-	low := config.DefaultConnMgrLowWater
-	high := config.DefaultConnMgrHighWater
-
-	connmgr := fx.Options()
-
-	if cfg.Swarm.ConnMgr.Type != "none" {
-		switch cfg.Swarm.ConnMgr.Type {
-		case "":
-			// 'default' value is the basic connection manager
-			break
-		case "basic":
-			var err error
-			grace, err = time.ParseDuration(cfg.Swarm.ConnMgr.GracePeriod)
-			if err != nil {
-				return fx.Error(fmt.Errorf("parsing Swarm.ConnMgr.GracePeriod: %s", err))
-			}
-
-			low = cfg.Swarm.ConnMgr.LowWater
-			high = cfg.Swarm.ConnMgr.HighWater
-		default:
-			return fx.Error(fmt.Errorf("unrecognized ConnMgr.Type: %q", cfg.Swarm.ConnMgr.Type))
-		}
-
+	var connmgr fx.Option
+
+	// set connmgr based on Swarm.ConnMgr.Type
+	connMgrType := cfg.Swarm.ConnMgr.Type.WithDefault(config.DefaultConnMgrType)
+	switch connMgrType {
+	case "none":
+		connmgr = fx.Options() // noop
+	case "", "basic":
+		grace := cfg.Swarm.ConnMgr.GracePeriod.WithDefault(config.DefaultConnMgrGracePeriod)
+		low := int(cfg.Swarm.ConnMgr.LowWater.WithDefault(config.DefaultConnMgrLowWater))
+		high := int(cfg.Swarm.ConnMgr.HighWater.WithDefault(config.DefaultConnMgrHighWater))
 		connmgr = fx.Provide(libp2p.ConnectionManager(low, high, grace))
+	default:
+		return fx.Error(fmt.Errorf("unrecognized Swarm.ConnMgr.Type: %q", connMgrType))
 	}
 
 	// parse PubSub config
diff --git a/core/node/libp2p/fd/sys_not_unix.go b/core/node/libp2p/fd/sys_not_unix.go
new file mode 100644
index 0000000000000000000000000000000000000000..c857987480d81b39ea4a368cef6c0b1023ef7d32
--- /dev/null
+++ b/core/node/libp2p/fd/sys_not_unix.go
@@ -0,0 +1,7 @@
+//go:build !linux && !darwin && !windows
+
+package fd
+
+func GetNumFDs() int {
+	return 0
+}
diff --git a/core/node/libp2p/fd/sys_unix.go b/core/node/libp2p/fd/sys_unix.go
new file mode 100644
index 0000000000000000000000000000000000000000..5e417c0fa6d9cfaa1a110c695f65063200634b83
--- /dev/null
+++ b/core/node/libp2p/fd/sys_unix.go
@@ -0,0 +1,16 @@
+//go:build linux || darwin
+// +build linux darwin
+
+package fd
+
+import (
+	"golang.org/x/sys/unix"
+)
+
+func GetNumFDs() int {
+	var l unix.Rlimit
+	if err := unix.Getrlimit(unix.RLIMIT_NOFILE, &l); err != nil {
+		return 0
+	}
+	return int(l.Cur)
+}
diff --git a/core/node/libp2p/fd/sys_windows.go b/core/node/libp2p/fd/sys_windows.go
new file mode 100644
index 0000000000000000000000000000000000000000..eec17f3883fd2a5c23d684cc240a29fca44d9495
--- /dev/null
+++ b/core/node/libp2p/fd/sys_windows.go
@@ -0,0 +1,11 @@
+//go:build windows
+
+package fd
+
+import (
+	"math"
+)
+
+func GetNumFDs() int {
+	return math.MaxInt
+}
diff --git a/core/node/libp2p/libp2p.go b/core/node/libp2p/libp2p.go
index c041139d1714d661e55344dc80eaf793adc3bd12..b36197152215c526bb7a517153df90daee9306f5 100644
--- a/core/node/libp2p/libp2p.go
+++ b/core/node/libp2p/libp2p.go
@@ -25,7 +25,6 @@ type Libp2pOpts struct {
 	Opts []libp2p.Option `group:"libp2p"`
 }
 
-// Misc options
 var UserAgent = simpleOpt(libp2p.UserAgent(version.GetUserAgentVersion()))
 
 func ConnectionManager(low, high int, grace time.Duration) func() (opts Libp2pOpts, err error) {
diff --git a/core/node/libp2p/rcmgr.go b/core/node/libp2p/rcmgr.go
index 2764f67af8a4c66ca263bc8363c0703ba7cd3f8a..35894dc7234078abef82447d024b9a3947ce580e 100644
--- a/core/node/libp2p/rcmgr.go
+++ b/core/node/libp2p/rcmgr.go
@@ -9,10 +9,6 @@ import (
 
 	"github.com/benbjohnson/clock"
 	logging "github.com/ipfs/go-log/v2"
-	config "github.com/ipfs/kubo/config"
-	"github.com/ipfs/kubo/core/node/helpers"
-	"github.com/ipfs/kubo/repo"
-
 	"github.com/libp2p/go-libp2p"
 	"github.com/libp2p/go-libp2p/core/network"
 	"github.com/libp2p/go-libp2p/core/peer"
@@ -21,21 +17,24 @@ import (
 	rcmgrObs "github.com/libp2p/go-libp2p/p2p/host/resource-manager/obs"
 	"github.com/multiformats/go-multiaddr"
 	"go.opencensus.io/stats/view"
-
 	"go.uber.org/fx"
+
+	config "github.com/ipfs/kubo/config"
+	"github.com/ipfs/kubo/core/node/helpers"
+	"github.com/ipfs/kubo/repo"
 )
 
 const NetLimitDefaultFilename = "limit.json"
 const NetLimitTraceFilename = "rcmgr.json.gz"
 
-var NoResourceMgrError = fmt.Errorf("missing ResourceMgr: make sure the daemon is running with Swarm.ResourceMgr.Enabled")
+var ErrNoResourceMgr = fmt.Errorf("missing ResourceMgr: make sure the daemon is running with Swarm.ResourceMgr.Enabled")
 
 func ResourceManager(cfg config.SwarmConfig) interface{} {
 	return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo) (network.ResourceManager, Libp2pOpts, error) {
 		var manager network.ResourceManager
 		var opts Libp2pOpts
 
-		enabled := cfg.ResourceMgr.Enabled.WithDefault(false)
+		enabled := cfg.ResourceMgr.Enabled.WithDefault(true)
 
 		//  ENV overrides Config (if present)
 		switch os.Getenv("LIBP2P_RCMGR") {
@@ -53,15 +52,22 @@ func ResourceManager(cfg config.SwarmConfig) interface{} {
 				return nil, opts, fmt.Errorf("opening IPFS_PATH: %w", err)
 			}
 
-			limits := adjustedDefaultLimits(cfg)
+			limitConfig, err := createDefaultLimitConfig(cfg)
+			if err != nil {
+				return nil, opts, err
+			}
 
+			// The logic for defaults and overriding with specified SwarmConfig.ResourceMgr.Limits
+			// is documented in docs/config.md.
+			// Any changes here should be reflected there.
 			if cfg.ResourceMgr.Limits != nil {
 				l := *cfg.ResourceMgr.Limits
-				l.Apply(limits)
-				limits = l
+				// This effectively overrides the computed default LimitConfig with any vlues from cfg.ResourceMgr.Limits
+				l.Apply(limitConfig)
+				limitConfig = l
 			}
 
-			limiter := rcmgr.NewFixedLimiter(limits)
+			limiter := rcmgr.NewFixedLimiter(limitConfig)
 
 			str, err := rcmgrObs.NewStatsTraceReporter()
 			if err != nil {
@@ -122,39 +128,59 @@ func ResourceManager(cfg config.SwarmConfig) interface{} {
 }
 
 type NetStatOut struct {
-	System    *network.ScopeStat           `json:",omitempty"`
-	Transient *network.ScopeStat           `json:",omitempty"`
-	Services  map[string]network.ScopeStat `json:",omitempty"`
-	Protocols map[string]network.ScopeStat `json:",omitempty"`
-	Peers     map[string]network.ScopeStat `json:",omitempty"`
+	System    *rcmgr.BaseLimit           `json:",omitempty"`
+	Transient *rcmgr.BaseLimit           `json:",omitempty"`
+	Services  map[string]rcmgr.BaseLimit `json:",omitempty"`
+	Protocols map[string]rcmgr.BaseLimit `json:",omitempty"`
+	Peers     map[string]rcmgr.BaseLimit `json:",omitempty"`
 }
 
-func NetStat(mgr network.ResourceManager, scope string) (NetStatOut, error) {
+func NetStat(mgr network.ResourceManager, scope string, percentage int) (NetStatOut, error) {
 	var err error
 	var result NetStatOut
 	switch {
 	case scope == "all":
 		rapi, ok := mgr.(rcmgr.ResourceManagerState)
 		if !ok { // NullResourceManager
-			return result, NoResourceMgrError
+			return result, ErrNoResourceMgr
+		}
+
+		limits, err := NetLimitAll(mgr)
+		if err != nil {
+			return result, err
 		}
 
 		stat := rapi.Stat()
-		result.System = &stat.System
-		result.Transient = &stat.Transient
+		result.System = compareLimits(scopeToLimit(&stat.System), limits.System, percentage)
+		result.Transient = compareLimits(scopeToLimit(&stat.Transient), limits.Transient, percentage)
 		if len(stat.Services) > 0 {
-			result.Services = stat.Services
+			result.Services = make(map[string]rcmgr.BaseLimit, len(stat.Services))
+			for srv, stat := range stat.Services {
+				ls := limits.Services[srv]
+				fstat := compareLimits(scopeToLimit(&stat), &ls, percentage)
+				if fstat != nil {
+					result.Services[srv] = *fstat
+				}
+			}
 		}
 		if len(stat.Protocols) > 0 {
-			result.Protocols = make(map[string]network.ScopeStat, len(stat.Protocols))
+			result.Protocols = make(map[string]rcmgr.BaseLimit, len(stat.Protocols))
 			for proto, stat := range stat.Protocols {
-				result.Protocols[string(proto)] = stat
+				ls := limits.Protocols[string(proto)]
+				fstat := compareLimits(scopeToLimit(&stat), &ls, percentage)
+				if fstat != nil {
+					result.Protocols[string(proto)] = *fstat
+				}
 			}
 		}
 		if len(stat.Peers) > 0 {
-			result.Peers = make(map[string]network.ScopeStat, len(stat.Peers))
+			result.Peers = make(map[string]rcmgr.BaseLimit, len(stat.Peers))
 			for p, stat := range stat.Peers {
-				result.Peers[p.Pretty()] = stat
+				ls := limits.Peers[p.Pretty()]
+				fstat := compareLimits(scopeToLimit(&stat), &ls, percentage)
+				if fstat != nil {
+					result.Peers[p.Pretty()] = *fstat
+				}
 			}
 		}
 
@@ -163,7 +189,7 @@ func NetStat(mgr network.ResourceManager, scope string) (NetStatOut, error) {
 	case scope == config.ResourceMgrSystemScope:
 		err = mgr.ViewSystem(func(s network.ResourceScope) error {
 			stat := s.Stat()
-			result.System = &stat
+			result.System = scopeToLimit(&stat)
 			return nil
 		})
 		return result, err
@@ -171,7 +197,7 @@ func NetStat(mgr network.ResourceManager, scope string) (NetStatOut, error) {
 	case scope == config.ResourceMgrTransientScope:
 		err = mgr.ViewTransient(func(s network.ResourceScope) error {
 			stat := s.Stat()
-			result.Transient = &stat
+			result.Transient = scopeToLimit(&stat)
 			return nil
 		})
 		return result, err
@@ -180,8 +206,8 @@ func NetStat(mgr network.ResourceManager, scope string) (NetStatOut, error) {
 		svc := strings.TrimPrefix(scope, config.ResourceMgrServiceScopePrefix)
 		err = mgr.ViewService(svc, func(s network.ServiceScope) error {
 			stat := s.Stat()
-			result.Services = map[string]network.ScopeStat{
-				svc: stat,
+			result.Services = map[string]rcmgr.BaseLimit{
+				svc: *scopeToLimit(&stat),
 			}
 			return nil
 		})
@@ -191,8 +217,8 @@ func NetStat(mgr network.ResourceManager, scope string) (NetStatOut, error) {
 		proto := strings.TrimPrefix(scope, config.ResourceMgrProtocolScopePrefix)
 		err = mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error {
 			stat := s.Stat()
-			result.Protocols = map[string]network.ScopeStat{
-				proto: stat,
+			result.Protocols = map[string]rcmgr.BaseLimit{
+				proto: *scopeToLimit(&stat),
 			}
 			return nil
 		})
@@ -206,8 +232,8 @@ func NetStat(mgr network.ResourceManager, scope string) (NetStatOut, error) {
 		}
 		err = mgr.ViewPeer(pid, func(s network.PeerScope) error {
 			stat := s.Stat()
-			result.Peers = map[string]network.ScopeStat{
-				p: stat,
+			result.Peers = map[string]rcmgr.BaseLimit{
+				p: *scopeToLimit(&stat),
 			}
 			return nil
 		})
@@ -218,12 +244,136 @@ func NetStat(mgr network.ResourceManager, scope string) (NetStatOut, error) {
 	}
 }
 
+var scopes = []string{
+	config.ResourceMgrSystemScope,
+	config.ResourceMgrTransientScope,
+	config.ResourceMgrServiceScopePrefix,
+	config.ResourceMgrProtocolScopePrefix,
+	config.ResourceMgrPeerScopePrefix,
+}
+
+func scopeToLimit(s *network.ScopeStat) *rcmgr.BaseLimit {
+	return &rcmgr.BaseLimit{
+		Streams:         s.NumStreamsInbound + s.NumStreamsOutbound,
+		StreamsInbound:  s.NumStreamsInbound,
+		StreamsOutbound: s.NumStreamsOutbound,
+		Conns:           s.NumConnsInbound + s.NumConnsOutbound,
+		ConnsInbound:    s.NumConnsInbound,
+		ConnsOutbound:   s.NumConnsOutbound,
+		FD:              s.NumFD,
+		Memory:          s.Memory,
+	}
+}
+
+// compareLimits compares stat and limit.
+// If any of the stats value are equals or above the specified percentage,
+// stat object is returned.
+func compareLimits(stat, limit *rcmgr.BaseLimit, percentage int) *rcmgr.BaseLimit {
+	if stat == nil || limit == nil {
+		return nil
+	}
+	if abovePercentage(int(stat.Memory), int(limit.Memory), percentage) {
+		return stat
+	}
+	if abovePercentage(stat.ConnsInbound, limit.ConnsInbound, percentage) {
+		return stat
+	}
+	if abovePercentage(stat.ConnsOutbound, limit.ConnsOutbound, percentage) {
+		return stat
+	}
+	if abovePercentage(stat.Conns, limit.Conns, percentage) {
+		return stat
+	}
+	if abovePercentage(stat.FD, limit.FD, percentage) {
+		return stat
+	}
+	if abovePercentage(stat.StreamsInbound, limit.StreamsInbound, percentage) {
+		return stat
+	}
+	if abovePercentage(stat.StreamsOutbound, limit.StreamsOutbound, percentage) {
+		return stat
+	}
+	if abovePercentage(stat.Streams, limit.Streams, percentage) {
+		return stat
+	}
+
+	return nil
+}
+
+func abovePercentage(v1, v2, percentage int) bool {
+	if percentage == 0 {
+		return true
+	}
+
+	if v2 == 0 {
+		return false
+	}
+
+	return int((v1/v2))*100 >= percentage
+}
+
+func NetLimitAll(mgr network.ResourceManager) (*NetStatOut, error) {
+	var result = &NetStatOut{}
+	lister, ok := mgr.(rcmgr.ResourceManagerState)
+	if !ok { // NullResourceManager
+		return result, ErrNoResourceMgr
+	}
+
+	for _, s := range scopes {
+		switch s {
+		case config.ResourceMgrSystemScope:
+			s, err := NetLimit(mgr, config.ResourceMgrSystemScope)
+			if err != nil {
+				return nil, err
+			}
+			result.System = &s
+		case config.ResourceMgrTransientScope:
+			s, err := NetLimit(mgr, config.ResourceMgrSystemScope)
+			if err != nil {
+				return nil, err
+			}
+			result.Transient = &s
+		case config.ResourceMgrServiceScopePrefix:
+			result.Services = make(map[string]rcmgr.BaseLimit)
+			for _, serv := range lister.ListServices() {
+				s, err := NetLimit(mgr, config.ResourceMgrServiceScopePrefix+serv)
+				if err != nil {
+					return nil, err
+				}
+				result.Services[serv] = s
+			}
+		case config.ResourceMgrProtocolScopePrefix:
+			result.Protocols = make(map[string]rcmgr.BaseLimit)
+			for _, prot := range lister.ListProtocols() {
+				ps := string(prot)
+				s, err := NetLimit(mgr, config.ResourceMgrProtocolScopePrefix+ps)
+				if err != nil {
+					return nil, err
+				}
+				result.Protocols[ps] = s
+			}
+		case config.ResourceMgrPeerScopePrefix:
+			result.Peers = make(map[string]rcmgr.BaseLimit)
+			for _, peer := range lister.ListPeers() {
+				ps := peer.Pretty()
+				s, err := NetLimit(mgr, config.ResourceMgrPeerScopePrefix+ps)
+				if err != nil {
+					return nil, err
+				}
+				result.Peers[ps] = s
+			}
+		}
+	}
+
+	return result, nil
+}
+
 func NetLimit(mgr network.ResourceManager, scope string) (rcmgr.BaseLimit, error) {
 	var result rcmgr.BaseLimit
 	getLimit := func(s network.ResourceScope) error {
 		limiter, ok := s.(rcmgr.ResourceScopeLimiter)
 		if !ok { // NullResourceManager
-			return NoResourceMgrError
+			return ErrNoResourceMgr
 		}
 		limit := limiter.Limit()
 		switch l := limit.(type) {
@@ -271,7 +421,7 @@ func NetSetLimit(mgr network.ResourceManager, repo repo.Repo, scope string, limi
 	setLimit := func(s network.ResourceScope) error {
 		limiter, ok := s.(rcmgr.ResourceScopeLimiter)
 		if !ok { // NullResourceManager
-			return NoResourceMgrError
+			return ErrNoResourceMgr
 		}
 
 		limiter.SetLimit(&limit)
@@ -347,3 +497,104 @@ func NetSetLimit(mgr network.ResourceManager, repo repo.Repo, scope string, limi
 
 	return nil
 }
+
+// NetResetLimit resets ResourceManager limits to defaults. The limits take effect immediately, and are also persisted to the repo config.
+func NetResetLimit(mgr network.ResourceManager, repo repo.Repo, scope string) (rcmgr.BaseLimit, error) {
+	var result rcmgr.BaseLimit
+
+	setLimit := func(s network.ResourceScope, l rcmgr.Limit) error {
+		limiter, ok := s.(rcmgr.ResourceScopeLimiter)
+		if !ok {
+			return ErrNoResourceMgr
+		}
+
+		limiter.SetLimit(l)
+		return nil
+	}
+
+	cfg, err := repo.Config()
+	if err != nil {
+		return result, fmt.Errorf("reading config to reset limit: %w", err)
+	}
+
+	defaults, err := createDefaultLimitConfig(cfg.Swarm)
+	if err != nil {
+		return result, fmt.Errorf("creating default limit config: %w", err)
+	}
+
+	if cfg.Swarm.ResourceMgr.Limits == nil {
+		cfg.Swarm.ResourceMgr.Limits = &rcmgr.LimitConfig{}
+	}
+	configLimits := cfg.Swarm.ResourceMgr.Limits
+
+	var setConfigFunc func() rcmgr.BaseLimit
+	switch {
+	case scope == config.ResourceMgrSystemScope:
+		err = mgr.ViewSystem(func(s network.ResourceScope) error { return setLimit(s, &defaults.System) })
+		setConfigFunc = func() rcmgr.BaseLimit {
+			configLimits.System = defaults.System
+			return defaults.System
+		}
+	case scope == config.ResourceMgrTransientScope:
+		err = mgr.ViewTransient(func(s network.ResourceScope) error { return setLimit(s, &defaults.Transient) })
+		setConfigFunc = func() rcmgr.BaseLimit {
+			configLimits.Transient = defaults.Transient
+			return defaults.Transient
+		}
+	case strings.HasPrefix(scope, config.ResourceMgrServiceScopePrefix):
+		svc := strings.TrimPrefix(scope, config.ResourceMgrServiceScopePrefix)
+
+		err = mgr.ViewService(svc, func(s network.ServiceScope) error { return setLimit(s, &defaults.ServiceDefault) })
+		setConfigFunc = func() rcmgr.BaseLimit {
+			if configLimits.Service == nil {
+				configLimits.Service = map[string]rcmgr.BaseLimit{}
+			}
+			configLimits.Service[svc] = defaults.ServiceDefault
+			return defaults.ServiceDefault
+		}
+	case strings.HasPrefix(scope, config.ResourceMgrProtocolScopePrefix):
+		proto := strings.TrimPrefix(scope, config.ResourceMgrProtocolScopePrefix)
+
+		err = mgr.ViewProtocol(protocol.ID(proto), func(s network.ProtocolScope) error { return setLimit(s, &defaults.ProtocolDefault) })
+		setConfigFunc = func() rcmgr.BaseLimit {
+			if configLimits.Protocol == nil {
+				configLimits.Protocol = map[protocol.ID]rcmgr.BaseLimit{}
+			}
+			configLimits.Protocol[protocol.ID(proto)] = defaults.ProtocolDefault
+
+			return defaults.ProtocolDefault
+		}
+	case strings.HasPrefix(scope, config.ResourceMgrPeerScopePrefix):
+		p := strings.TrimPrefix(scope, config.ResourceMgrPeerScopePrefix)
+
+		var pid peer.ID
+		pid, err = peer.Decode(p)
+		if err != nil {
+			return result, fmt.Errorf("invalid peer ID: %q: %w", p, err)
+		}
+
+		err = mgr.ViewPeer(pid, func(s network.PeerScope) error { return setLimit(s, &defaults.PeerDefault) })
+		setConfigFunc = func() rcmgr.BaseLimit {
+			if configLimits.Peer == nil {
+				configLimits.Peer = map[peer.ID]rcmgr.BaseLimit{}
+			}
+			configLimits.Peer[pid] = defaults.PeerDefault
+
+			return defaults.PeerDefault
+		}
+	default:
+		return result, fmt.Errorf("invalid scope %q", scope)
+	}
+
+	if err != nil {
+		return result, fmt.Errorf("resetting new limits on resource manager: %w", err)
+	}
+
+	result = setConfigFunc()
+
+	if err := repo.SetConfig(cfg); err != nil {
+		return result, fmt.Errorf("writing new limits to repo config: %w", err)
+	}
+
+	return result, nil
+}
diff --git a/core/node/libp2p/rcmgr_defaults.go b/core/node/libp2p/rcmgr_defaults.go
index e09a598450e22eb04e697112bb582e9586d56945..3cee4c50c4749aa8ff8d1208602c790aaac5fdba 100644
--- a/core/node/libp2p/rcmgr_defaults.go
+++ b/core/node/libp2p/rcmgr_defaults.go
@@ -1,833 +1,179 @@
 package libp2p
 
 import (
-	"encoding/json"
-	"fmt"
-	"math/bits"
-	"os"
-	"strings"
+	"math"
 
-	"github.com/ipfs/kubo/config"
+	"github.com/dustin/go-humanize"
 	"github.com/libp2p/go-libp2p"
 	rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager"
+	"github.com/pbnjay/memory"
 
-	"github.com/wI2L/jsondiff"
+	"github.com/ipfs/kubo/config"
+	"github.com/ipfs/kubo/core/node/libp2p/fd"
 )
 
-// This file defines implicit limit defaults used when Swarm.ResourceMgr.Enabled
-
-// adjustedDefaultLimits allows for tweaking defaults based on external factors,
-// such as values in Swarm.ConnMgr.HiWater config.
-func adjustedDefaultLimits(cfg config.SwarmConfig) rcmgr.LimitConfig {
-	// Run checks to avoid introducing regressions
-	if os.Getenv("IPFS_CHECK_RCMGR_DEFAULTS") != "" {
-		// FIXME: Broken. Being tracked in https://github.com/ipfs/go-ipfs/issues/8949.
-		checkImplicitDefaults()
-	}
-	defaultLimits := rcmgr.DefaultLimits
-	libp2p.SetDefaultServiceLimits(&defaultLimits)
-
-	// Adjust limits
-	// (based on https://github.com/filecoin-project/lotus/pull/8318/files)
-	// - if Swarm.ConnMgr.HighWater is too high, adjust Conn/FD/Stream limits
-
-	// Outbound conns and FDs are set very high to allow for the accelerated DHT client to (re)load its routing table.
-	// Currently it doesn't gracefully handle RM throttling--once it does we can lower these.
-	// High outbound conn limits are considered less of a DoS risk than high inbound conn limits.
-	// Also note that, due to the behavior of the accelerated DHT client, we don't need many streams, just conns.
-	if minOutbound := 65536; defaultLimits.SystemBaseLimit.ConnsOutbound < minOutbound {
-		defaultLimits.SystemBaseLimit.ConnsOutbound = minOutbound
-	}
-	if minFD := 4096; defaultLimits.SystemBaseLimit.FD < minFD {
-		defaultLimits.SystemBaseLimit.FD = minFD
-	}
-	defaultLimitConfig := defaultLimits.AutoScale()
-
-	// Do we need to adjust due to Swarm.ConnMgr.HighWater?
-	if cfg.ConnMgr.Type == "basic" {
-		maxconns := cfg.ConnMgr.HighWater
-		if 2*maxconns > defaultLimitConfig.System.ConnsInbound {
-			// adjust conns to 2x to allow for two conns per peer (TCP+QUIC)
-			defaultLimitConfig.System.ConnsInbound = logScale(2 * maxconns)
-			defaultLimitConfig.System.ConnsOutbound = logScale(2 * maxconns)
-			defaultLimitConfig.System.Conns = logScale(4 * maxconns)
-
-			defaultLimitConfig.System.StreamsInbound = logScale(16 * maxconns)
-			defaultLimitConfig.System.StreamsOutbound = logScale(64 * maxconns)
-			defaultLimitConfig.System.Streams = logScale(64 * maxconns)
-
-			if 2*maxconns > defaultLimitConfig.System.FD {
-				defaultLimitConfig.System.FD = logScale(2 * maxconns)
-			}
-
-			defaultLimitConfig.ServiceDefault.StreamsInbound = logScale(8 * maxconns)
-			defaultLimitConfig.ServiceDefault.StreamsOutbound = logScale(32 * maxconns)
-			defaultLimitConfig.ServiceDefault.Streams = logScale(32 * maxconns)
-
-			defaultLimitConfig.ProtocolDefault.StreamsInbound = logScale(8 * maxconns)
-			defaultLimitConfig.ProtocolDefault.StreamsOutbound = logScale(32 * maxconns)
-			defaultLimitConfig.ProtocolDefault.Streams = logScale(32 * maxconns)
-
-			log.Info("adjusted default resource manager limits")
-		}
-
-	}
-
-	return defaultLimitConfig
+// We are doing some magic when parsing config files (we are using a map[string]interface{} to compare config files).
+// When you don't have a type the JSON Parse function cast numbers to float64 by default,
+// losing precision when writing the final number. So if we use math.MaxInt as our infinite number,
+// after writing the config file we will have 9223372036854776000 instead of 9223372036854775807,
+// making the parsing process fail.
+const bigEnough = math.MaxInt / 2
+
+var infiniteBaseLimit = rcmgr.BaseLimit{
+	Streams:         bigEnough,
+	StreamsInbound:  bigEnough,
+	StreamsOutbound: bigEnough,
+	Conns:           bigEnough,
+	ConnsInbound:    bigEnough,
+	ConnsOutbound:   bigEnough,
+	FD:              bigEnough,
+	Memory:          bigEnough,
 }
 
-func logScale(val int) int {
-	bitlen := bits.Len(uint(val))
-	return 1 << bitlen
+var noLimitIncrease = rcmgr.BaseLimitIncrease{
+	ConnsInbound:    0,
+	ConnsOutbound:   0,
+	Conns:           0,
+	StreamsInbound:  0,
+	StreamsOutbound: 0,
+	Streams:         0,
+	Memory:          0,
+	FDFraction:      0,
 }
 
-// checkImplicitDefaults compares libp2p defaults agains expected ones
-// and panics when they don't match. This ensures we are not surprised
-// by silent default limit changes when we update go-libp2p dependencies.
-func checkImplicitDefaults() {
-	ok := true
+// This file defines implicit limit defaults used when Swarm.ResourceMgr.Enabled
 
-	// Check 1: did go-libp2p-resource-manager's DefaultLimits change?
-	defaults, err := json.Marshal(rcmgr.DefaultLimits)
-	if err != nil {
-		log.Fatal(err)
-	}
-	changes, err := jsonDiff([]byte(expectedDefaultLimits), defaults)
+// createDefaultLimitConfig creates LimitConfig to pass to libp2p's resource manager.
+// The defaults follow the documentation in docs/config.md.
+// Any changes in the logic here should be reflected there.
+func createDefaultLimitConfig(cfg config.SwarmConfig) (rcmgr.LimitConfig, error) {
+	maxMemoryDefaultString := humanize.Bytes(uint64(memory.TotalMemory()) / 8)
+	maxMemoryString := cfg.ResourceMgr.MaxMemory.WithDefault(maxMemoryDefaultString)
+	maxMemory, err := humanize.ParseBytes(maxMemoryString)
 	if err != nil {
-		log.Fatal(err)
+		return rcmgr.LimitConfig{}, err
 	}
-	if len(changes) > 0 {
-		ok = false
-		log.Errorf("===> OOF! go-libp2p-resource-manager changed DefaultLimits\n"+
-			"=> changes ('test' represents the old value):\n%s\n"+
-			"=> go-libp2p-resource-manager DefaultLimits update needs a review:\n"+
-			"Please inspect if changes impact go-ipfs users, and update expectedDefaultLimits in rcmgr_defaults.go to remove this message",
-			strings.Join(changes, "\n"),
-		)
+
+	numFD := cfg.ResourceMgr.MaxFileDescriptors.WithDefault(int64(fd.GetNumFDs()) / 2)
+
+	scalingLimitConfig := rcmgr.ScalingLimitConfig{
+		SystemBaseLimit: rcmgr.BaseLimit{
+			Memory: int64(maxMemory),
+			FD:     int(numFD),
+
+			// By default, we just limit connections on the inbound side.
+			Conns:         bigEnough,
+			ConnsInbound:  rcmgr.DefaultLimits.SystemBaseLimit.ConnsInbound, // same as libp2p default
+			ConnsOutbound: bigEnough,
+
+			// We limit streams since they not only take up memory and CPU.
+			// The Memory limit protects us on the memory side,
+			// but a StreamsInbound limit helps protect against unbound CPU consumption from stream processing.
+			Streams:         bigEnough,
+			StreamsInbound:  rcmgr.DefaultLimits.SystemBaseLimit.StreamsInbound,
+			StreamsOutbound: bigEnough,
+		},
+		// Most limits don't see an increase because they're already infinite/bigEnough or at their max value.
+		// The values that should scale based on the amount of memory allocated to libp2p need to increase accordingly.
+		SystemLimitIncrease: rcmgr.BaseLimitIncrease{
+			Memory:     rcmgr.DefaultLimits.SystemLimitIncrease.Memory,
+			FDFraction: rcmgr.DefaultLimits.SystemLimitIncrease.FDFraction,
+
+			Conns:         0,
+			ConnsInbound:  rcmgr.DefaultLimits.SystemLimitIncrease.ConnsInbound,
+			ConnsOutbound: 0,
+
+			Streams:         0,
+			StreamsInbound:  rcmgr.DefaultLimits.SystemLimitIncrease.StreamsInbound,
+			StreamsOutbound: 0,
+		},
+
+		TransientBaseLimit: rcmgr.BaseLimit{
+			Memory: rcmgr.DefaultLimits.TransientBaseLimit.Memory,
+			FD:     rcmgr.DefaultLimits.TransientBaseLimit.FD,
+
+			Conns:         bigEnough,
+			ConnsInbound:  rcmgr.DefaultLimits.TransientBaseLimit.ConnsInbound,
+			ConnsOutbound: bigEnough,
+
+			Streams:         bigEnough,
+			StreamsInbound:  rcmgr.DefaultLimits.TransientBaseLimit.StreamsInbound,
+			StreamsOutbound: bigEnough,
+		},
+
+		TransientLimitIncrease: rcmgr.BaseLimitIncrease{
+			Memory:     rcmgr.DefaultLimits.TransientLimitIncrease.Memory,
+			FDFraction: rcmgr.DefaultLimits.TransientLimitIncrease.FDFraction,
+
+			Conns:         0,
+			ConnsInbound:  rcmgr.DefaultLimits.TransientLimitIncrease.ConnsInbound,
+			ConnsOutbound: 0,
+
+			Streams:         0,
+			StreamsInbound:  rcmgr.DefaultLimits.TransientLimitIncrease.StreamsInbound,
+			StreamsOutbound: 0,
+		},
+
+		// Lets get out of the way of the allow list functionality.
+		// If someone specified "Swarm.ResourceMgr.Allowlist" we should let it go through.
+		AllowlistedSystemBaseLimit:     infiniteBaseLimit,
+		AllowlistedSystemLimitIncrease: noLimitIncrease,
+
+		AllowlistedTransientBaseLimit:     infiniteBaseLimit,
+		AllowlistedTransientLimitIncrease: noLimitIncrease,
+
+		// Keep it simple by not having Service, ServicePeer, Protocol, ProtocolPeer, Conn, or Stream limits.
+		ServiceBaseLimit:     infiniteBaseLimit,
+		ServiceLimitIncrease: noLimitIncrease,
+
+		ServicePeerBaseLimit:     infiniteBaseLimit,
+		ServicePeerLimitIncrease: noLimitIncrease,
+
+		ProtocolBaseLimit:     infiniteBaseLimit,
+		ProtocolLimitIncrease: noLimitIncrease,
+
+		ProtocolPeerBaseLimit:     infiniteBaseLimit,
+		ProtocolPeerLimitIncrease: noLimitIncrease,
+
+		ConnBaseLimit:     infiniteBaseLimit,
+		ConnLimitIncrease: noLimitIncrease,
+
+		StreamBaseLimit:     infiniteBaseLimit,
+		StreamLimitIncrease: noLimitIncrease,
+
+		// Limit the resources consumed by a peer.
+		// This doesn't protect us against intentional DoS attacks since an attacker can easily spin up multiple peers.
+		// We specify this limit against unintentional DoS attacks (e.g., a peer has a bug and is sending too much traffic intentionally).
+		// In that case we want to keep that peer's resource consumption contained.
+		// To keep this simple, we only constrain inbound connections and streams.
+		PeerBaseLimit: rcmgr.BaseLimit{
+			Memory:          bigEnough,
+			FD:              bigEnough,
+			Conns:           bigEnough,
+			ConnsInbound:    rcmgr.DefaultLimits.PeerBaseLimit.ConnsInbound,
+			ConnsOutbound:   bigEnough,
+			Streams:         bigEnough,
+			StreamsInbound:  rcmgr.DefaultLimits.PeerBaseLimit.StreamsInbound,
+			StreamsOutbound: bigEnough,
+		},
+		// Most limits don't see an increase because they're already infinite/bigEnough.
+		// The values that should scale based on the amount of memory allocated to libp2p need to increase accordingly.
+		PeerLimitIncrease: rcmgr.BaseLimitIncrease{
+			Memory:          0,
+			FDFraction:      0,
+			Conns:           0,
+			ConnsInbound:    rcmgr.DefaultLimits.PeerLimitIncrease.ConnsInbound,
+			ConnsOutbound:   0,
+			Streams:         0,
+			StreamsInbound:  rcmgr.DefaultLimits.PeerLimitIncrease.StreamsInbound,
+			StreamsOutbound: 0,
+		},
 	}
 
-	// Check 2: did go-libp2p's SetDefaultServiceLimits change?
-	// We compare the baseline (min specs), and check if we went down in any limits.
-	l := rcmgr.DefaultLimits
-	libp2p.SetDefaultServiceLimits(&l)
-	limits := l.AutoScale()
-	testLimiter := rcmgr.NewFixedLimiter(limits)
+	// Whatever limits libp2p has specifically tuned for its protocols/services we'll apply.
+	libp2p.SetDefaultServiceLimits(&scalingLimitConfig)
 
-	serviceDefaults, err := json.Marshal(testLimiter)
-	if err != nil {
-		log.Fatal(err)
-	}
-	changes, err = jsonDiff([]byte(expectedDefaultServiceLimits), serviceDefaults)
-	if err != nil {
-		log.Fatal(err)
-	}
-	if len(changes) > 0 {
-		oldState := map[string]int{}
-		type Op struct {
-			Op    string
-			Path  string
-			Value int
-		}
-		for _, changeStr := range changes {
-			change := Op{}
-			err := json.Unmarshal([]byte(changeStr), &change)
-			if err != nil {
-				continue
-			}
-			if change.Op == "test" {
-				oldState[change.Path] = change.Value
-			}
-		}
-
-		for _, changeStr := range changes {
-			change := Op{}
-			err := json.Unmarshal([]byte(changeStr), &change)
-			if err != nil {
-				continue
-			}
-			if change.Op == "replace" {
-				oldVal, okFound := oldState[change.Path]
-				if okFound && oldVal > change.Value {
-					ok = false
-					fmt.Printf("reduced value for %s. Old: %v; new: %v\n", change.Path, oldVal, change.Value)
-				}
-			}
-		}
-
-		if !ok {
-			log.Errorf("===> OOF! go-libp2p changed DefaultServiceLimits\n" +
-				"=> See the aboce reduced values for info.\n" +
-				"=> go-libp2p SetDefaultServiceLimits update needs a review:\n" +
-				"Please inspect if changes impact go-ipfs users, and update expectedDefaultServiceLimits in rcmgr_defaults.go to remove this message",
-			)
-		}
-	}
-	if !ok {
-		log.Fatal("daemon will refuse to run with the resource manager until this is resolved")
-	}
-}
+	defaultLimitConfig := scalingLimitConfig.Scale(int64(maxMemory), int(numFD))
 
-// jsonDiff compares two strings and returns diff in JSON Patch format
-func jsonDiff(old []byte, updated []byte) ([]string, error) {
-	// generate 'invertible' patch which includes old values as "test" op
-	patch, err := jsondiff.CompareJSONOpts(old, updated, jsondiff.Invertible())
-	changes := make([]string, len(patch))
-	if err != nil {
-		return changes, err
-	}
-	for i, op := range patch {
-		changes[i] = fmt.Sprintf("  %s", op)
-	}
-	return changes, nil
+	return defaultLimitConfig, nil
 }
-
-// https://github.com/libp2p/go-libp2p/blob/v0.22.0/p2p/host/resource-manager/limit_defaults.go#L343
-const expectedDefaultLimits = `{
-  "SystemBaseLimit": {
-    "Streams": 2048,
-    "StreamsInbound": 1024,
-    "StreamsOutbound": 2048,
-    "Conns": 128,
-    "ConnsInbound": 64,
-    "ConnsOutbound": 128,
-    "FD": 256,
-    "Memory": 134217728
-  },
-  "SystemLimitIncrease": {
-    "Streams": 2048,
-    "StreamsInbound": 1024,
-    "StreamsOutbound": 2048,
-    "Conns": 128,
-    "ConnsInbound": 64,
-    "ConnsOutbound": 128,
-    "Memory": 1073741824,
-    "FDFraction": 1
-  },
-  "TransientBaseLimit": {
-    "Streams": 256,
-    "StreamsInbound": 128,
-    "StreamsOutbound": 256,
-    "Conns": 64,
-    "ConnsInbound": 32,
-    "ConnsOutbound": 64,
-    "FD": 64,
-    "Memory": 33554432
-  },
-  "TransientLimitIncrease": {
-    "Streams": 256,
-    "StreamsInbound": 128,
-    "StreamsOutbound": 256,
-    "Conns": 32,
-    "ConnsInbound": 16,
-    "ConnsOutbound": 32,
-    "Memory": 134217728,
-    "FDFraction": 0.25
-  },
-  "AllowlistedSystemBaseLimit": {
-    "Streams": 2048,
-    "StreamsInbound": 1024,
-    "StreamsOutbound": 2048,
-    "Conns": 128,
-    "ConnsInbound": 64,
-    "ConnsOutbound": 128,
-    "FD": 256,
-    "Memory": 134217728
-  },
-  "AllowlistedSystemLimitIncrease": {
-    "Streams": 2048,
-    "StreamsInbound": 1024,
-    "StreamsOutbound": 2048,
-    "Conns": 128,
-    "ConnsInbound": 64,
-    "ConnsOutbound": 128,
-    "Memory": 1073741824,
-    "FDFraction": 1
-  },
-  "AllowlistedTransientBaseLimit": {
-    "Streams": 256,
-    "StreamsInbound": 128,
-    "StreamsOutbound": 256,
-    "Conns": 64,
-    "ConnsInbound": 32,
-    "ConnsOutbound": 64,
-    "FD": 64,
-    "Memory": 33554432
-  },
-  "AllowlistedTransientLimitIncrease": {
-    "Streams": 256,
-    "StreamsInbound": 128,
-    "StreamsOutbound": 256,
-    "Conns": 32,
-    "ConnsInbound": 16,
-    "ConnsOutbound": 32,
-    "Memory": 134217728,
-    "FDFraction": 0.25
-  },
-  "ServiceBaseLimit": {
-    "Streams": 4096,
-    "StreamsInbound": 1024,
-    "StreamsOutbound": 4096,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "FD": 0,
-    "Memory": 67108864
-  },
-  "ServiceLimitIncrease": {
-    "Streams": 2048,
-    "StreamsInbound": 512,
-    "StreamsOutbound": 2048,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "Memory": 134217728,
-    "FDFraction": 0
-  },
-  "ServiceLimits": null,
-  "ServicePeerBaseLimit": {
-    "Streams": 256,
-    "StreamsInbound": 128,
-    "StreamsOutbound": 256,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "FD": 0,
-    "Memory": 16777216
-  },
-  "ServicePeerLimitIncrease": {
-    "Streams": 8,
-    "StreamsInbound": 4,
-    "StreamsOutbound": 8,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "Memory": 4194304,
-    "FDFraction": 0
-  },
-  "ServicePeerLimits": null,
-  "ProtocolBaseLimit": {
-    "Streams": 2048,
-    "StreamsInbound": 512,
-    "StreamsOutbound": 2048,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "FD": 0,
-    "Memory": 67108864
-  },
-  "ProtocolLimitIncrease": {
-    "Streams": 512,
-    "StreamsInbound": 256,
-    "StreamsOutbound": 512,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "Memory": 171966464,
-    "FDFraction": 0
-  },
-  "ProtocolLimits": null,
-  "ProtocolPeerBaseLimit": {
-    "Streams": 256,
-    "StreamsInbound": 64,
-    "StreamsOutbound": 128,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "FD": 0,
-    "Memory": 16777216
-  },
-  "ProtocolPeerLimitIncrease": {
-    "Streams": 16,
-    "StreamsInbound": 4,
-    "StreamsOutbound": 8,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "Memory": 4,
-    "FDFraction": 0
-  },
-  "ProtocolPeerLimits": null,
-  "PeerBaseLimit": {
-    "Streams": 512,
-    "StreamsInbound": 256,
-    "StreamsOutbound": 512,
-    "Conns": 8,
-    "ConnsInbound": 4,
-    "ConnsOutbound": 8,
-    "FD": 4,
-    "Memory": 67108864
-  },
-  "PeerLimitIncrease": {
-    "Streams": 256,
-    "StreamsInbound": 128,
-    "StreamsOutbound": 256,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "Memory": 134217728,
-    "FDFraction": 0.015625
-  },
-  "PeerLimits": null,
-  "ConnBaseLimit": {
-    "Streams": 0,
-    "StreamsInbound": 0,
-    "StreamsOutbound": 0,
-    "Conns": 1,
-    "ConnsInbound": 1,
-    "ConnsOutbound": 1,
-    "FD": 1,
-    "Memory": 33554432
-  },
-  "ConnLimitIncrease": {
-    "Streams": 0,
-    "StreamsInbound": 0,
-    "StreamsOutbound": 0,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "Memory": 0,
-    "FDFraction": 0
-  },
-  "StreamBaseLimit": {
-    "Streams": 1,
-    "StreamsInbound": 1,
-    "StreamsOutbound": 1,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "FD": 0,
-    "Memory": 16777216
-  },
-  "StreamLimitIncrease": {
-    "Streams": 0,
-    "StreamsInbound": 0,
-    "StreamsOutbound": 0,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "Memory": 0,
-    "FDFraction": 0
-  }
-}`
-
-// Generated from the default limits and scaling to 0 (base limit).
-const expectedDefaultServiceLimits = `{
-  "System": {
-    "Streams": 2048,
-    "StreamsInbound": 1024,
-    "StreamsOutbound": 2048,
-    "Conns": 128,
-    "ConnsInbound": 64,
-    "ConnsOutbound": 128,
-    "FD": 256,
-    "Memory": 134217728
-  },
-  "Transient": {
-    "Streams": 256,
-    "StreamsInbound": 128,
-    "StreamsOutbound": 256,
-    "Conns": 64,
-    "ConnsInbound": 32,
-    "ConnsOutbound": 64,
-    "FD": 64,
-    "Memory": 33554432
-  },
-  "AllowlistedSystem": {
-    "Streams": 2048,
-    "StreamsInbound": 1024,
-    "StreamsOutbound": 2048,
-    "Conns": 128,
-    "ConnsInbound": 64,
-    "ConnsOutbound": 128,
-    "FD": 256,
-    "Memory": 134217728
-  },
-  "AllowlistedTransient": {
-    "Streams": 256,
-    "StreamsInbound": 128,
-    "StreamsOutbound": 256,
-    "Conns": 64,
-    "ConnsInbound": 32,
-    "ConnsOutbound": 64,
-    "FD": 64,
-    "Memory": 33554432
-  },
-  "ServiceDefault": {
-    "Streams": 4096,
-    "StreamsInbound": 1024,
-    "StreamsOutbound": 4096,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "FD": 0,
-    "Memory": 67108864
-  },
-  "Service": {
-    "libp2p.autonat": {
-      "Streams": 64,
-      "StreamsInbound": 64,
-      "StreamsOutbound": 64,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 4194304
-    },
-    "libp2p.holepunch": {
-      "Streams": 64,
-      "StreamsInbound": 32,
-      "StreamsOutbound": 32,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 4194304
-    },
-    "libp2p.identify": {
-      "Streams": 128,
-      "StreamsInbound": 64,
-      "StreamsOutbound": 64,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 4194304
-    },
-    "libp2p.ping": {
-      "Streams": 64,
-      "StreamsInbound": 64,
-      "StreamsOutbound": 64,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 4194304
-    },
-    "libp2p.relay/v1": {
-      "Streams": 256,
-      "StreamsInbound": 256,
-      "StreamsOutbound": 256,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 16777216
-    },
-    "libp2p.relay/v2": {
-      "Streams": 256,
-      "StreamsInbound": 256,
-      "StreamsOutbound": 256,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 16777216
-    }
-  },
-  "ServicePeerDefault": {
-    "Streams": 256,
-    "StreamsInbound": 128,
-    "StreamsOutbound": 256,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "FD": 0,
-    "Memory": 16777216
-  },
-  "ServicePeer": {
-    "libp2p.autonat": {
-      "Streams": 2,
-      "StreamsInbound": 2,
-      "StreamsOutbound": 2,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 1048576
-    },
-    "libp2p.holepunch": {
-      "Streams": 2,
-      "StreamsInbound": 2,
-      "StreamsOutbound": 2,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 1048576
-    },
-    "libp2p.identify": {
-      "Streams": 32,
-      "StreamsInbound": 16,
-      "StreamsOutbound": 16,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 1048576
-    },
-    "libp2p.ping": {
-      "Streams": 4,
-      "StreamsInbound": 2,
-      "StreamsOutbound": 3,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 8590458880
-    },
-    "libp2p.relay/v1": {
-      "Streams": 64,
-      "StreamsInbound": 64,
-      "StreamsOutbound": 64,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 1048576
-    },
-    "libp2p.relay/v2": {
-      "Streams": 64,
-      "StreamsInbound": 64,
-      "StreamsOutbound": 64,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 1048576
-    }
-  },
-  "ProtocolDefault": {
-    "Streams": 2048,
-    "StreamsInbound": 512,
-    "StreamsOutbound": 2048,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "FD": 0,
-    "Memory": 67108864
-  },
-  "Protocol": {
-    "/ipfs/id/1.0.0": {
-      "Streams": 128,
-      "StreamsInbound": 64,
-      "StreamsOutbound": 64,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 4194304
-    },
-    "/ipfs/id/push/1.0.0": {
-      "Streams": 128,
-      "StreamsInbound": 64,
-      "StreamsOutbound": 64,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 4194304
-    },
-    "/ipfs/ping/1.0.0": {
-      "Streams": 64,
-      "StreamsInbound": 64,
-      "StreamsOutbound": 64,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 4194304
-    },
-    "/libp2p/autonat/1.0.0": {
-      "Streams": 64,
-      "StreamsInbound": 64,
-      "StreamsOutbound": 64,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 4194304
-    },
-    "/libp2p/circuit/relay/0.1.0": {
-      "Streams": 640,
-      "StreamsInbound": 640,
-      "StreamsOutbound": 640,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 16777216
-    },
-    "/libp2p/circuit/relay/0.2.0/hop": {
-      "Streams": 640,
-      "StreamsInbound": 640,
-      "StreamsOutbound": 640,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 16777216
-    },
-    "/libp2p/circuit/relay/0.2.0/stop": {
-      "Streams": 640,
-      "StreamsInbound": 640,
-      "StreamsOutbound": 640,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 16777216
-    },
-    "/libp2p/dcutr": {
-      "Streams": 64,
-      "StreamsInbound": 32,
-      "StreamsOutbound": 32,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 4194304
-    },
-    "/p2p/id/delta/1.0.0": {
-      "Streams": 128,
-      "StreamsInbound": 64,
-      "StreamsOutbound": 64,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 4194304
-    }
-  },
-  "ProtocolPeerDefault": {
-    "Streams": 256,
-    "StreamsInbound": 64,
-    "StreamsOutbound": 128,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "FD": 0,
-    "Memory": 16777216
-  },
-  "ProtocolPeer": {
-    "/ipfs/id/1.0.0": {
-      "Streams": 32,
-      "StreamsInbound": 16,
-      "StreamsOutbound": 16,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 8590458880
-    },
-    "/ipfs/id/push/1.0.0": {
-      "Streams": 32,
-      "StreamsInbound": 16,
-      "StreamsOutbound": 16,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 8590458880
-    },
-    "/ipfs/ping/1.0.0": {
-      "Streams": 4,
-      "StreamsInbound": 2,
-      "StreamsOutbound": 3,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 8590458880
-    },
-    "/libp2p/autonat/1.0.0": {
-      "Streams": 2,
-      "StreamsInbound": 2,
-      "StreamsOutbound": 2,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 1048576
-    },
-    "/libp2p/circuit/relay/0.1.0": {
-      "Streams": 128,
-      "StreamsInbound": 128,
-      "StreamsOutbound": 128,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 33554432
-    },
-    "/libp2p/circuit/relay/0.2.0/hop": {
-      "Streams": 128,
-      "StreamsInbound": 128,
-      "StreamsOutbound": 128,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 33554432
-    },
-    "/libp2p/circuit/relay/0.2.0/stop": {
-      "Streams": 128,
-      "StreamsInbound": 128,
-      "StreamsOutbound": 128,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 33554432
-    },
-    "/libp2p/dcutr": {
-      "Streams": 2,
-      "StreamsInbound": 2,
-      "StreamsOutbound": 2,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 1048576
-    },
-    "/p2p/id/delta/1.0.0": {
-      "Streams": 32,
-      "StreamsInbound": 16,
-      "StreamsOutbound": 16,
-      "Conns": 0,
-      "ConnsInbound": 0,
-      "ConnsOutbound": 0,
-      "FD": 0,
-      "Memory": 8590458880
-    }
-  },
-  "PeerDefault": {
-    "Streams": 512,
-    "StreamsInbound": 256,
-    "StreamsOutbound": 512,
-    "Conns": 8,
-    "ConnsInbound": 4,
-    "ConnsOutbound": 8,
-    "FD": 4,
-    "Memory": 67108864
-  },
-  "Conn": {
-    "Streams": 0,
-    "StreamsInbound": 0,
-    "StreamsOutbound": 0,
-    "Conns": 1,
-    "ConnsInbound": 1,
-    "ConnsOutbound": 1,
-    "FD": 1,
-    "Memory": 1048576
-  },
-  "Stream": {
-    "Streams": 1,
-    "StreamsInbound": 1,
-    "StreamsOutbound": 1,
-    "Conns": 0,
-    "ConnsInbound": 0,
-    "ConnsOutbound": 0,
-    "FD": 0,
-    "Memory": 16777216
-  }
-}`
diff --git a/core/node/libp2p/rcmgr_logging.go b/core/node/libp2p/rcmgr_logging.go
index 21b7fe65e5909caf0c86e2b725a50a3662e8d50b..34742dd1d7f2f1de57472b2427775af96a171c52 100644
--- a/core/node/libp2p/rcmgr_logging.go
+++ b/core/node/libp2p/rcmgr_logging.go
@@ -22,7 +22,7 @@ type loggingResourceManager struct {
 	logInterval time.Duration
 
 	mut               sync.Mutex
-	limitExceededErrs uint64
+	limitExceededErrs map[string]int
 }
 
 type loggingScope struct {
@@ -32,6 +32,7 @@ type loggingScope struct {
 }
 
 var _ network.ResourceManager = (*loggingResourceManager)(nil)
+var _ rcmgr.ResourceManagerState = (*loggingResourceManager)(nil)
 
 func (n *loggingResourceManager) start(ctx context.Context) {
 	logInterval := n.logInterval
@@ -46,11 +47,17 @@ func (n *loggingResourceManager) start(ctx context.Context) {
 			case <-ticker.C:
 				n.mut.Lock()
 				errs := n.limitExceededErrs
-				n.limitExceededErrs = 0
-				n.mut.Unlock()
-				if errs != 0 {
-					n.logger.Warnf("Resource limits were exceeded %d times, consider inspecting logs and raising the resource manager limits.", errs)
+				n.limitExceededErrs = make(map[string]int)
+
+				for e, count := range errs {
+					n.logger.Errorf("Resource limits were exceeded %d times with error %q.", count, e)
+				}
+
+				if len(errs) != 0 {
+					n.logger.Errorf("Consider inspecting logs and raising the resource manager limits. Documentation: https://github.com/ipfs/kubo/blob/master/docs/config.md#swarmresourcemgr")
 				}
+
+				n.mut.Unlock()
 			case <-ctx.Done():
 				return
 			}
@@ -61,7 +68,16 @@ func (n *loggingResourceManager) start(ctx context.Context) {
 func (n *loggingResourceManager) countErrs(err error) {
 	if errors.Is(err, network.ErrResourceLimitExceeded) {
 		n.mut.Lock()
-		n.limitExceededErrs++
+		if n.limitExceededErrs == nil {
+			n.limitExceededErrs = make(map[string]int)
+		}
+
+		// we need to unwrap the error to get the limit scope and the kind of reached limit
+		eout := errors.Unwrap(err)
+		if eout != nil {
+			n.limitExceededErrs[eout.Error()]++
+		}
+
 		n.mut.Unlock()
 	}
 }
@@ -103,6 +119,40 @@ func (n *loggingResourceManager) Close() error {
 	return n.delegate.Close()
 }
 
+func (n *loggingResourceManager) ListServices() []string {
+	rapi, ok := n.delegate.(rcmgr.ResourceManagerState)
+	if !ok {
+		return nil
+	}
+
+	return rapi.ListServices()
+}
+func (n *loggingResourceManager) ListProtocols() []protocol.ID {
+	rapi, ok := n.delegate.(rcmgr.ResourceManagerState)
+	if !ok {
+		return nil
+	}
+
+	return rapi.ListProtocols()
+}
+func (n *loggingResourceManager) ListPeers() []peer.ID {
+	rapi, ok := n.delegate.(rcmgr.ResourceManagerState)
+	if !ok {
+		return nil
+	}
+
+	return rapi.ListPeers()
+}
+
+func (n *loggingResourceManager) Stat() rcmgr.ResourceManagerStat {
+	rapi, ok := n.delegate.(rcmgr.ResourceManagerState)
+	if !ok {
+		return rcmgr.ResourceManagerStat{}
+	}
+
+	return rapi.Stat()
+}
+
 func (s *loggingScope) ReserveMemory(size int, prio uint8) error {
 	err := s.delegate.ReserveMemory(size, prio)
 	s.countErrs(err)
diff --git a/core/node/libp2p/rcmgr_logging_test.go b/core/node/libp2p/rcmgr_logging_test.go
index 1dfad73afcc7292be2d5a45b27fca8d5b71e853d..3521e5314eac9ce6b2eb09bef28b1efdc63cdb57 100644
--- a/core/node/libp2p/rcmgr_logging_test.go
+++ b/core/node/libp2p/rcmgr_logging_test.go
@@ -55,7 +55,7 @@ func TestLoggingResourceManager(t *testing.T) {
 			if oLogs.Len() == 0 {
 				continue
 			}
-			require.Equal(t, "Resource limits were exceeded 2 times, consider inspecting logs and raising the resource manager limits.", oLogs.All()[0].Message)
+			require.Equal(t, "Resource limits were exceeded 2 times with error \"system: cannot reserve inbound connection: resource limit exceeded\".", oLogs.All()[0].Message)
 			return
 		}
 	}
diff --git a/core/node/libp2p/transport.go b/core/node/libp2p/transport.go
index e59770772a5c47559e440ec2bf3a8487e6551519..d96891df2269074cba32df8a50ca0facfa7ebcac 100644
--- a/core/node/libp2p/transport.go
+++ b/core/node/libp2p/transport.go
@@ -33,8 +33,7 @@ func Transports(tptConfig config.Transports) interface{} {
 		if tptConfig.Network.QUIC.WithDefault(!privateNetworkEnabled) {
 			if privateNetworkEnabled {
 				return opts, fmt.Errorf(
-					"The QUIC transport does not support private networks. " +
-						"Please disable Swarm.Transports.Network.QUIC.",
+					"QUIC transport does not support private networks, please disable Swarm.Transports.Network.QUIC",
 				)
 			}
 			// TODO(9290): Make WithMetrics configurable
@@ -45,8 +44,7 @@ func Transports(tptConfig config.Transports) interface{} {
 		if tptConfig.Network.WebTransport.WithDefault(false && !privateNetworkEnabled) {
 			if privateNetworkEnabled {
 				return opts, fmt.Errorf(
-					"The WebTransport transport does not support private networks. " +
-						"Please disable Swarm.Transports.Network.WebTransport.",
+					"WebTransport transport does not support private networks, please disable Swarm.Transports.Network.WebTransport",
 				)
 			}
 			opts.Opts = append(opts.Opts, libp2p.Transport(webtransport.New))
diff --git a/docs/RELEASE_ISSUE_TEMPLATE.md b/docs/RELEASE_ISSUE_TEMPLATE.md
index 3797746690fc045f92a222d4390d71757140599c..9edf679c130597f0a9950b3b0b869364a421719a 100644
--- a/docs/RELEASE_ISSUE_TEMPLATE.md
+++ b/docs/RELEASE_ISSUE_TEMPLATE.md
@@ -1,3 +1,5 @@
+<!-- Last updated by @galargh during [0.16.0 release](https://github.com/ipfs/kubo/issues/9237) -->
+
 > Release Issue Template.  If doing a patch release, see [here](https://github.com/ipfs/kubo/blob/master/docs/PATCH_RELEASE_TEMPLATE.md)
 
 # Items to do upon creating the release issue
@@ -10,154 +12,216 @@
 # Meta
 * Release owner: @who
 * Release reviewer: @who
-* Expected RC date: week of 2022-MM-DD
-* 🚢 Expected final release date: 2022-MM-DD
+* Expected RC date: week of YYYY-MM-DD
+* 🚢 Expected final release date: YYYY-MM-DD
 * Accompanying PR for improving the release process: (example: https://github.com/ipfs/kubo/pull/9100)
 
 See the [Kubo release process](https://pl-strflt.notion.site/Kubo-Release-Process-5a5d066264704009a28a79cff93062c4) for more info.
 
 # Kubo X.Y.Z Release
 
-We're happy to announce Kubo X.Y.Z, bla bla...
+We're happy to announce Kubo X.Y.Z!
 
-As usual, this release includes important fixes, some of which may be critical for security. Unless the fix addresses a bug being exploited in the wild, the fix will _not_ be called out in the release notes. Please make sure to update ASAP. See our [release process](https://github.com/ipfs/go-ipfs/tree/master/docs/releases.md#security-fix-policy) for details.
+As usual, this release includes important fixes, some of which may be critical for security. Unless the fix addresses a bug being exploited in the wild, the fix will _not_ be called out in the release notes. Please make sure to update ASAP. See our [security fix policy](https://github.com/ipfs/go-ipfs/tree/master/docs/releases.md#security-fix-policy) for details.
 
 ## 🗺 What's left for release
 
 <List of items with PRs and/or Issues to be considered for this release>
 
+### Required
+
+### Nice to have
+
 ## 🔦 Highlights
 
 < top highlights for this release notes. For ANY version (final or RCs) >
 
 ## ✅ Release Checklist
 
-For each RC published in each stage:
-
-- version string in `version.go` has been updated (in the `release-vX.Y.Z` branch).
-- new commits should be added to the `release-vX.Y.Z` branch from `master` using `git cherry-pick -x ...`
-  - Note: `release-` branches are protected. You can do all needed updates on a separated branch and when everything is settled push to `release-vX.Y.Z`
-- tag commit with `vX.Y.Z-rcN`
-- add artifacts to https://dist.ipfs.tech
-  1. Make a PR against [ipfs/distributions](https://github.com/ipfs/distributions) with local changes produced by `add-version` (see [usage](https://github.com/ipfs/distributions#usage))
-  2. Wait for PR to build artifacts and generate diff (~30min)
-  3. Inspect results, merge if CI is green and the diff looks ok
-  4. Wait for `master` branch to build and update DNSLink at https://dist.ipfs.tech (~30min)
-- cut a pre-release on [github](https://github.com/ipfs/kubo/releases) and reuse signed artifacts from https://dist.ipfs.tech/kubo (upload the result of the ipfs/distributions build in the previous step).
-- Announce the RC:
-  - [ ] Create a new post on [IPFS Discourse](https://discuss.ipfs.tech)
-    - This will automatically post to IPFS Discord #ipfs-chatter
-    - Examples from the past: [0.14.0](https://discuss.ipfs.io/t/kubo-formerly-go-ipfs-v0-14-0-release-is-out/14794)
-  - [ ] Pin the topic. You need to be part of the [admin group](https://discuss.ipfs.tech/g/admins) for that.
-  - [ ] To the _early testers_ listed in [docs/EARLY_TESTERS.md](https://github.com/ipfs/go-ipfs/tree/master/docs/EARLY_TESTERS.md).  Do this by copy/pasting their GitHub usernames and checkboxes as a comment so they get a GitHub notification.  ([example](https://github.com/ipfs/go-ipfs/issues/8176#issuecomment-909356394))
-
 Checklist:
 
-- [ ] **Stage 0 - Automated Testing**
-  - [ ] Upgrade to the latest patch release of Go that CircleCI has published
+- [ ] **Stage 0 - Prerequisites**
+  - [ ] Open an issue against [bifrost-infra](https://github.com/protocol/bifrost-infra) ahead of the release ([example](https://github.com/protocol/bifrost-infra/issues/2109)).
+    - [ ] Spell out all that we want updated - gateways, the bootstraper and the cluster/preload nodes
+    - [ ] Mention @protocol/bifrost-team in the issue and let them know the expected date of the release
+  - [ ] Ensure that the `What's left for release` section has all the checkboxes checked. If that's not the case, discuss the open items with Kubo maintainers and update the release schedule accordingly.
+  - [ ] Create `docs-release-vX.Y.Z` branch, open a draft PR and keep updating `docs/RELEASE_ISSUE_TEMPLATE.md` on that branch as you go.
+  - [ ] Ensure you have a [GPG key generated](https://docs.github.com/en/authentication/managing-commit-signature-verification/generating-a-new-gpg-key) and [added to your GitHub account](https://docs.github.com/en/authentication/managing-commit-signature-verification/adding-a-gpg-key-to-your-github-account). This will enable you to created signed tags.
+  - [ ] Ensure you have [admin access](https://discuss.ipfs.tech/g/admins) to [IPFS Discourse](https://discuss.ipfs.tech/). Admin access is required to globally pin posts and create banners. @2color might be able to assist you.
+  - [ ] Access to [#bifrost](https://filecoinproject.slack.com/archives/C03MMMF606T) channel in FIL Slack might come in handy. Ask the release reviewer to invite you over.
+  - [ ] Access to [#shared-pl-marketing-requests](https://filecoinproject.slack.com/archives/C018EJ8LWH1) channel in FIL Slack will be required to request social shares. Ask the release reviewer to invite you over.
+  - [ ] After the release is deployed to our internal infrastructure, you're going to need read access to [IPFS network metrics](https://github.com/protocol/pldw/blob/624f47cf4ec14ad2cec6adf601a9f7b203ef770d/docs/sources/ipfs.md#ipfs-network-metrics) dashboards. Open an access request in https://github.com/protocol/pldw/issues/new/choose if you don't have it yet ([example](https://github.com/protocol/pldw/issues/158)).
+  - [ ] You're also going to need NPM installed on your system. See [here](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) for instructions.
+  - [ ] Prepare changelog proposal in [docs/changelogs/vX.Y.md](https://github.com/ipfs/kubo/blob/master/docs/changelogs/).
+    - Skip filling out the `### Changelog` section (the one where which lists all the commits and contributors) for now. We will populate it after the release branch is cut.
+  - [ ] Install ZSH ([instructions](https://github.com/ohmyzsh/ohmyzsh/wiki/Installing-ZSH#install-and-set-up-zsh-as-default)). It is needed by the changelog creation script.
+  - [ ] Ensure you have `kubo` checked out under `$(go env GOPATH)/src/github.com/ipfs/kubo`. This is required by the changelog creation script.
+    - If you want your clone to live in a different location, you can symlink it to the expected location by running `mkdir -p $(go env GOPATH)/src/github.com/ipfs && ln -s $(pwd) $(go env GOPATH)/src/github.com/ipfs/kubo`.
+  - [ ] Ensure that [README.md](https://github.com/ipfs/go-ipfs/tree/master/README.md) is up to date.
+- [ ] **Stage 1 - Initial Preparations**
+  - [ ] Upgrade to the latest patch release of Go that CircleCI has published (currently used version: `1.19.1`)
     - [ ] See the list here: https://hub.docker.com/r/cimg/go/tags
     - [ ] [ipfs/distributions](https://github.com/ipfs/distributions): bump [this version](https://github.com/ipfs/distributions/blob/master/.tool-versions#L2)
     - [ ] [ipfs/kubo](https://github.com/ipfs/kubo): [example PR](https://github.com/ipfs/kubo/pull/8599)
-  - [ ] Fork a new branch (`release-vX.Y.Z`) from `master` and make any further release related changes to this branch. If any "non-trivial" changes (see the footnotes of [docs/releases.md](https://github.com/ipfs/go-ipfs/tree/master/docs/releases.md) for a definition) get added to the release, uncheck all the checkboxes and return to this stage.
-    - [ ] Follow the RC release process to cut the first RC.
-    - [ ] Bump the version in `version.go` in the `master` branch to `vX.(Y+1).0-dev`.
-  - [ ] Automated Testing (already tested in CI) - Ensure that all tests are passing, this includes:
-    - [ ] unit, sharness, cross-build, etc (`make test`)
-    - [ ] lint (`make test_go_lint`)
-    - [ ] [interop](https://github.com/ipfs/interop#test-with-a-non-yet-released-version-of-go-ipfs)
+    - [ ] [ipfs/ipfs-docs](https://github.com/ipfs/ipfs-docs): [example PR](https://github.com/ipfs/ipfs-docs/pull/1298)
+  - [ ] Fork a new branch (`release-vX.Y.Z`) from `master`.
+  - [ ] Bump the version in `version.go` in the `master` branch to `vX.(Y+1).0-dev` via a PR ([example](https://github.com/ipfs/kubo/pull/9305)).
+- [ ] **Stage 2 - Release Candidate** - _if any [non-trivial](docs/releases.md#footnotes) changes need to be included in the release, return to this stage_
+  - [ ] Bump the version in `version.go` in the `release-vX.Y.Z` branch to `vX.Y.Z-rcN`.
+  - [ ] If applicable, add new commits to the `release-vX.Y.Z` branch from `master` using `git cherry-pick -x ...`
+      - Note: `release-*` branches are protected. You can do all needed updates on a separated branch (e.g. `wip-release-vX.Y.Z`) and when everything is settled push to `release-vX.Y.Z`
+  - [ ] Push the `release-vX.Y.Z` branch to GitHub (`git push origin release-vX.Y.Z`) and create a draft PR targetting `release` branch if it doesn't exist yet ([example](https://github.com/ipfs/kubo/pull/9306)).
+  - [ ] Wait for CI to run and complete PR checks. All checks should pass.
+  - [ ] Create a signed tag for the release candidate.
+    - [ ] This is a dangerous operation, as it is difficult to reverse due to Go modules and automated Docker image publishing. Remember to verify the commands you intend to run for items marked with ⚠️ with the release reviewer.
+    - [ ] ⚠️ Tag HEAD `release-vX.Y.Z` commit with `vX.Y.Z-rcN` (`git tag -s vX.Y.Z-rcN -m 'Pre-release X.Y.Z-rcn'`)
+    - [ ] Run `git show vX.Y.Z` to ensure the tag is correct.
+    - [ ] ⚠️ Push the `vX.Y.Z` tag to GitHub (`git push origin vX.Y.Z`; DO NOT USE `git push --tags` because it pushes all your local tags).
+  - [ ] Add artifacts to https://dist.ipfs.tech by making a PR against [ipfs/distributions](https://github.com/ipfs/distributions)
+    - [ ] Clone the `ipfs/distributions` repo locally.
+    - [ ] Create a new branch (`kubo-release-vX.Y.Z-rcn`) from `master`.
+    - [ ] Run `./dist.sh add-version kubo vX.Y.Z-rcN` to add the new version to the `versions` file ([instructions](https://github.com/ipfs/distributions#usage)).
+      - `dist.sh` will print _WARNING: not marking pre-release kubo vX.Y.Z-rc1n as the current version._.
+    - [ ] Push the `kubo-release-vX.Y.Z-rcn` branch to GitHub and create a PR from that branch ([example](https://github.com/ipfs/distributions/pull/760)).
+    - [ ] Ask for a review from the release reviewer.
+    - [ ] Enable auto-merge for the PR.
+      - PR build will build the artifacts and generate a diff in around 30 minutes
+      - PR will be merged automatically once the diff is approved
+      - `master` build will publish the artifacts to https://dist.ipfs.io in around 30 minutes
+    - [ ] Ensure that the artifacts are available at https://dist.ipfs.io
+  - [ ] Publish the RC to [the NPM package](https://www.npmjs.com/package/go-ipfs?activeTab=versions) by running https://github.com/ipfs/npm-go-ipfs/actions/workflows/main.yml (it happens automatically but it is safe to speed up the process and kick of a run manually)
+  - [ ] Cut a pre-release on [GitHub](https://github.com/ipfs/kubo/releases) ([instructions](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release), [example](https://github.com/ipfs/kubo/releases/tag/v0.16.0-rc1))
+    - Use `vX.Y.Z-rcN` as the tag.
+    - Link to the release issue in the description.
+    - Link to the relevant [changelog](https://github.com/ipfs/kubo/blob/master/docs/changelogs/) in the description.
+    - Check `This is a pre-release`.
+  - [ ] Synchronize release artifacts by running [sync-release-assets](https://github.com/ipfs/kubo/actions/workflows/sync-release-assets.yml) workflow.
+  - [ ] Announce the RC
+    - [ ] Create a new post on [IPFS Discourse](https://discuss.ipfs.tech). ([example](https://discuss.ipfs.tech/t/kubo-v0-16-0-rc1-release-candidate-is-out/15248))
+      - Use `Kubo vX.Y.Z-rcn Release Candidate is out!` as the title.
+      - Use `kubo` and `go-ipfs` as topics.
+      - Repeat the title as a heading (`##`) in the description.
+      - Link to the GitHub Release, binaries on IPNS, docker pull command and release notes in the description.
+    - [ ] Pin the topic globally so that it stays at the top of the category.
+    - [ ] If there is no more important banner currently set on Discourse (e.g. IPFS Camp announcement), make the topic into a banner.
+    - [ ] Check if Discourse post was automatically copied to:
+      - [ ] IPFS Discord #ipfs-chatter
+      - [ ] FIL Slack #ipfs-chatter
+      - [ ] Matrix https://matrix.to/#/#ipfs-chatter:ipfs.io
+    - [ ] Mention [early testers](https://github.com/ipfs/go-ipfs/tree/master/docs/EARLY_TESTERS.md) in the comment under the release issue ([example](https://github.com/ipfs/kubo/issues/9237#issuecomment-1258072509)).
+- [ ] **Stage 3 - Internal Testing**
+  - [ ] Library Testing.
+    - [ ] [interop](https://github.com/ipfs/interop)
+      - [ ] Clone the `ipfs/interop` repo locally.
+      - [ ] Create a new branch (`kubo-release-vX.Y.Z-rcn`) from `master`.
+      - [ ] Update `go-ipfs` version to `vX.Y.Z-rcN` in [package.json](https://github.com/ipfs/interop/blob/master/package.json).
+      - [ ] Run `npm install` locally
+      - [ ] Push the `kubo-release-vX.Y.Z-rcn` branch to GitHub and create a draft PR from that branch ([example](https://github.com/ipfs/interop/pull/511)).
     - [ ] [go-ipfs-api](https://github.com/ipfs/go-ipfs-api)
+      - [ ] Create a branch with kubo version pinned in the [test setup action](https://github.com/ipfs/go-ipfs-api/blob/master/.github/actions/go-test-setup/action.yml) ([example](https://github.com/ipfs/go-ipfs-api/commit/d156b808cc3aebafba65a38e5dd6993543a50e82)).
+      - [ ] Ensure that CI is green.
+      - [ ] Delete the branch.
     - [ ] [go-ipfs-http-client](https://github.com/ipfs/go-ipfs-http-client)
+      - [ ] Create a branch with kubo version pinned in the [test setup action](https://github.com/ipfs/go-ipfs-http-client/blob/master/.github/actions/go-test-setup/action.yml) ([example](https://github.com/ipfs/go-ipfs-http-client/commit/8a057960d26f1c60fffef09be3b05ec3f2e71bba)).
+      - [ ] Ensure that CI is green.
+      - [ ] Delete the branch.
     - [ ] [WebUI](https://github.com/ipfs-shipyard/ipfs-webui)
-- [ ] **Stage 1 - Internal Testing**
-  - [ ] CHANGELOG.md has been updated
-    - use [`./bin/mkreleaselog`](https://github.com/ipfs/go-ipfs/tree/master/bin/mkreleaselog) to generate a nice starter list
-    	- you need to install `zsh`.
-  - [ ] Infrastructure Testing. 
-    - [ ] Open an issue against https://github.com/protocol/bifrost-infra and spell out all that we want (e.g.,mgateways, bootstrapper, and cluster).  [example](https://github.com/protocol/bifrost-infra/issues/2046)
-    - [ ] Deploy new version to a subset of Bootstrappers
-    - [ ] Deploy new version to a subset of Gateways
-    - [ ] Deploy new version to a subset of Preload nodes
-    - [ ] Collect [metrics](https://protocollabs.grafana.net/d/8zlhkKTZk/gateway-slis-precomputed?orgId=1) every day. Work with the Infrastructure team to learn of any hiccup
-  - [ ] IPFS Application Testing -  Run the tests of the following applications:
+      - [ ] Run [CI workflow](https://github.com/ipfs/ipfs-webui/actions/workflows/ci.yml) with `vX.Y.Z-rcN` for the `kubo-version` input.
+      - [ ] Ensure that CI is green.
+  - [ ] Infrastructure Testing.
+    - [ ] Update the issue against [bifrost-infra](https://github.com/protocol/bifrost-infra) ([example](https://github.com/protocol/bifrost-infra/issues/2109)).
+      - [ ] Mention @protocol/bifrost-team in the issue to let them know the release is ready
+      - [ ] [Optional] Reply under a message about the issue in the #bifrost channel on FIL Slack once the RC is out. Send the message to the channel.
+    - [ ] Check [metrics](https://protocollabs.grafana.net/d/8zlhkKTZk/gateway-slis-precomputed?orgId=1) every day.
+      - Compare the metrics trends week over week.
+      - If there is an unexpected variation in the trend, message the #bifrost channel on FIL Slack and ask for help investigation the cause.
+  - [ ] IPFS Application Testing.
     - [ ] [IPFS Desktop](https://github.com/ipfs-shipyard/ipfs-desktop)
-      - [ ] Ensure the RC is published to [the NPM package](https://www.npmjs.com/package/go-ipfs?activeTab=versions) ([happens automatically, just wait for CI](https://github.com/ipfs/npm-go-ipfs/actions))
-      - [ ] Upgrade to the RC in [ipfs-desktop](https://github.com/ipfs-shipyard/ipfs-desktop) and push to a branch ([example](https://github.com/ipfs/ipfs-desktop/pull/1826/commits/b0a23db31ce942b46d95965ee6fe770fb24d6bde)), and open a draft PR to track through the final release ([example](https://github.com/ipfs/ipfs-desktop/pull/1826))
-      - [ ] Ensure CI tests pass, repeat for new RCs
+      - [ ] Upgrade to the RC in [ipfs-desktop](https://github.com/ipfs-shipyard/ipfs-desktop)
+      - [ ] Run `npm install` to update `package-lock.json`.
+      - [ ] Push to a branch ([example](https://github.com/ipfs/ipfs-desktop/pull/1826/commits/b0a23db31ce942b46d95965ee6fe770fb24d6bde))
+      - [ ] Open a draft PR to track through the final release ([example](https://github.com/ipfs/ipfs-desktop/pull/1826))
+      - [ ] Ensure CI tests pass
     - [ ] [IPFS Companion](https://github.com/ipfs-shipyard/ipfs-companion)
-      - Start kubo daemon of the version to release.
-      - Start a fresh chromium or chrome instance using `chromium --user-data-dir=$(mktemp -d)`
-      - Start a fresh firefox instance using `firefox --profile $(mktemp -d)`
-      - Install IPFS Companion from [vendor-specific store](https://github.com/ipfs/ipfs-companion/#readme).
-      - Check that the comunication between Kubo daemon and IPFS companion is working properly checking if the number of connected peers changes.
-- [ ] **Stage 2 - Community Prod Testing**
-  - [ ] Documentation
-    - [ ] Ensure that [CHANGELOG.md](https://github.com/ipfs/go-ipfs/tree/master/CHANGELOG.md) is up to date
-	  - [ ] Add a link from release notes to Discuss post (like we did here: https://github.com/ipfs/kubo/releases/tag/v0.15.0 )
-	  - [ ] Keep the release notes as trim as possible (removing some of the top headers, like we did here: https://github.com/ipfs/kubo/releases/tag/v0.15.0 )
-    - [ ] Ensure that [README.md](https://github.com/ipfs/go-ipfs/tree/master/README.md)  is up to date
-    - [ ] Update docs by merging the auto-created PR in https://github.com/ipfs/ipfs-docs/pulls (they are auto-created every 12 hours) (only for final releases, not RCs)
-  - [ ] Invite the wider community through (link to the release issue):
-    - [ ] [discuss.ipfs.io](https://discuss.ipfs.io/c/announcements)
-    - [ ] Matrix
-- [ ] **Stage 3 - Release**
-  - [ ] Final preparation
-    - [ ] Verify that version string in [`version.go`](https://github.com/ipfs/go-ipfs/tree/master/version.go) has been updated.
-    - [ ] Open a PR merging `release-vX.Y.Z` into the `release` branch.
-      - This should be reviewed by the person who most recently released a version of `go-ipfs`.
-	  - Use a merge commit (no rebase, no squash)
-    - [ ] Prepare the command to use for tagging the merge commit (on the `release` branch) with `vX.Y.Z`.
-      - Use `git tag -s` to ensure the tag is signed
-    - [ ] Have the tagging command reviewed by the person who most recently released a version of `go-ipfs`
-      - This is a dangerous operation, as it is difficult to reverse due to Go modules and automated Docker image publishing
-    - [ ] Push the tag
-      - Use `git push origin <tag>`
-      - DO NOT USE `git push --tags`, as it will push ALL of your local tags
-      - This should initiate a Docker build in GitHub Actions that publishes a `vX.Y.Z` tagged Docker image to DockerHub
-    - [ ] Release published
-      - [ ] to [dist.ipfs.tech](https://dist.ipfs.tech)
-      - [ ] to [npm-go-ipfs](https://www.npmjs.com/package/go-ipfs) (done by CI at [ipfs/npm-go-ipfs](https://github.com/ipfs/npm-go-ipfs), but ok to dispatch [this job](https://github.com/ipfs/npm-go-ipfs/actions/workflows/main.yml) manually)
-      - [ ] to [chocolatey](https://chocolatey.org/packages/go-ipfs) (done by CI at [ipfs/choco-go-ipfs](https://github.com/ipfs/choco-go-ipfs/), but ok to dispatch [this job](https://github.com/ipfs/choco-go-ipfs/actions/workflows/main.yml) manually)
-         - [ ] Manually run [the release workflow](https://github.com/ipfs/choco-go-ipfs/actions/workflows/main.yml)
-         - [ ] Wait for Chocolatey to approve the release (usually takes a few hours)
-      - [ ] to [snap](https://snapcraft.io/ipfs) (done CI at [snap/snapcraft.yaml](https://github.com/ipfs/kubo/blob/master/snap/snapcraft.yaml))
-      - [ ] to [github](https://github.com/ipfs/go-ipfs/releases)
-        - [ ] After publishing the GitHub release, run the workflow to attach the release assets: https://github.com/ipfs/go-ipfs/actions/workflows/sync-release-assets.yml
-      - [ ] to [arch](https://www.archlinux.org/packages/community/x86_64/go-ipfs/) (flag it out of date)
-    - [ ] Cut a new ipfs-desktop release
-  - [ ] Get a blog post created 
-    - [Submit a request using this form](https://airtable.com/shrNH8YWole1xc70I).
-    - Notify marketing in #shared-pl-marketing-requests about the blog entry request (since the form gets spam).
-    - Don't mark this as done until the blog entry is live.
-  - [ ] Broadcasting (link to blog post)
-    - [ ] Twitter (request in Filecoin Slack channel #shared-pl-marketing-requests)
-    - [ ] [Reddit](https://reddit.com/r/ipfs)
-    - [ ] [discuss.ipfs.io](https://discuss.ipfs.io/c/announcements)
-      - A bot auto-posts this to Discord and Matrix
-- [ ] **Post-Release**
+      - [ ] Start kubo daemon of the version to release.
+      - [ ] Start a fresh chromium or chrome instance using `chromium --user-data-dir=$(mktemp -d)` (macos `/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --user-data-dir=$(mktemp -d)`)
+      - [ ] Start a fresh firefox instance using `firefox --profile $(mktemp -d)` (macos `/Applications/Firefox.app/Contents/MacOS/firefox --profile $(mktemp -d)`)
+      - [ ] Install IPFS Companion from [vendor-specific store](https://github.com/ipfs/ipfs-companion/#readme).
+      - [ ] Check that the comunication between Kubo daemon and IPFS companion is working properly checking if the number of connected peers changes.
+- [ ] **Stage 5 - Release** - _ONLY FOR FINAL RELEASE_
+  - [ ] Prepare the `release` branch.
+    - [ ] Bump the version in `version.go` in the `release-vX.Y.Z` branch to `vX.Y.Z`.
+    - [ ] Update the [docs/changelogs/vX.Y.md](docs/changelogs) with the new commits and contributors.
+      - [ ] Run `./bin/mkreleaselog` twice to generate the changelog and copy the output.
+      - The first run of the script might be poluted with `git clone` output.
+      - [ ] Paste the output into the `### Changelog` section of the changelog file inside the `<details><summary></summary></details>` block.
+      - [ ] Commit the changelog changes.
+    - [ ] Push the `release-vX.Y.Z` branch to GitHub (`git push origin release-vX.Y.Z`)
+    - [ ] Mark the PR created from `release-vX.Y.Z` as ready for review.
+      - [ ] Ensure the PR is targetting `release` branch.
+      - [ ] Ensure that CI is green.
+      - [ ] Have release reviewer review the PR.
+    - [ ] Merge the PR into `release` branch using the `Create a merge commit` (do **NOT** use `Squash and merge` nor `Rebase and merge` because we need to be able to sign the merge commit).
+      - Do not delete the `release-vX.Y.Z` branch.
+    - [ ] Checkout the `release` branch locally.
+      - Remember to pull the latest changes.
+    - [ ] Create a signed tag for the release.
+      - [ ] This is a dangerous operation, as it is difficult to reverse due to Go modules and automated Docker image publishing. Remember to verify the commands you intend to run for items marked with ⚠️ with the release reviewer.
+      - [ ] ⚠️ Tag HEAD `release` commit with `vX.Y.Z` (`git tag -s vX.Y.Z -m 'Release X.Y.Z'`)
+      - [ ] Run `git show vX.Y.Z` to ensure the tag is correct.
+      - [ ] ⚠️ Push the `vX.Y.Z` tag to GitHub (`git push origin vX.Y.Z`; DO NOT USE `git push --tags` because it pushes all your local tags).
+  - [ ] Publish the release.
+    - [ ] Wait for [Publish docker image](https://github.com/ipfs/kubo/actions/workflows/docker-image.yml) workflow run initiated by the tag push to finish.
+    - [ ] Add artifacts to https://dist.ipfs.tech by making a PR against [ipfs/distributions](https://github.com/ipfs/distributions)
+      - [ ] Clone the `ipfs/distributions` repo locally.
+      - [ ] Create a new branch (`kubo-release-vX.Y.Z`) from `master`.
+      - [ ] Run `./dist.sh add-version kubo vX.Y.Z` to add the new version to the `versions` file ([instructions](https://github.com/ipfs/distributions#usage)).
+      - [ ] Push the `kubo-release-vX.Y.Z` branch to GitHub and create a PR from that branch ([example](https://github.com/ipfs/distributions/pull/768)).
+      - [ ] Ask for a review from the release reviewer.
+      - [ ] Enable auto-merge for the PR.
+        - PR build will build the artifacts and generate a diff in around 30 minutes
+        - PR will be merged automatically once the diff is approved
+        - `master` build will publish the artifacts to https://dist.ipfs.io in around 30 minutes
+      - [ ] Ensure that the artifacts are available at https://dist.ipfs.io
+    - [ ] Publish the release to [the NPM package](https://www.npmjs.com/package/go-ipfs?activeTab=versions) by running https://github.com/ipfs/npm-go-ipfs/actions/workflows/main.yml (it happens automatically but it is safe to speed up the process and kick of a run manually)
+  - [ ] Cut the release on [GitHub](https://github.com/ipfs/kubo/releases) ([instructions](https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release), [example](https://github.com/ipfs/kubo/releases/tag/v0.16.0))
+    - Use `vX.Y.Z` as the tag.
+    - Link to the release issue in the description.
+    - Copy the relevant [changelog](https://github.com/ipfs/kubo/blob/release/docs/changelogs/) into the release description.
+      - Keep the release notes as trim as possible (e.g. remove top headers where possible, [example](https://github.com/ipfs/kubo/releases/tag/v0.15.0))
+  - [ ] Synchronize release artifacts by running [sync-release-assets](https://github.com/ipfs/kubo/actions/workflows/sync-release-assets.yml) workflow.
+  - [ ] Announce the release
+    - [ ] Add a link to the release to this release issue as a comment.
+    - [ ] Create a new post on [IPFS Discourse](https://discuss.ipfs.tech). ([example](https://discuss.ipfs.tech/t/kubo-v0-16-0-release-is-out/15286))
+      - Use `Kubo vX.Y.Z Release is out!` as the title.
+      - Use `kubo` and `go-ipfs` as topics.
+      - Repeat the title as a heading (`##`) in the description.
+      - Link to the GitHub Release, binaries on IPNS, docker pull command and release notes in the description.
+    - [ ] Pin the topic globally so that it stays at the top of the category.
+    - [ ] If there is no more important banner currently set on Discourse (e.g. IPFS Camp announcement), make the topic into a banner.
+    - [ ] Check if Discourse post was automatically copied to:
+      - [ ] IPFS Discord #ipfs-chatter
+      - [ ] FIL Slack #ipfs-chatter
+      - [ ] Matrix
+  - [ ] Add a link from release notes to Discuss post (like we did here: https://github.com/ipfs/kubo/releases/tag/v0.15.0)
+  - [ ] Update the draft PR created for [interop](https://github.com/ipfs/interop) to use the new release and mark it as ready for review.
+  - [ ] Update the draft PR created for [IPFS Desktop](https://github.com/ipfs-shipyard/ipfs-desktop) to use the new release and mark it as ready for review.
+  - [ ] Update docs
+    - [ ] Run https://github.com/ipfs/ipfs-docs/actions/workflows/update-on-new-ipfs-tag.yml to generate a PR to the docs repo
+    - [ ] Merge the auto-created PR in https://github.com/ipfs/ipfs-docs/pulls ([example](https://github.com/ipfs/ipfs-docs/pull/1263))
+  - [ ] Get the blog post created and shared
+    - [ ] Submit a request for blog post creation using [the form](https://airtable.com/shrNH8YWole1xc70I).
+      - Notify marketing in #shared-pl-marketing-requests about the blog entry request (since the form tends to go to spam; [example]([example](https://filecoinproject.slack.com/archives/C018EJ8LWH1/p1664885305374909))).
+      - Don't mark this as done until the blog entry is live.
+    - [ ] Share the blog post
+      - [ ] Twitter (request in Filecoin Slack channel #shared-pl-marketing-requests; [example](https://filecoinproject.slack.com/archives/C018EJ8LWH1/p1664903524843269?thread_ts=1664885305.374909&cid=C018EJ8LWH1))
+      - [ ] [Reddit](https://reddit.com/r/ipfs)
+- [ ] **Stage 6 - Post-Release**
   - [ ] Merge the `release` branch back into `master`, ignoring the changes to `version.go` (keep the `-dev` version from master).
   - [ ] Create an issue using this release issue template for the _next_ release.
-  - [ ] Make sure any last-minute changelog updates from the blog post make it back into the CHANGELOG.
-  - [ ] Mark PR draft created for IPFS Desktop as ready for review.
-  
-## ⁉️ Do you have questions?
-
-The best place to ask your questions about IPFS, how it works and what you can do with it is at [discuss.ipfs.io](http://discuss.ipfs.io). We are also available at the `#ipfs` channel on Freenode, which is also [accessible through our Matrix bridge](https://riot.im/app/#/room/#freenode_#ipfs:matrix.org).
-
-## Release improvements for next time
-
-< Add any release improvements that were observed this cycle here so they can get incorporated into future releases. >
-
-## Items for a separate comment
-
-< Do these as a separate comment to avoid the main issue from getting too large and checkbox updates taking too long. >
-
-### Changelog
-
-< changelog generated by bin/mkreleaselog > (add it to a separated comment if it is too big)
-
-### ❤️ Contributors
+  - [ ] Close this release issue.
 
-< list generated by bin/mkreleaselog >
+## How to contribute?
 
 Would you like to contribute to the IPFS project and don't know how? Well, there are a few places you can get started:
 
diff --git a/docs/changelogs/v0.17.md b/docs/changelogs/v0.17.md
new file mode 100644
index 0000000000000000000000000000000000000000..4d4bde3c170b1a0115a155a7216aab2ca9492558
--- /dev/null
+++ b/docs/changelogs/v0.17.md
@@ -0,0 +1,212 @@
+# Kubo changelog v0.17
+
+## v0.17.0
+
+### Overview
+
+Below is an outline of all that is in this release, so you get a sense of all that's included.
+
+- [Kubo changelog v0.17](#kubo-changelog-v017)
+  - [v0.17.0](#v0170)
+    - [Overview](#overview)
+    - [🔦 Highlights](#-highlights)
+      - [libp2p resource management enabled by default](#libp2p-resource-management-enabled-by-default)
+      - [Implicit connection manager limits](#implicit-connection-manager-limits)
+      - [TAR Response Format on Gateways](#tar-response-format-on-gateways)
+      - [Dialling `/wss` peer behind a reverse proxy](#dialling-wss-peer-behind-a-reverse-proxy)
+    - [Changelog](#changelog)
+    - [Contributors](#contributors)
+
+### 🔦 Highlights
+
+<!-- TODO -->
+
+#### libp2p resource management enabled by default
+
+To help protect nodes from DoS (resource exhaustion) and eclipse attacks,
+go-libp2p released a [Network Resource Manager](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager) with a host of improvements throughout 2022.
+
+Kubo first [exposed this functionality in Kubo 0.13](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.13.md#-libp2p-network-resource-manager-swarmresourcemgr),
+but it was disabled by default.
+
+The resource manager is now enabled by default to protect nodes.
+The defaults balance providing protection from various attacks while still enabling normal usecases to work as expected.
+
+If you want to adjust the defaults, then you can:
+1. bound the amount of memory and file descriptors that libp2p will use with [Swarm.ResourceMgr.MaxMemory](https://github.com/ipfs/go-ipfs/blob/master/docs/config.md#swarmresourcemgrmaxmemory)
+and [Swarm.ResourceMgr.MaxFileDescriptors](https://github.com/ipfs/go-ipfs/blob/master/docs/config.md#swarmresourcemgrmaxfiledescriptors) and/or
+2. override any specific resource scopes/limits with [Swarm.ResourceMgr.Limits](https://github.com/ipfs/go-ipfs/blob/master/docs/config.md#swarmresourcemgrlimits)
+
+See [Swarm.ResourceMgr](https://github.com/ipfs/go-ipfs/blob/master/docs/config.md#swarmresourcemgr) for
+1. what limits are set by default,
+2. example override configuration,
+3. how to access prometheus metrics and view grafana dashboards of resource usage, and
+4. how to set explicit "allow lists" to protect against eclipse attacks.
+
+#### Implicit connection manager limits
+
+Starting with this release, `ipfs init` will no longer store the default
+[Connection Manager](https://github.com/ipfs/kubo/blob/master/docs/config.md#swarmconnmgr)
+limits in the user config under `Swarm.ConnMgr`.
+
+Users are still free to use this setting to set custom values, but for most use
+cases, the defaults provided with the latest Kubo release should be sufficient.
+
+To remove any custom limits and switch to the implicit defaults managed by Kubo:
+
+```console
+$ ipfs config --json Swarm.ConnMgr '{}'
+```
+
+We will be adjusting defaults in the future releases.
+
+#### TAR Response Format on Gateways
+
+Implemented [IPIP-288](https://github.com/ipfs/specs/pull/288) which adds
+support for requesting deserialized UnixFS directory as a TAR stream.
+
+HTTP clients can request TAR response by passing the `?format=tar` URL
+parameter, or setting `Accept: application/x-tar` HTTP header:
+
+```console
+$ export DIR_CID=bafybeigccimv3zqm5g4jt363faybagywkvqbrismoquogimy7kvz2sj7sq
+$ curl -H "Accept: application/x-tar" "http://127.0.0.1:8080/ipfs/$DIR_CID" > dir.tar
+$ curl "http://127.0.0.1:8080/ipfs/$DIR_CID?format=tar" | tar xv
+bafybeigccimv3zqm5g4jt363faybagywkvqbrismoquogimy7kvz2sj7sq
+bafybeigccimv3zqm5g4jt363faybagywkvqbrismoquogimy7kvz2sj7sq/1 - Barrel - Part 1 - alt.txt
+bafybeigccimv3zqm5g4jt363faybagywkvqbrismoquogimy7kvz2sj7sq/1 - Barrel - Part 1 - transcript.txt
+bafybeigccimv3zqm5g4jt363faybagywkvqbrismoquogimy7kvz2sj7sq/1 - Barrel - Part 1.png
+```
+
+#### Dialling `/wss` peer behind a reverse proxy
+
+This release resolves a regression introduced in Kubo 0.16, making it possible
+again to connect to a peer over a WebSockets endpoint (`/wss`) that is
+deployed behind a reverse proxy.
+
+More details in [go-libp2p release notes](https://github.com/libp2p/go-libp2p/releases/tag/v0.23.3).
+
+### Changelog
+
+<details><summary>Full Changelog</summary>
+
+- github.com/ipfs/kubo:
+  - chore: bump version to v0.17.0 ([ipfs/kubo#9427](https://github.com/ipfs/kubo/pull/9427))
+  - chore: bump version to v0.17.0-rc2 ([ipfs/kubo#9414](https://github.com/ipfs/kubo/pull/9414))
+  - Doc improvements and changelog for resource manager (#9413) ([ipfs/kubo#9413](https://github.com/ipfs/kubo/pull/9413))
+  - fix(docs): typo
+  - docs: document /wss fixes in 0.17
+  - refactor(config): remove Swarm.ConnMgr defaults
+  - fix(config): skip nulls in ResourceMgr
+  - Apply go fmt
+  - Update core/node/libp2p/rcmgr_defaults.go
+  - Remove limitation by HighWater param.
+  - Fix RM errors when acceleratedDHT is active
+  - docs: Deprecate Reframe on docs. (#9401) ([ipfs/kubo#9401](https://github.com/ipfs/kubo/pull/9401))
+  - chore: bump version to v0.17.0-rc1 ([ipfs/kubo#9394](https://github.com/ipfs/kubo/pull/9394))
+  - feat: Improve ResourceManager UX (#9338) ([ipfs/kubo#9338](https://github.com/ipfs/kubo/pull/9338))
+  - feat: ipfs-webui 2.20.0
+  - docs: note log tail is broken (#9383) ([ipfs/kubo#9383](https://github.com/ipfs/kubo/pull/9383))
+  - feat(gateway): TAR response format (#9029) ([ipfs/kubo#9029](https://github.com/ipfs/kubo/pull/9029))
+  - fix: error when using huge json limit file
+  - chore: go-multicodec v0.7.0
+  - fix: remove old unused buggy coredag code
+  - feat: Add command line completion for fish
+  - chore: delete snap configuration ([ipfs/kubo#9352](https://github.com/ipfs/kubo/pull/9352))
+  - docs: update scoop package
+  - docs: init release issue template improvement process v0.16.0 ([ipfs/kubo#9283](https://github.com/ipfs/kubo/pull/9283))
+  - feat: add delegated routing metrics (#9354) ([ipfs/kubo#9354](https://github.com/ipfs/kubo/pull/9354))
+  - chore: create v0.17.md changelog ([ipfs/kubo#9353](https://github.com/ipfs/kubo/pull/9353))
+  - docs: pin remote arg
+  - feat: webui@v2.19.0
+  - test(car): export/import of (dag-)cbor/json codecs
+  - add refs local alias repo ls (#9320) ([ipfs/kubo#9320](https://github.com/ipfs/kubo/pull/9320))
+  - docs(cmds): Clarify block fetching of refs endpoint.
+  - chore(cmds): dag import: use ipld legacy decode ([ipfs/kubo#9219](https://github.com/ipfs/kubo/pull/9219))
+  - fix ipfs swarm peering crash in offline mode (#9261) ([ipfs/kubo#9261](https://github.com/ipfs/kubo/pull/9261))
+  - feat: remove provider delay interval in bitswap (#9053) ([ipfs/kubo#9053](https://github.com/ipfs/kubo/pull/9053))
+  - feat: --reset flag on swarm limit command (#9310) ([ipfs/kubo#9310](https://github.com/ipfs/kubo/pull/9310))
+  - fix: add InlineDNSLink flag to PublicGateways config (#9328) ([ipfs/kubo#9328](https://github.com/ipfs/kubo/pull/9328))
+  - docs: Fix typo and grammar in README
+  - ci: add stylecheck to golangci-lint (#9334) ([ipfs/kubo#9334](https://github.com/ipfs/kubo/pull/9334))
+  - Fix: `swarm stats all` command
+  - Merge release v0.16.0 back into master ([ipfs/kubo#9324](https://github.com/ipfs/kubo/pull/9324))
+  - fix: Set default Methods value to nil
+  - docs: add WebTransport docs ([ipfs/kubo#9314](https://github.com/ipfs/kubo/pull/9314))
+  - chore: bump version to 0.17.0-dev
+- github.com/ipfs/go-delegated-routing (v0.6.0 -> v0.7.0):
+  - Release v0.7.0
+  - feat: add latency & count metrics for content routing client (#59) ([ipfs/go-delegated-routing#59](https://github.com/ipfs/go-delegated-routing/pull/59))
+  - docs: add basic readme ([ipfs/go-delegated-routing#57](https://github.com/ipfs/go-delegated-routing/pull/57))
+  - sync: update CI config files ([ipfs/go-delegated-routing#40](https://github.com/ipfs/go-delegated-routing/pull/40))
+  - added link to reframe blog post (#54) ([ipfs/go-delegated-routing#54](https://github.com/ipfs/go-delegated-routing/pull/54))
+- github.com/ipfs/go-ipfs-files (v0.1.1 -> v0.2.0):
+  - Release v0.2.0
+  - fix: error when TAR has files outside of root (#56) ([ipfs/go-ipfs-files#56](https://github.com/ipfs/go-ipfs-files/pull/56))
+  - sync: update CI config files ([ipfs/go-ipfs-files#55](https://github.com/ipfs/go-ipfs-files/pull/55))
+  - chore(Directory): add DirIterator API restriction: iterate only once
+- github.com/ipfs/go-unixfs (v0.4.0 -> v0.4.1):
+  - Update version.json
+  - Fix: panic when childer is nil (#127) ([ipfs/go-unixfs#127](https://github.com/ipfs/go-unixfs/pull/127))
+  - sync: update CI config files (#125) ([ipfs/go-unixfs#125](https://github.com/ipfs/go-unixfs/pull/125))
+- github.com/ipld/go-ipld-prime (v0.18.0 -> v0.19.0):
+  - Prepare v0.19.0
+  - fix: correct json codec links & bytes handling
+  - test(basicnode): increase test coverage for int and map types (#454) ([ipld/go-ipld-prime#454](https://github.com/ipld/go-ipld-prime/pull/454))
+  - fix: remove reliance on ioutil
+  - run gofmt -s
+  - bump go.mod to Go 1.18 and run go fix
+  - feat: add kinded union to gendemo
+- github.com/libp2p/go-libp2p (v0.23.2 -> v0.23.4):
+  - Release v0.23.4 (#1864) ([libp2p/go-libp2p#1864](https://github.com/libp2p/go-libp2p/pull/1864))
+  - release v0.23.3
+  - websocket: set the HTTP host header in WSS
+- github.com/libp2p/go-netroute (v0.2.0 -> v0.2.1):
+  - v0.2.1 ([libp2p/go-netroute#27](https://github.com/libp2p/go-netroute/pull/27))
+  - fix(phys-addr-length): fix physical address length mismatch ([libp2p/go-netroute#29](https://github.com/libp2p/go-netroute/pull/29))
+  - compare priority if route rule's dst mask is same size
+  - compare priority if route rule's dst mask is same size
+  - sync: update CI config files (#24) ([libp2p/go-netroute#24](https://github.com/libp2p/go-netroute/pull/24))
+- github.com/marten-seemann/qpack (v0.2.1 -> v0.3.0):
+  - update to Ginkgo v2 (#30) ([marten-seemann/qpack#30](https://github.com/marten-seemann/qpack/pull/30))
+  - return write error when encoding header fields (#28) ([marten-seemann/qpack#28](https://github.com/marten-seemann/qpack/pull/28))
+  - update Go versions (#29) ([marten-seemann/qpack#29](https://github.com/marten-seemann/qpack/pull/29))
+  - remove CircleCI build status from README
+  - add link to QPACK RFC to README
+  - remove build constraint from fuzzer ([marten-seemann/qpack#24](https://github.com/marten-seemann/qpack/pull/24))
+- github.com/multiformats/go-multicodec (v0.6.0 -> v0.7.0):
+  - feat: update ./multicodec/table.csv ([multiformats/go-multicodec#71](https://github.com/multiformats/go-multicodec/pull/71))
+
+</details>
+
+### Contributors
+
+| Contributor | Commits | Lines ± | Files Changed |
+|-------------|---------|---------|---------------|
+| Antonio Navarro Perez | 11 | +780/-987 | 31 |
+| Marcin Rataj | 14 | +791/-543 | 26 |
+| web3-bot | 7 | +393/-427 | 71 |
+| galargh | 20 | +309/-277 | 21 |
+| Gus Eggert | 5 | +358/-222 | 58 |
+| Henrique Dias | 3 | +409/-30 | 13 |
+| Dustin Long | 1 | +314/-0 | 2 |
+| Marco Munizaga | 2 | +211/-46 | 11 |
+| Rod Vagg | 4 | +188/-62 | 13 |
+| Jorropo | 2 | +4/-219 | 5 |
+| Steve Loeppky | 1 | +115/-72 | 4 |
+| Andreas Källberg | 1 | +145/-5 | 5 |
+| Lucas Molas | 3 | +76/-53 | 9 |
+| snyh | 2 | +36/-18 | 2 |
+| Piotr Galar | 2 | +31/-4 | 2 |
+| Ondrej Kokes | 1 | +25/-4 | 2 |
+| Marten Seemann | 6 | +14/-14 | 14 |
+| Yann Autissier | 1 | +14/-4 | 1 |
+| maxos | 1 | +8/-1 | 2 |
+| reidlw | 1 | +1/-4 | 1 |
+| Russell Dempsey | 2 | +4/-1 | 2 |
+| Ian Davis | 1 | +4/-0 | 2 |
+| Daniel Norman | 1 | +3/-1 | 1 |
+| Will Scott | 1 | +1/-1 | 1 |
+| Nikhilesh Susarla | 1 | +2/-0 | 2 |
+| Jamie Wilkinson | 1 | +1/-1 | 1 |
+| Will | 1 | +0/-1 | 1 |
diff --git a/docs/command-completion.md b/docs/command-completion.md
index 5ca1edd168a9791e74517e9d19b1320a589c1750..b84483e616e94b5ac4fdd1f714552a3770adb5ca 100644
--- a/docs/command-completion.md
+++ b/docs/command-completion.md
@@ -11,3 +11,16 @@ The simplest way to "eval" the completions logic:
 
 To install the completions permanently, they can be moved to
 `/etc/bash_completion.d` or sourced from your `~/.bashrc` file.
+
+## Fish
+
+The fish shell is also supported:
+
+The simplest way to use the completions logic:
+
+```bash
+> ipfs commands completion fish | source
+```
+
+To install the completions permanently, they can be moved to
+`/etc/fish/completions` or `~/.config/fish/completions` or sourced from your `~/.config/fish/config.fish` file.
diff --git a/docs/config.md b/docs/config.md
index 80fc0f243432eba97c5e6d902a06379154180821..5027b67b7c2522366ab8993a40a511bf2a1c47cf 100644
--- a/docs/config.md
+++ b/docs/config.md
@@ -59,6 +59,7 @@ config file at runtime.
       - [`Gateway.PublicGateways: Paths`](#gatewaypublicgateways-paths)
       - [`Gateway.PublicGateways: UseSubdomains`](#gatewaypublicgateways-usesubdomains)
       - [`Gateway.PublicGateways: NoDNSLink`](#gatewaypublicgateways-nodnslink)
+      - [`Gateway.PublicGateways: InlineDNSLink`](#gatewaypublicgateways-inlinednslink)
       - [Implicit defaults of `Gateway.PublicGateways`](#implicit-defaults-of-gatewaypublicgateways)
     - [`Gateway` recipes](#gateway-recipes)
   - [`Identity`](#identity)
@@ -140,6 +141,8 @@ config file at runtime.
         - [`Swarm.ConnMgr.GracePeriod`](#swarmconnmgrgraceperiod)
     - [`Swarm.ResourceMgr`](#swarmresourcemgr)
       - [`Swarm.ResourceMgr.Enabled`](#swarmresourcemgrenabled)
+      - [`Swarm.ResourceMgr.MaxMemory`](#swarmresourcemgrmaxmemory)
+      - [`Swarm.ResourceMgr.MaxFileDescriptors`](#swarmresourcemgrmaxfiledescriptors)
       - [`Swarm.ResourceMgr.Limits`](#swarmresourcemgrlimits)
       - [`Swarm.ResourceMgr.Allowlist`](#swarmresourcemgrallowlist)
     - [`Swarm.Transports`](#swarmtransports)
@@ -149,7 +152,7 @@ config file at runtime.
       - [`Swarm.Transports.Network.QUIC`](#swarmtransportsnetworkquic)
       - [`Swarm.Transports.Network.Relay`](#swarmtransportsnetworkrelay)
       - [`Swarm.Transports.Network.WebTransport`](#swarmtransportsnetworkwebtransport)
-        - [`How to enable WebTransport`](#how-to-enable-webtransport)
+        - [How to enable WebTransport](#how-to-enable-webtransport)
     - [`Swarm.Transports.Security`](#swarmtransportssecurity)
       - [`Swarm.Transports.Security.TLS`](#swarmtransportssecuritytls)
       - [`Swarm.Transports.Security.SECIO`](#swarmtransportssecuritysecio)
@@ -240,9 +243,15 @@ documented in `ipfs config profile --help`.
 
 - `lowpower`
 
-  Reduces daemon overhead on the system. May affect node
+  Reduces daemon overhead on the system. Affects node
   functionality - performance of content discovery and data
-  fetching may be degraded.
+  fetching may be degraded. Local data won't be announced on routing systems like DHT.
+
+  - `Swarm.ConnMgr` set to maintain minimum number of p2p connections at a time.
+  - Disables [`Reprovider`](#reprovider) service → no CID will be announced on DHT and other routing systems(!)
+  - Disables AutoNAT.
+
+  Use this profile with caution.
 
 ## Types
 
@@ -767,6 +776,26 @@ Default: `false` (DNSLink lookup enabled by default for every defined hostname)
 
 Type: `bool`
 
+#### `Gateway.PublicGateways: InlineDNSLink`
+
+An optional flag to explicitly configure whether subdomain gateway's redirects
+(enabled by `UseSubdomains: true`) should always inline a DNSLink name (FQDN)
+into a single DNS label:
+
+```
+//example.com/ipns/example.net → HTTP 301 → //example-net.ipns.example.com
+```
+
+DNSLink name inlining allows for HTTPS on public subdomain gateways with single
+label wildcard TLS certs (also enabled when passing `X-Forwarded-Proto: https`),
+and provides disjoint Origin per root CID when special rules like
+https://publicsuffix.org, or a custom localhost logic in browsers like Brave
+has to be applied.
+
+Default: `false`
+
+Type: `flag`
+
 #### Implicit defaults of `Gateway.PublicGateways`
 
 Default entries for `localhost` hostname and loopback IPs are always present.
@@ -1276,8 +1305,7 @@ Contains options for content, peer, and IPNS routing mechanisms.
 Map of additional Routers.
 
 Allows for extending the default routing (DHT) with alternative Router
-implementations, such as custom DHTs and delegated routing based
-on the [reframe protocol](https://github.com/ipfs/specs/tree/main/reframe#readme).
+implementations.
 
 The map key is a name of a Router, and the value is its configuration.
 
@@ -1293,7 +1321,7 @@ It specifies the routing type that will be created.
 
 Currently supported types:
 
-- `reframe` (delegated routing based on the [reframe protocol](https://github.com/ipfs/specs/tree/main/reframe#readme))
+- `reframe` **(DEPRECATED)** (delegated routing based on the [reframe protocol](https://github.com/ipfs/specs/tree/main/reframe#readme))
 - `dht`
 - `parallel` and `sequential`: Helpers that can be used to run several routers sequentially or in parallel.
 
@@ -1305,7 +1333,7 @@ Type: `string`
 
 Parameters needed to create the specified router. Supported params per router type:
 
-Reframe:
+Reframe **(DEPRECATED)**:
   - `Endpoint` (mandatory): URL that will be used to connect to a specified router.
 
 DHT:
@@ -1328,21 +1356,6 @@ Sequential:
     - `IgnoreErrors:bool`: It will specify if that router should be ignored if an error occurred.
   - `Timeout:duration`: Global timeout.  It accepts strings compatible with Go `time.ParseDuration(string)`.
 
-**Examples:**
-
-To add router provided by _Store the Index_ team at [cid.contact](https://cid.contact):
-
-```console
-$ ipfs config Routing.Routers.CidContact --json '{
-  "Type": "reframe",
-  "Parameters": {
-    "Endpoint": "https://cid.contact/reframe"
-  }
-}'
-```
-
-Anyone can create and run their own Reframe endpoint, and experiment with custom routing logic. See [`someguy`](https://github.com/aschmahmann/someguy) example, which proxies requests to BOTH the IPFS Public DHT AND an Indexer node. Protocol Labs provides a public instance at `https://routing.delegate.ipfs.io/reframe`.
-
 Default: `{}` (use the safe implicit defaults)
 
 Type: `object[string->string]`
@@ -1358,44 +1371,25 @@ Type: `object[string->object]`
 
 **Examples:**
 
-To use the previously added `CidContact` reframe router on all methods:
-
-```console
-$ ipfs config Routing.Methods --json '{
-      "find-peers": {
-        "RouterName": "CidContact"
-      },
-      "find-providers": {
-        "RouterName": "CidContact"
-      },
-      "get-ipns": {
-        "RouterName": "CidContact"
-      },
-      "provide": {
-        "RouterName": "CidContact"
-      },
-      "put-ipns": {
-        "RouterName": "CidContact"
-      }
-    }'
-```
-Complete example using 3 Routers, reframe, DHT and parallel.
+Complete example using 2 Routers, DHT (LAN/WAN) and parallel.
 
 ```
 $ ipfs config Routing.Type --json '"custom"'
 
-$ ipfs config Routing.Routers.CidContact --json '{
-  "Type": "reframe",
+$ ipfs config Routing.Routers.WanDHT --json '{
+  "Type": "dht",
   "Parameters": {
-    "Endpoint": "https://cid.contact/reframe"
+    "Mode": "auto",
+    "PublicIPNetwork": true,
+    "AcceleratedDHTClient": false
   }
 }'
 
-$ ipfs config Routing.Routers.WanDHT --json '{
+$ ipfs config Routing.Routers.LanDHT --json '{
   "Type": "dht",
   "Parameters": {
     "Mode": "auto",
-    "PublicIPNetwork": true,
+    "PublicIPNetwork": false,
     "AcceleratedDHTClient": false
   }
 }'
@@ -1405,7 +1399,7 @@ $ ipfs config Routing.Routers.ParallelHelper --json '{
   "Parameters": {
     "Routers": [
         {
-        "RouterName" : "CidContact",
+        "RouterName" : "LanDHT",
         "IgnoreErrors" : true,
         "Timeout": "3s"
         },
@@ -1430,7 +1424,7 @@ ipfs config Routing.Methods --json '{
         "RouterName": "ParallelHelper"
       },
       "provide": {
-        "RouterName": "WanDHT"
+        "RouterName": "ParallelHelper"
       },
       "put-ipns": {
         "RouterName": "ParallelHelper"
@@ -1707,7 +1701,8 @@ be configured to keep. Kubo currently supports two connection managers:
 * none: never close idle connections.
 * basic: the default connection manager.
 
-Default: basic
+By default, this section is empty and the implicit defaults defined below
+are used.
 
 #### `Swarm.ConnMgr.Type`
 
@@ -1716,8 +1711,7 @@ management) and `"basic"`.
 
 Default: "basic".
 
-Type: `string` (when unset or `""`, the default connection manager is applied
-and all `ConnMgr` fields are ignored).
+Type: `optionalString` (default when unset or empty)
 
 #### Basic Connection Manager
 
@@ -1756,7 +1750,7 @@ trim down to.
 
 Default: `600`
 
-Type: `integer`
+Type: `optionalInteger`
 
 ##### `Swarm.ConnMgr.HighWater`
 
@@ -1766,7 +1760,7 @@ towards this limit.
 
 Default: `900`
 
-Type: `integer`
+Type: `optionalInteger`
 
 ##### `Swarm.ConnMgr.GracePeriod`
 
@@ -1775,52 +1769,112 @@ by the connection manager.
 
 Default: `"20s"`
 
-Type: `duration`
+Type: `optionalDuration`
 
 ### `Swarm.ResourceMgr`
 
-**EXPERIMENTAL: `Swarm.ResourceMgr` configuration will change in future release**
-
-The [libp2p Network Resource Manager](https://github.com/libp2p/go-libp2p-resource-manager#readme) allows setting limits per a scope,
+The [libp2p Network Resource Manager](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#readme) allows setting limits per [Resource Scope](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#resource-scopes),
 and tracking recource usage over time.
 
+##### Levels of Configuration
+
+libp2p's resource manager provides tremendous flexibility but also adds a lot of complexity.
+There are these levels of limit configuration for resource management protection:
+1. "The user who does nothing" - In this case they get some sane defaults discussed below
+   based on the amount of memory and file descriptors their system has.
+   This should protect the node from many attacks.
+2. "Slightly more advanced user" - They can tweak the default limits discussed below.  
+   Where the defaults aren't good enough, a good set of higher-level "knobs" are exposed to satisfy most use cases
+   without requiring users to wade into all the intricacies of libp2p's resource manager.
+   The "knobs"/inputs are `Swarm.ResourceMgr.MaxMemory` and `Swarm.ResourceMgr.MaxFileDescriptors` as described below. 
+3. "Power user" - They specify all the default limits from below they want override via `Swarm.ResourceMgr.Limits`;
+
+##### Default Limits
+
+With these inputs defined, [resource manager limits](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#limits) are created at the 
+[system](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#the-system-scope), 
+[transient](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#the-transient-scope), 
+and [peer](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#peer-scopes) scopes.
+Other scopes are ignored (by being set to "~infinity".
+
+The reason these scopes are chosen is because:
+- system - This gives us the coarse-grained control we want so we can reason about the system as a whole.
+  It is the backstop, and allows us to reason about resource consumption more easily
+  since don't have think about the interaction of many other scopes.
+- transient - Limiting connections that are in process of being established provides backpressure so not too much work queues up.
+- peer - The peer scope doesn't protect us against intentional DoS attacks.
+  It's just as easy for an attacker to send 100 requests/second with 1 peerId vs. 10 requests/second with 10 peers.
+  We are reliant on the system scope for protection here in the malicious case.
+  The reason for having a peer scope is to protect against unintentional DoS attacks
+  (e.g., bug in a peer which is causing it to "misbehave").
+  In the unintional case, we want to make sure a "misbehaving" node doesn't consume more resources than necessary.
+
+Within these scopes, limits are just set on 
+[memory](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#memory), 
+[file descriptors (FD)](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#file-descriptors), [*inbound* connections](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#connections),
+and [*inbound* streams](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#streams).
+Limits are set based on the inputs above.
+We trust this node to behave properly and thus don't limit *outbound* connection/stream limits.
+We apply any limits that libp2p has for its protocols/services
+since we assume libp2p knows best here.
+
+** libp2p resource monitoring **
+For [monitoring libp2p resource usage](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#monitoring), 
+various `*rcmgr_*` metrics can be accessed as the prometheus endpoint at `{Addresses.API}/debug/metrics/prometheus` (default: `http://127.0.0.1:5001/debug/metrics/prometheus`).  
+There are also [pre-built Grafana dashboards](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager/obs/grafana-dashboards) that can be added to a Grafana instance. 
+
 #### `Swarm.ResourceMgr.Enabled`
 
-**EXPERIMENTAL: `Swarm.ResourceMgr` is in active development, enable it only if you want to provide maintainers with feedback**
+Enables the libp2p Resource Manager using limits based on the defaults and/or other configuration as discussed above.
 
+Default: `true`
+Type: `flag`
 
-Enables the libp2p Network Resource Manager and auguments the default limits
-using user-defined ones in `Swarm.ResourceMgr.Limits` (if present).
+#### `Swarm.ResourceMgr.MaxMemory`
 
-Various `*rcmgr_*` metrics can be accessed as the prometheus endpoint at `{Addresses.API}/debug/metrics/prometheus` (default: `http://127.0.0.1:5001/debug/metrics/prometheus`)
+This is the max amount of memory to allow libp2p to use.
+libp2p's resource manager will prevent additional resource creation while this limit is reached.
+This value is also used to scale the limit on various resources at various scopes 
+when the default limits (discuseed above) are used.
+For example, increasing this value will increase the default limit for incoming connections.
 
-Default: `false`
+Default: `[TOTAL_SYSTEM_MEMORY]/8`
+Type: `optionalBytes`
 
-Type: `flag`
+#### `Swarm.ResourceMgr.MaxFileDescriptors`
+
+This is the maximum number of file descriptors to allow libp2p to use.
+libp2p's resource manager will prevent additional file descriptor consumption while this limit is reached.
+
+This param is ignored on Windows.
+
+Default `[TOTAL_SYSTEM_FILE_DESCRIPTORS]/2`
+Type: `optionalInteger`
 
 #### `Swarm.ResourceMgr.Limits`
 
-**EXPERIMENTAL: `Swarm.ResourceMgr.Limits` configuration will change in future release, exposed here only for convenience**
+Map of resource limits [per scope](https://github.com/libp2p/go-libp2p/tree/master/p2p/host/resource-manager#resource-scopes).
 
-Map of resource limits [per scope](https://github.com/libp2p/go-libp2p-resource-manager#resource-scopes).
+The map supports fields from the [`LimitConfig` struct](https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/limit_defaults.go#L111).
 
-The map supports fields from [`BasicLimiterConfig`](https://github.com/libp2p/go-libp2p-resource-manager/blob/v0.3.0/limit_config.go#L165-L185)
-struct from [go-libp2p-resource-manager](https://github.com/libp2p/go-libp2p-resource-manager#readme).
+[`BaseLimit`s](https://github.com/libp2p/go-libp2p/blob/master/p2p/host/resource-manager/limit.go#L89) can be set for any scope, and within the `BaseLimit`, all limit <key,value>s are optional.
 
-**Example: (format may change in future release)**
+The `Swarm.ResourceMgr.Limits` override the default limits described above. 
+Any override `BaseLimits` or limit <key,value>s from `Swarm.ResourceMgr.Limits`
+that aren't specified will use the default limits.
 
+Example #1: setting limits for a specific scope
 ```json
 {
   "Swarm": {
     "ResourceMgr": {
-      "Enabled": true,
       "Limits": {
         "System": {
+          "Memory": 1073741824,
+          "FD": 512,
           "Conns": 1024,
           "ConnsInbound": 256,
           "ConnsOutbound": 1024,
-          "FD": 512,
-          "Memory": 1073741824,
           "Streams": 16384,
           "StreamsInbound": 4096,
           "StreamsOutbound": 16384
@@ -1831,20 +1885,35 @@ struct from [go-libp2p-resource-manager](https://github.com/libp2p/go-libp2p-res
 }
 ```
 
+Example #2: setting a specific <key,value> limit
+```json
+{
+  "Swarm": {
+    "ResourceMgr": {
+      "Limits": {
+        "Transient": {
+          "ConnsOutbound": 256,
+        }
+      }
+    }
+  }
+}
+```
+
 Current resource usage and a list of services, protocols, and peers can be
 obtained via `ipfs swarm stats --help`
 
-It is also possible to adjust some runtime limits via `ipfs stats limit --help`.
-Changes made via `stats limit` are persisted in `Swarm.ResourceMgr.Limits`.
+It is also possible to adjust some runtime limits via `ipfs swarm limit --help`.
+Changes made via `ipfs swarm limit` are persisted in `Swarm.ResourceMgr.Limits`.
 
-Default: `{}` (use the safe implicit defaults)
+Default: `{}` (use the safe implicit defaults described above)
 
 Type: `object[string->object]`
 
 #### `Swarm.ResourceMgr.Allowlist`
 
 A list of multiaddrs that can bypass normal system limits (but are still limited by the allowlist scope).
-Convenience config around [go-libp2p-resource-manager#Allowlist.Add](https://pkg.go.dev/github.com/libp2p/go-libp2p-resource-manager#Allowlist.Add).
+Convenience config around [go-libp2p-resource-manager#Allowlist.Add](https://pkg.go.dev/github.com/libp2p/go-libp2p/p2p/host/resource-manager#Allowlist.Add).
 
 Default: `[]`
 
@@ -1964,7 +2033,7 @@ Default: Disabled
 Type: `flag`
 
 
-#### How to enable WebTransport
+##### How to enable WebTransport
 
 Thoses steps are temporary and wont be needed once we make it enabled by default.
 
diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod
index 886885cc9ec44a14ca1f5e2f72108a3a0fe9e42c..b7398dbaa127c7b832cb88f22f00f4e6732d6748 100644
--- a/docs/examples/kubo-as-a-library/go.mod
+++ b/docs/examples/kubo-as-a-library/go.mod
@@ -7,10 +7,10 @@ go 1.17
 replace github.com/ipfs/kubo => ./../../..
 
 require (
-	github.com/ipfs/go-ipfs-files v0.1.1
+	github.com/ipfs/go-ipfs-files v0.2.0
 	github.com/ipfs/interface-go-ipfs-core v0.7.0
 	github.com/ipfs/kubo v0.14.0-rc1
-	github.com/libp2p/go-libp2p v0.23.2
+	github.com/libp2p/go-libp2p v0.23.4
 	github.com/multiformats/go-multiaddr v0.7.0
 )
 
@@ -29,7 +29,7 @@ require (
 	github.com/cespare/xxhash v1.1.0 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 	github.com/containerd/cgroups v1.0.4 // indirect
-	github.com/coreos/go-systemd/v22 v22.4.0 // indirect
+	github.com/coreos/go-systemd/v22 v22.5.0 // indirect
 	github.com/crackcomm/go-gitignore v0.0.0-20170627025303-887ab5e44cc3 // indirect
 	github.com/cskr/pubsub v1.0.2 // indirect
 	github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
@@ -42,7 +42,7 @@ require (
 	github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect
 	github.com/flynn/noise v1.0.0 // indirect
 	github.com/francoispqt/gojay v1.2.13 // indirect
-	github.com/fsnotify/fsnotify v1.5.4 // indirect
+	github.com/fsnotify/fsnotify v1.6.0 // indirect
 	github.com/go-logr/logr v1.2.3 // indirect
 	github.com/go-logr/stdr v1.2.2 // indirect
 	github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
@@ -68,7 +68,7 @@ require (
 	github.com/ipfs/go-cid v0.3.2 // indirect
 	github.com/ipfs/go-cidutil v0.1.0 // indirect
 	github.com/ipfs/go-datastore v0.6.0 // indirect
-	github.com/ipfs/go-delegated-routing v0.6.0 // indirect
+	github.com/ipfs/go-delegated-routing v0.7.0 // indirect
 	github.com/ipfs/go-ds-badger v0.3.0 // indirect
 	github.com/ipfs/go-ds-flatfs v0.5.1 // indirect
 	github.com/ipfs/go-ds-leveldb v0.5.0 // indirect
@@ -103,17 +103,17 @@ require (
 	github.com/ipfs/go-namesys v0.5.0 // indirect
 	github.com/ipfs/go-path v0.3.0 // indirect
 	github.com/ipfs/go-peertaskqueue v0.7.1 // indirect
-	github.com/ipfs/go-unixfs v0.4.0 // indirect
+	github.com/ipfs/go-unixfs v0.4.1 // indirect
 	github.com/ipfs/go-unixfsnode v1.4.0 // indirect
 	github.com/ipfs/go-verifcid v0.0.2 // indirect
 	github.com/ipld/edelweiss v0.2.0 // indirect
 	github.com/ipld/go-codec-dagpb v1.4.1 // indirect
-	github.com/ipld/go-ipld-prime v0.18.0 // indirect
+	github.com/ipld/go-ipld-prime v0.19.0 // indirect
 	github.com/jackpal/go-nat-pmp v1.0.2 // indirect
 	github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
 	github.com/jbenet/goprocess v0.1.4 // indirect
-	github.com/klauspost/compress v1.15.10 // indirect
-	github.com/klauspost/cpuid/v2 v2.1.1 // indirect
+	github.com/klauspost/compress v1.15.12 // indirect
+	github.com/klauspost/cpuid/v2 v2.1.2 // indirect
 	github.com/koron/go-ssdp v0.0.3 // indirect
 	github.com/libp2p/go-buffer-pool v0.1.0 // indirect
 	github.com/libp2p/go-cidranger v1.1.0 // indirect
@@ -132,20 +132,20 @@ require (
 	github.com/libp2p/go-mplex v0.7.0 // indirect
 	github.com/libp2p/go-msgio v0.2.0 // indirect
 	github.com/libp2p/go-nat v0.1.0 // indirect
-	github.com/libp2p/go-netroute v0.2.0 // indirect
+	github.com/libp2p/go-netroute v0.2.1 // indirect
 	github.com/libp2p/go-openssl v0.1.0 // indirect
 	github.com/libp2p/go-reuseport v0.2.0 // indirect
 	github.com/libp2p/go-yamux/v4 v4.0.0 // indirect
 	github.com/libp2p/zeroconf/v2 v2.2.0 // indirect
 	github.com/lucas-clemente/quic-go v0.29.1 // indirect
-	github.com/marten-seemann/qpack v0.2.1 // indirect
-	github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
-	github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
+	github.com/marten-seemann/qpack v0.3.0 // indirect
+	github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
+	github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
 	github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
 	github.com/marten-seemann/webtransport-go v0.1.1 // indirect
 	github.com/mattn/go-isatty v0.0.16 // indirect
 	github.com/mattn/go-pointer v0.0.1 // indirect
-	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
+	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/miekg/dns v1.1.50 // indirect
 	github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
 	github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
@@ -157,7 +157,7 @@ require (
 	github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
 	github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
 	github.com/multiformats/go-multibase v0.1.1 // indirect
-	github.com/multiformats/go-multicodec v0.6.0 // indirect
+	github.com/multiformats/go-multicodec v0.7.0 // indirect
 	github.com/multiformats/go-multihash v0.2.1 // indirect
 	github.com/multiformats/go-multistream v0.3.3 // indirect
 	github.com/multiformats/go-varint v0.0.6 // indirect
@@ -169,8 +169,8 @@ require (
 	github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect
-	github.com/prometheus/client_golang v1.13.0 // indirect
-	github.com/prometheus/client_model v0.2.0 // indirect
+	github.com/prometheus/client_golang v1.14.0 // indirect
+	github.com/prometheus/client_model v0.3.0 // indirect
 	github.com/prometheus/common v0.37.0 // indirect
 	github.com/prometheus/procfs v0.8.0 // indirect
 	github.com/raulk/go-watchdog v1.3.0 // indirect
@@ -178,10 +178,6 @@ require (
 	github.com/spaolacci/murmur3 v1.1.0 // indirect
 	github.com/stretchr/objx v0.4.0 // indirect
 	github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
-	github.com/tidwall/gjson v1.14.0 // indirect
-	github.com/tidwall/match v1.1.1 // indirect
-	github.com/tidwall/pretty v1.2.0 // indirect
-	github.com/wI2L/jsondiff v0.2.0 // indirect
 	github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect
 	github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 // indirect
 	github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect
@@ -206,14 +202,14 @@ require (
 	go.uber.org/multierr v1.8.0 // indirect
 	go.uber.org/zap v1.23.0 // indirect
 	go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
-	golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect
-	golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b // indirect
-	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
-	golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5 // indirect
-	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
-	golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 // indirect
-	golang.org/x/text v0.3.7 // indirect
-	golang.org/x/tools v0.1.12 // indirect
+	golang.org/x/crypto v0.1.0 // indirect
+	golang.org/x/exp v0.0.0-20221106115401-f9659909a136 // indirect
+	golang.org/x/mod v0.6.0 // indirect
+	golang.org/x/net v0.1.0 // indirect
+	golang.org/x/sync v0.1.0 // indirect
+	golang.org/x/sys v0.2.0 // indirect
+	golang.org/x/text v0.4.0 // indirect
+	golang.org/x/tools v0.2.0 // indirect
 	golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
 	google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect
 	google.golang.org/grpc v1.47.0 // indirect
diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum
index b3f5b6f434f6b76c74b99d18f3086c6860374f0f..170376b10e1e358571a1ee6e0ddbe0f6eaafd36e 100644
--- a/docs/examples/kubo-as-a-library/go.sum
+++ b/docs/examples/kubo-as-a-library/go.sum
@@ -166,8 +166,9 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
 github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/go-systemd/v22 v22.4.0 h1:y9YHcjnjynCd/DVbg5j9L/33jQM3MxJlbj/zWskzfGU=
 github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@@ -249,8 +250,9 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3
 github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
 github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
@@ -346,8 +348,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 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 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 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/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -363,6 +366,7 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
 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-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -440,6 +444,7 @@ github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=
 github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y=
 github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
 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.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
 github.com/ipfs/bbloom v0.0.1/go.mod h1:oqo8CVWsJFMOZqTglBG4wydCE4IQA/G2/SEofB0rjUI=
@@ -496,8 +501,8 @@ github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh7
 github.com/ipfs/go-datastore v0.5.1/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk=
 github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk=
 github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8=
-github.com/ipfs/go-delegated-routing v0.6.0 h1:+M1siyTB2H4mHzEnbWjepQxlmKbapVWdbYLexSDODpg=
-github.com/ipfs/go-delegated-routing v0.6.0/go.mod h1:FJjhCChfcWK9z6OXo2jwKKJoxq1JlEWG7YTvwaA7UbI=
+github.com/ipfs/go-delegated-routing v0.7.0 h1:43FyMnKA+8XnyX68Fwg6aoGkqrf8NS5aG7p644s26PU=
+github.com/ipfs/go-delegated-routing v0.7.0/go.mod h1:u4zxjUWIe7APUW5ds9CfD0tJX3vM9JhIeNqA8kE4vHE=
 github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
 github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
 github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8=
@@ -558,8 +563,8 @@ github.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uY
 github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s=
 github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
 github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs=
-github.com/ipfs/go-ipfs-files v0.1.1 h1:/MbEowmpLo9PJTEQk16m9rKzUHjeP4KRU9nWJyJO324=
-github.com/ipfs/go-ipfs-files v0.1.1/go.mod h1:8xkIrMWH+Y5P7HvJ4Yc5XWwIW2e52dyXUiC0tZyjDbM=
+github.com/ipfs/go-ipfs-files v0.2.0 h1:z6MCYHQSZpDWpUSK59Kf0ajP1fi4gLCf6fIulVsp8A8=
+github.com/ipfs/go-ipfs-files v0.2.0/go.mod h1:vT7uaQfIsprKktzbTPLnIsd+NGw9ZbYwSq0g3N74u0M=
 github.com/ipfs/go-ipfs-keystore v0.0.2 h1:Fa9xg9IFD1VbiZtrNLzsD0GuELVHUFXCWF64kCPfEXU=
 github.com/ipfs/go-ipfs-keystore v0.0.2/go.mod h1:H49tRmibOEs7gLMgbOsjC4dqh1u5e0R/SWuc2ScfgSo=
 github.com/ipfs/go-ipfs-pinner v0.2.1 h1:kw9hiqh2p8TatILYZ3WAfQQABby7SQARdrdA+5Z5QfY=
@@ -643,8 +648,8 @@ github.com/ipfs/go-peertaskqueue v0.7.1/go.mod h1:M/akTIE/z1jGNXMU7kFB4TeSEFvj68
 github.com/ipfs/go-pinning-service-http-client v0.1.2/go.mod h1:6wd5mjYhXJTiWU8b4RSWPpWdlzE5/csoXV0dWWMjun4=
 github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw=
 github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o=
-github.com/ipfs/go-unixfs v0.4.0 h1:qSyyxfB/OiDdWHYiSbyaqKC7zfSE/TFL0QdwkRjBm20=
-github.com/ipfs/go-unixfs v0.4.0/go.mod h1:I7Nqtm06HgOOd+setAoCU6rf/HgVFHE+peeNuOv/5+g=
+github.com/ipfs/go-unixfs v0.4.1 h1:nmJFKvF+khK03PIWyCxxydD/nkQX315NZDcgvRqMXf0=
+github.com/ipfs/go-unixfs v0.4.1/go.mod h1:2SUDFhUSzrcL408B1qpIkJJ5HznnyTzweViPXUAvkNg=
 github.com/ipfs/go-unixfsnode v1.1.2/go.mod h1:5dcE2x03pyjHk4JjamXmunTMzz+VUtqvPwZjIEkfV6s=
 github.com/ipfs/go-unixfsnode v1.4.0 h1:9BUxHBXrbNi8mWHc6j+5C580WJqtVw9uoeEKn4tMhwA=
 github.com/ipfs/go-unixfsnode v1.4.0/go.mod h1:qc7YFFZ8tABc58p62HnIYbUMwj9chhUuFWmxSokfePo=
@@ -673,8 +678,9 @@ github.com/ipld/go-ipld-prime v0.14.0/go.mod h1:9ASQLwUFLptCov6lIYc70GRB4V7UTyLD
 github.com/ipld/go-ipld-prime v0.14.1/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704nH0RDcQtgTP0=
 github.com/ipld/go-ipld-prime v0.16.0/go.mod h1:axSCuOCBPqrH+gvXr2w9uAOulJqBPhHPT2PjoiiU1qA=
 github.com/ipld/go-ipld-prime v0.17.0/go.mod h1:aYcKm5TIvGfY8P3QBKz/2gKcLxzJ1zDaD+o0bOowhgs=
-github.com/ipld/go-ipld-prime v0.18.0 h1:xUk7NUBSWHEXdjiOu2sLXouFJOMs0yoYzeI5RAqhYQo=
 github.com/ipld/go-ipld-prime v0.18.0/go.mod h1:735yXW548CKrLwVCYXzqx90p5deRJMVVxM9eJ4Qe+qE=
+github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04=
+github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4=
 github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY=
 github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
 github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
@@ -727,14 +733,16 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6
 github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo=
 github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
+github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
+github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
 github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
-github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
 github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/klauspost/cpuid/v2 v2.1.2 h1:XhdX4fqAJUA0yj+kUwMavO0hHrSPAecYdYf1ZmxHvak=
+github.com/klauspost/cpuid/v2 v2.1.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
@@ -793,8 +801,9 @@ github.com/libp2p/go-libp2p v0.16.0/go.mod h1:ump42BsirwAWxKzsCiFnTtN1Yc+DuPu76f
 github.com/libp2p/go-libp2p v0.18.0/go.mod h1:+veaZ9z1SZQhmc5PW78jvnnxZ89Mgvmh4cggO11ETmw=
 github.com/libp2p/go-libp2p v0.20.0/go.mod h1:g0C5Fu+aXXbCXkusCzLycuBowEih3ElmDqtbo61Em7k=
 github.com/libp2p/go-libp2p v0.22.0/go.mod h1:UDolmweypBSjQb2f7xutPnwZ/fxioLbMBxSjRksxxU4=
-github.com/libp2p/go-libp2p v0.23.2 h1:yqyTeKQJyofWXxEv/eEVUvOrGdt/9x+0PIQ4N1kaxmE=
 github.com/libp2p/go-libp2p v0.23.2/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI=
+github.com/libp2p/go-libp2p v0.23.4 h1:hWi9XHSOVFR1oDWRk7rigfyA4XNMuYL20INNybP9LP8=
+github.com/libp2p/go-libp2p v0.23.4/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI=
 github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo=
 github.com/libp2p/go-libp2p-asn-util v0.1.0/go.mod h1:wu+AnM9Ii2KgO5jMmS1rz9dvzTdj8BXqsPR9HR0XB7I=
 github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw=
@@ -1054,8 +1063,9 @@ github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdm
 github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=
 github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A=
 github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ=
-github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4nWRE=
 github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI=
+github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
+github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
 github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0=
 github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
 github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
@@ -1142,8 +1152,9 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
 github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
+github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
+github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
 github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
 github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
 github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
@@ -1156,11 +1167,13 @@ github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXB
 github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s=
 github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI=
 github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
-github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
 github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
+github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
+github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
 github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
-github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU=
 github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
+github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
+github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
 github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
 github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
 github.com/marten-seemann/webtransport-go v0.1.1 h1:TnyKp3pEXcDooTaNn4s9dYpMJ7kMnTp7k5h+SgYP/mc=
@@ -1182,8 +1195,9 @@ github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o
 github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
 github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -1278,8 +1292,9 @@ github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mo
 github.com/multiformats/go-multicodec v0.3.1-0.20211210143421-a526f306ed2c/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ=
 github.com/multiformats/go-multicodec v0.4.1/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ=
 github.com/multiformats/go-multicodec v0.5.0/go.mod h1:DiY2HFaEp5EhEXb/iYzVAunmyX/aSFMxq2KMKfWEues=
-github.com/multiformats/go-multicodec v0.6.0 h1:KhH2kSuCARyuJraYMFxrNO3DqIaYhOdS039kbhgVwpE=
 github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw=
+github.com/multiformats/go-multicodec v0.7.0 h1:rTUjGOwjlhGHbEMbPoSUJowG1spZTVsITRANCjKTUAQ=
+github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw=
 github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
 github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po=
 github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
@@ -1335,6 +1350,14 @@ github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvw
 github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
+github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
+github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
+github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
+github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
+github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
+github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
+github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls=
+github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
 github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@@ -1342,8 +1365,14 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
 github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
-github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
 github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
+github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
+github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
+github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
+github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
+github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
+github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg=
+github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
 github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
 github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
@@ -1396,15 +1425,17 @@ github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66Id
 github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
 github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
 github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
+github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
+github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
+github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
 github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@@ -1530,12 +1561,6 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70
 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
 github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g=
-github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w=
-github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
-github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
-github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 github.com/tj/assert v0.0.3/go.mod h1:Ne6X72Q+TB1AteidzQncjw9PabbMp4PBMZ1k+vd1Pvk=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=
@@ -1549,8 +1574,6 @@ github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2
 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
 github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
 github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
-github.com/wI2L/jsondiff v0.2.0 h1:dE00WemBa1uCjrzQUUTE/17I6m5qAaN0EMFOg2Ynr/k=
-github.com/wI2L/jsondiff v0.2.0/go.mod h1:axTcwtBkY4TsKuV+RgoMhHyHKKFRI6nnjRLi8LLYQnA=
 github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE=
 github.com/warpfork/go-testmark v0.3.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0=
 github.com/warpfork/go-testmark v0.9.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0=
@@ -1722,8 +1745,9 @@ golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
 golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
+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=
@@ -1737,8 +1761,9 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
 golang.org/x/exp v0.0.0-20210615023648-acb5c1269671/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc=
 golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
-golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4=
 golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
+golang.org/x/exp v0.0.0-20221106115401-f9659909a136 h1:Fq7F/w7MAa1KJ5bt2aJ62ihqp9HDcRuyILskkpIAurw=
+golang.org/x/exp v0.0.0-20221106115401-f9659909a136/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
 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-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1764,8 +1789,9 @@ 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.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
 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-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 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=
@@ -1832,8 +1858,10 @@ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su
 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-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5 h1:KafLifaRFIuSJ5C+7CyFJOF9haxKNC1CEIDk8GX6X0k=
 golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.0.0-20220923203811-8be639271d50/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-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1857,8 +1885,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
 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 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
 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-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1948,6 +1977,7 @@ golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/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-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1955,11 +1985,15 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
 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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 h1:ohgcoMbSofXygzo6AD2I1kz3BFmW1QArPYTtwEM3UXc=
-golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 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=
@@ -1967,8 +2001,9 @@ 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.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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 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=
@@ -2033,8 +2068,9 @@ golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
-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/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
 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=
diff --git a/docs/releases.md b/docs/releases.md
index 25111d4b1490c8a3629edf9720340b2846f49c09..d42feea7bc892d90759f3e17669c8ae0c709ee26 100644
--- a/docs/releases.md
+++ b/docs/releases.md
@@ -16,6 +16,7 @@
   - [Security Fix Policy](#security-fix-policy)
   - [Performing a Release](#performing-a-release)
   - [Release Version Numbers (aka semver)](#release-version-numbers-aka-semver)
+  - [_Footnotes_](#footnotes)
 
 ## Release Philosophy
 
@@ -112,5 +113,7 @@ We do not yet retroactively apply fixes to older releases (no Long Term Support
 
 ----------------------------
 
+## _Footnotes_
+
 - <sup>**[1]**</sup> - _early testers_ is an IPFS programme in which members of the community can self-volunteer to help test `kubo` Release Candidates. You find more info about it at [EARLY_TESTERS.md](./EARLY_TESTERS.md)
 - <sup>**[2]**</sup> - A non-trivial change is any change that could potentially introduce an issue not trivially caught by automated testing. This is up to the discretion of the Lead Maintainer but the assumption is that every change is non-trivial unless proven otherwise.
diff --git a/fuse/ipns/ipns_test.go b/fuse/ipns/ipns_test.go
index 06bb0cdfe5903ba59b6dd0aaa281f120e4a3b94b..51ac9518cf45664725a5d502c7875e36127a285a 100644
--- a/fuse/ipns/ipns_test.go
+++ b/fuse/ipns/ipns_test.go
@@ -116,12 +116,12 @@ func setupIpnsTest(t *testing.T, node *core.IpfsNode) (*core.IpfsNode, *mountWra
 		}
 	}
 
-	coreApi, err := coreapi.NewCoreAPI(node)
+	coreAPI, err := coreapi.NewCoreAPI(node)
 	if err != nil {
 		t.Fatal(err)
 	}
 
-	fs, err := NewFileSystem(node.Context(), coreApi, "", "")
+	fs, err := NewFileSystem(node.Context(), coreAPI, "", "")
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/fuse/ipns/ipns_unix.go b/fuse/ipns/ipns_unix.go
index e1840d259644db46f2ce4ebff63445e4db49ef87..c198989191b8bc2c893969f92ad6eabf15ba6118 100644
--- a/fuse/ipns/ipns_unix.go
+++ b/fuse/ipns/ipns_unix.go
@@ -147,25 +147,25 @@ func CreateRoot(ctx context.Context, ipfs iface.CoreAPI, keys map[string]iface.K
 }
 
 // Attr returns file attributes.
-func (*Root) Attr(ctx context.Context, a *fuse.Attr) error {
+func (r *Root) Attr(ctx context.Context, a *fuse.Attr) error {
 	log.Debug("Root Attr")
 	a.Mode = os.ModeDir | 0111 // -rw+x
 	return nil
 }
 
 // Lookup performs a lookup under this node.
-func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
+func (r *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
 	switch name {
 	case "mach_kernel", ".hidden", "._.":
 		// Just quiet some log noise on OS X.
 		return nil, fuse.ENOENT
 	}
 
-	if lnk, ok := s.LocalLinks[name]; ok {
+	if lnk, ok := r.LocalLinks[name]; ok {
 		return lnk, nil
 	}
 
-	nd, ok := s.LocalDirs[name]
+	nd, ok := r.LocalDirs[name]
 	if ok {
 		switch nd := nd.(type) {
 		case *Directory:
@@ -179,7 +179,7 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
 
 	// other links go through ipns resolution and are symlinked into the ipfs mountpoint
 	ipnsName := "/ipns/" + name
-	resolved, err := s.Ipfs.Name().Resolve(ctx, ipnsName)
+	resolved, err := r.Ipfs.Name().Resolve(ctx, ipnsName)
 	if err != nil {
 		log.Warnf("ipns: namesys resolve error: %s", err)
 		return nil, fuse.ENOENT
@@ -189,7 +189,7 @@ func (s *Root) Lookup(ctx context.Context, name string) (fs.Node, error) {
 		return nil, errors.New("invalid path from ipns record")
 	}
 
-	return &Link{s.IpfsRoot + "/" + strings.TrimPrefix(resolved.String(), "/ipfs/")}, nil
+	return &Link{r.IpfsRoot + "/" + strings.TrimPrefix(resolved.String(), "/ipfs/")}, nil
 }
 
 func (r *Root) Close() error {
@@ -270,8 +270,8 @@ func (fi *FileNode) Attr(ctx context.Context, a *fuse.Attr) error {
 }
 
 // Lookup performs a lookup under this node.
-func (s *Directory) Lookup(ctx context.Context, name string) (fs.Node, error) {
-	child, err := s.dir.Child(name)
+func (d *Directory) Lookup(ctx context.Context, name string) (fs.Node, error) {
+	child, err := d.dir.Child(name)
 	if err != nil {
 		// todo: make this error more versatile.
 		return nil, fuse.ENOENT
@@ -290,8 +290,8 @@ func (s *Directory) Lookup(ctx context.Context, name string) (fs.Node, error) {
 }
 
 // ReadDirAll reads the link structure as directory entries
-func (dir *Directory) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
-	listing, err := dir.dir.List(ctx)
+func (d *Directory) ReadDirAll(ctx context.Context) ([]fuse.Dirent, error) {
+	listing, err := d.dir.List(ctx)
 	if err != nil {
 		return nil, err
 	}
@@ -401,8 +401,8 @@ func (fi *File) Forget() {
 	}
 }
 
-func (dir *Directory) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
-	child, err := dir.dir.Mkdir(req.Name)
+func (d *Directory) Mkdir(ctx context.Context, req *fuse.MkdirRequest) (fs.Node, error) {
+	child, err := d.dir.Mkdir(req.Name)
 	if err != nil {
 		return nil, err
 	}
@@ -451,15 +451,15 @@ func (fi *File) Release(ctx context.Context, req *fuse.ReleaseRequest) error {
 	return fi.fi.Close()
 }
 
-func (dir *Directory) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
+func (d *Directory) Create(ctx context.Context, req *fuse.CreateRequest, resp *fuse.CreateResponse) (fs.Node, fs.Handle, error) {
 	// New 'empty' file
 	nd := dag.NodeWithData(ft.FilePBData(nil, 0))
-	err := dir.dir.AddChild(req.Name, nd)
+	err := d.dir.AddChild(req.Name, nd)
 	if err != nil {
 		return nil, nil, err
 	}
 
-	child, err := dir.dir.Child(req.Name)
+	child, err := d.dir.Child(req.Name)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -483,8 +483,8 @@ func (dir *Directory) Create(ctx context.Context, req *fuse.CreateRequest, resp
 	return nodechild, &File{fi: fd}, nil
 }
 
-func (dir *Directory) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
-	err := dir.dir.Unlink(req.Name)
+func (d *Directory) Remove(ctx context.Context, req *fuse.RemoveRequest) error {
+	err := d.dir.Unlink(req.Name)
 	if err != nil {
 		return fuse.ENOENT
 	}
@@ -492,13 +492,13 @@ func (dir *Directory) Remove(ctx context.Context, req *fuse.RemoveRequest) error
 }
 
 // Rename implements NodeRenamer
-func (dir *Directory) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
-	cur, err := dir.dir.Child(req.OldName)
+func (d *Directory) Rename(ctx context.Context, req *fuse.RenameRequest, newDir fs.Node) error {
+	cur, err := d.dir.Child(req.OldName)
 	if err != nil {
 		return err
 	}
 
-	err = dir.dir.Unlink(req.OldName)
+	err = d.dir.Unlink(req.OldName)
 	if err != nil {
 		return err
 	}
diff --git a/fuse/ipns/mount_unix.go b/fuse/ipns/mount_unix.go
index c04cc45a461e75ee26257834f50b66ed8f7166a3..34a8eef5137d2e567b4de27ccb1fff7b51c71f43 100644
--- a/fuse/ipns/mount_unix.go
+++ b/fuse/ipns/mount_unix.go
@@ -12,7 +12,7 @@ import (
 
 // Mount mounts ipns at a given location, and returns a mount.Mount instance.
 func Mount(ipfs *core.IpfsNode, ipnsmp, ipfsmp string) (mount.Mount, error) {
-	coreApi, err := coreapi.NewCoreAPI(ipfs)
+	coreAPI, err := coreapi.NewCoreAPI(ipfs)
 	if err != nil {
 		return nil, err
 	}
@@ -22,12 +22,12 @@ func Mount(ipfs *core.IpfsNode, ipnsmp, ipfsmp string) (mount.Mount, error) {
 		return nil, err
 	}
 
-	allow_other := cfg.Mounts.FuseAllowOther
+	allowOther := cfg.Mounts.FuseAllowOther
 
-	fsys, err := NewFileSystem(ipfs.Context(), coreApi, ipfsmp, ipnsmp)
+	fsys, err := NewFileSystem(ipfs.Context(), coreAPI, ipfsmp, ipnsmp)
 	if err != nil {
 		return nil, err
 	}
 
-	return mount.NewMount(ipfs.Process, fsys, ipnsmp, allow_other)
+	return mount.NewMount(ipfs.Process, fsys, ipnsmp, allowOther)
 }
diff --git a/fuse/mount/fuse.go b/fuse/mount/fuse.go
index 99f374043d216694b36156833ad99c7b6c27ad2e..b1060e6fd705c6c598fdaac7d8e0ebec42759a01 100644
--- a/fuse/mount/fuse.go
+++ b/fuse/mount/fuse.go
@@ -30,7 +30,7 @@ type mount struct {
 
 // Mount mounts a fuse fs.FS at a given location, and returns a Mount instance.
 // parent is a ContextGroup to bind the mount's ContextGroup to.
-func NewMount(p goprocess.Process, fsys fs.FS, mountpoint string, allow_other bool) (Mount, error) {
+func NewMount(p goprocess.Process, fsys fs.FS, mountpoint string, allowOther bool) (Mount, error) {
 	var conn *fuse.Conn
 	var err error
 
@@ -39,7 +39,7 @@ func NewMount(p goprocess.Process, fsys fs.FS, mountpoint string, allow_other bo
 		fuse.AsyncRead(),
 	}
 
-	if allow_other {
+	if allowOther {
 		mountOpts = append(mountOpts, fuse.AllowOther())
 	}
 	conn, err = fuse.Mount(mountpoint, mountOpts...)
diff --git a/fuse/readonly/mount_unix.go b/fuse/readonly/mount_unix.go
index 87e4b33a7c0030219a1c76f034b366403bfe9808..19be37abecb45bd2ccd7cd67d37ea27e7e442464 100644
--- a/fuse/readonly/mount_unix.go
+++ b/fuse/readonly/mount_unix.go
@@ -15,7 +15,7 @@ func Mount(ipfs *core.IpfsNode, mountpoint string) (mount.Mount, error) {
 	if err != nil {
 		return nil, err
 	}
-	allow_other := cfg.Mounts.FuseAllowOther
+	allowOther := cfg.Mounts.FuseAllowOther
 	fsys := NewFileSystem(ipfs)
-	return mount.NewMount(ipfs.Process, fsys, mountpoint, allow_other)
+	return mount.NewMount(ipfs.Process, fsys, mountpoint, allowOther)
 }
diff --git a/fuse/readonly/readonly_unix.go b/fuse/readonly/readonly_unix.go
index a4d6f251301dd9d6d773fde8fa6e5ab40f785c4d..39ca972e59590eb58ad986c6277a254e8c3f3091 100644
--- a/fuse/readonly/readonly_unix.go
+++ b/fuse/readonly/readonly_unix.go
@@ -180,11 +180,11 @@ func (s *Node) Lookup(ctx context.Context, name string) (fs.Node, error) {
 	case os.ErrNotExist, mdag.ErrLinkNotFound:
 		// todo: make this error more versatile.
 		return nil, fuse.ENOENT
+	case nil:
+		// noop
 	default:
 		log.Errorf("fuse lookup %q: %s", name, err)
 		return nil, fuse.EIO
-	case nil:
-		// noop
 	}
 
 	nd, err := s.Ipfs.DAG.Get(ctx, link.Cid)
diff --git a/gc/gc.go b/gc/gc.go
index e3b0fda68f90da6c46a122641fbc3bc405485ed1..851b42d3b32e485933fbd0acffb25beb6e234680 100644
--- a/gc/gc.go
+++ b/gc/gc.go
@@ -167,7 +167,7 @@ func Descendants(ctx context.Context, getLinks dag.GetLinks, set *cid.Set, roots
 	verboseCidError := func(err error) error {
 		if strings.Contains(err.Error(), verifcid.ErrBelowMinimumHashLength.Error()) ||
 			strings.Contains(err.Error(), verifcid.ErrPossiblyInsecureHashFunction.Error()) {
-			err = fmt.Errorf("\"%s\"\nPlease run 'ipfs pin verify'"+
+			err = fmt.Errorf("\"%s\"\nPlease run 'ipfs pin verify'"+ //nolint
 				" to list insecure hashes. If you want to read them,"+
 				" please downgrade your go-ipfs to 0.4.13\n", err)
 			log.Error(err)
diff --git a/go.mod b/go.mod
index 18f145e1175e286db307e1831bbe496a575cc34b..405284f498620d311a530fc4995a6114badc3ceb 100644
--- a/go.mod
+++ b/go.mod
@@ -3,16 +3,17 @@ module github.com/ipfs/kubo
 require (
 	bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc
 	contrib.go.opencensus.io/exporter/prometheus v0.4.0
+	github.com/benbjohnson/clock v1.3.0
 	github.com/blang/semver/v4 v4.0.0
 	github.com/cenkalti/backoff/v4 v4.1.3
 	github.com/ceramicnetwork/go-dag-jose v0.1.0
 	github.com/cespare/xxhash v1.1.0
 	github.com/cheggaaa/pb v1.0.29
-	github.com/coreos/go-systemd/v22 v22.4.0
+	github.com/coreos/go-systemd/v22 v22.5.0
 	github.com/dustin/go-humanize v1.0.0
 	github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302
 	github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5
-	github.com/fsnotify/fsnotify v1.5.4
+	github.com/fsnotify/fsnotify v1.6.0
 	github.com/gabriel-vasile/mimetype v1.4.1
 	github.com/hashicorp/go-multierror v1.1.1
 	github.com/ipfs/go-bitswap v0.10.2
@@ -21,6 +22,7 @@ require (
 	github.com/ipfs/go-cid v0.3.2
 	github.com/ipfs/go-cidutil v0.1.0
 	github.com/ipfs/go-datastore v0.6.0
+	github.com/ipfs/go-delegated-routing v0.7.0
 	github.com/ipfs/go-detect-race v0.0.1
 	github.com/ipfs/go-ds-badger v0.3.0
 	github.com/ipfs/go-ds-flatfs v0.5.1
@@ -35,19 +37,20 @@ require (
 	github.com/ipfs/go-ipfs-cmds v0.8.1
 	github.com/ipfs/go-ipfs-exchange-interface v0.2.0
 	github.com/ipfs/go-ipfs-exchange-offline v0.3.0
-	github.com/ipfs/go-ipfs-files v0.1.1
+	github.com/ipfs/go-ipfs-files v0.2.0
 	github.com/ipfs/go-ipfs-keystore v0.0.2
 	github.com/ipfs/go-ipfs-pinner v0.2.1
 	github.com/ipfs/go-ipfs-posinfo v0.0.1
 	github.com/ipfs/go-ipfs-provider v0.7.1
+	github.com/ipfs/go-ipfs-redirects-file v0.1.1
 	github.com/ipfs/go-ipfs-routing v0.2.1
 	github.com/ipfs/go-ipfs-util v0.0.2
-	github.com/ipfs/go-ipld-cbor v0.0.5
 	github.com/ipfs/go-ipld-format v0.4.0
 	github.com/ipfs/go-ipld-git v0.1.1
 	github.com/ipfs/go-ipld-legacy v0.1.1
 	github.com/ipfs/go-ipns v0.3.0
 	github.com/ipfs/go-log v1.0.5
+	github.com/ipfs/go-log/v2 v2.5.1
 	github.com/ipfs/go-merkledag v0.6.0
 	github.com/ipfs/go-metrics-interface v0.0.1
 	github.com/ipfs/go-metrics-prometheus v0.0.2
@@ -55,7 +58,7 @@ require (
 	github.com/ipfs/go-namesys v0.5.0
 	github.com/ipfs/go-path v0.3.0
 	github.com/ipfs/go-pinning-service-http-client v0.1.2
-	github.com/ipfs/go-unixfs v0.4.0
+	github.com/ipfs/go-unixfs v0.4.1
 	github.com/ipfs/go-unixfsnode v1.4.0
 	github.com/ipfs/go-verifcid v0.0.2
 	github.com/ipfs/interface-go-ipfs-core v0.7.0
@@ -63,13 +66,12 @@ require (
 	github.com/ipld/go-car v0.4.0
 	github.com/ipld/go-car/v2 v2.4.0
 	github.com/ipld/go-codec-dagpb v1.4.1
-	github.com/ipld/go-ipld-prime v0.18.0
+	github.com/ipld/go-ipld-prime v0.19.0
 	github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c
 	github.com/jbenet/go-temp-err-catcher v0.1.0
 	github.com/jbenet/goprocess v0.1.4
 	github.com/libp2p/go-doh-resolver v0.4.0
-	github.com/libp2p/go-libp2p v0.23.2
-	github.com/libp2p/go-libp2p-core v0.20.1 // indirect
+	github.com/libp2p/go-libp2p v0.23.4
 	github.com/libp2p/go-libp2p-http v0.2.1
 	github.com/libp2p/go-libp2p-kad-dht v0.18.0
 	github.com/libp2p/go-libp2p-kbucket v0.4.7
@@ -85,15 +87,14 @@ require (
 	github.com/multiformats/go-multiaddr v0.7.0
 	github.com/multiformats/go-multiaddr-dns v0.3.1
 	github.com/multiformats/go-multibase v0.1.1
-	github.com/multiformats/go-multicodec v0.6.0
+	github.com/multiformats/go-multicodec v0.7.0
 	github.com/multiformats/go-multihash v0.2.1
 	github.com/opentracing/opentracing-go v1.2.0
+	github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58
 	github.com/pkg/errors v0.9.1
-	github.com/prometheus/client_golang v1.13.0
-	github.com/prometheus/common v0.37.0 // indirect
+	github.com/prometheus/client_golang v1.14.0
 	github.com/stretchr/testify v1.8.0
 	github.com/syndtr/goleveldb v1.0.0
-	github.com/wI2L/jsondiff v0.2.0
 	github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1
 	github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7
 	go.opencensus.io v0.23.0
@@ -109,16 +110,9 @@ require (
 	go.uber.org/dig v1.14.1
 	go.uber.org/fx v1.17.1
 	go.uber.org/zap v1.23.0
-	golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e
-	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
-	golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41
-)
-
-require (
-	github.com/benbjohnson/clock v1.3.0
-	github.com/ipfs/go-delegated-routing v0.6.0
-	github.com/ipfs/go-ipfs-redirects-file v0.1.1
-	github.com/ipfs/go-log/v2 v2.5.1
+	golang.org/x/crypto v0.1.0
+	golang.org/x/sync v0.1.0
+	golang.org/x/sys v0.2.0
 )
 
 require (
@@ -167,16 +161,18 @@ require (
 	github.com/ipfs/go-ipfs-delay v0.0.1 // indirect
 	github.com/ipfs/go-ipfs-ds-help v1.1.0 // indirect
 	github.com/ipfs/go-ipfs-pq v0.0.2 // indirect
+	github.com/ipfs/go-ipld-cbor v0.0.5 // indirect
 	github.com/ipfs/go-peertaskqueue v0.7.1 // indirect
 	github.com/ipld/edelweiss v0.2.0 // indirect
 	github.com/jackpal/go-nat-pmp v1.0.2 // indirect
-	github.com/klauspost/compress v1.15.10 // indirect
-	github.com/klauspost/cpuid/v2 v2.1.1 // indirect
+	github.com/klauspost/compress v1.15.12 // indirect
+	github.com/klauspost/cpuid/v2 v2.1.2 // indirect
 	github.com/koron/go-ssdp v0.0.3 // indirect
 	github.com/libp2p/go-buffer-pool v0.1.0 // indirect
 	github.com/libp2p/go-cidranger v1.1.0 // indirect
 	github.com/libp2p/go-flow-metrics v0.1.0 // indirect
 	github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect
+	github.com/libp2p/go-libp2p-core v0.20.1 // indirect
 	github.com/libp2p/go-libp2p-discovery v0.7.0 // indirect
 	github.com/libp2p/go-libp2p-gostream v0.3.0 // indirect
 	github.com/libp2p/go-libp2p-swarm v0.11.0 // indirect
@@ -184,22 +180,22 @@ require (
 	github.com/libp2p/go-mplex v0.7.0 // indirect
 	github.com/libp2p/go-msgio v0.2.0 // indirect
 	github.com/libp2p/go-nat v0.1.0 // indirect
-	github.com/libp2p/go-netroute v0.2.0 // indirect
+	github.com/libp2p/go-netroute v0.2.1 // indirect
 	github.com/libp2p/go-openssl v0.1.0 // indirect
 	github.com/libp2p/go-reuseport v0.2.0 // indirect
 	github.com/libp2p/go-yamux/v4 v4.0.0 // indirect
 	github.com/libp2p/zeroconf/v2 v2.2.0 // indirect
 	github.com/lucas-clemente/quic-go v0.29.1 // indirect
-	github.com/marten-seemann/qpack v0.2.1 // indirect
-	github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect
-	github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect
+	github.com/marten-seemann/qpack v0.3.0 // indirect
+	github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect
+	github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect
 	github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
 	github.com/marten-seemann/webtransport-go v0.1.1 // indirect
 	github.com/mattn/go-colorable v0.1.4 // indirect
 	github.com/mattn/go-isatty v0.0.16 // indirect
 	github.com/mattn/go-pointer v0.0.1 // indirect
 	github.com/mattn/go-runewidth v0.0.4 // indirect
-	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
+	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
 	github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
 	github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
@@ -212,12 +208,14 @@ require (
 	github.com/multiformats/go-varint v0.0.6 // indirect
 	github.com/nxadm/tail v1.4.8 // indirect
 	github.com/onsi/ginkgo v1.16.5 // indirect
+	github.com/onsi/ginkgo/v2 v2.5.0 // indirect
+	github.com/onsi/gomega v1.24.0 // indirect
 	github.com/opencontainers/runtime-spec v1.0.2 // indirect
 	github.com/openzipkin/zipkin-go v0.4.0 // indirect
-	github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/polydawn/refmt v0.0.0-20201211092308-30ac6d18308e // indirect
-	github.com/prometheus/client_model v0.2.0 // indirect
+	github.com/prometheus/client_model v0.3.0 // indirect
+	github.com/prometheus/common v0.37.0 // indirect
 	github.com/prometheus/procfs v0.8.0 // indirect
 	github.com/prometheus/statsd_exporter v0.21.0 // indirect
 	github.com/raulk/go-watchdog v1.3.0 // indirect
@@ -225,9 +223,6 @@ require (
 	github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
 	github.com/spaolacci/murmur3 v1.1.0 // indirect
 	github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e // indirect
-	github.com/tidwall/gjson v1.14.0 // indirect
-	github.com/tidwall/match v1.1.1 // indirect
-	github.com/tidwall/pretty v1.2.0 // indirect
 	github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb // indirect
 	github.com/whyrusleeping/base32 v0.0.0-20170828182744-c30ac30633cc // indirect
 	github.com/whyrusleeping/cbor-gen v0.0.0-20210219115102-f37d292932f2 // indirect
@@ -241,13 +236,13 @@ require (
 	go.uber.org/atomic v1.10.0 // indirect
 	go.uber.org/multierr v1.8.0 // indirect
 	go4.org v0.0.0-20200411211856-f5505b9728dd // indirect
-	golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b // indirect
-	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
-	golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5 // indirect
+	golang.org/x/exp v0.0.0-20221106115401-f9659909a136 // indirect
+	golang.org/x/mod v0.6.0 // indirect
+	golang.org/x/net v0.1.0 // indirect
 	golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
-	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
-	golang.org/x/text v0.3.7 // indirect
-	golang.org/x/tools v0.1.12 // indirect
+	golang.org/x/term v0.1.0 // indirect
+	golang.org/x/text v0.4.0 // indirect
+	golang.org/x/tools v0.2.0 // indirect
 	golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
 	google.golang.org/appengine v1.6.6 // indirect
 	google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1 // indirect
diff --git a/go.sum b/go.sum
index dd9220fc78cd2cb43e9898a87a3c9230dc7dad59..ef0441f6c3a196b5836c3acdb5a05f514a9ca648 100644
--- a/go.sum
+++ b/go.sum
@@ -162,8 +162,8 @@ github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7
 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
-github.com/coreos/go-systemd/v22 v22.4.0 h1:y9YHcjnjynCd/DVbg5j9L/33jQM3MxJlbj/zWskzfGU=
-github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@@ -245,8 +245,8 @@ github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUork
 github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
-github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/gabriel-vasile/mimetype v1.4.1 h1:TRWk7se+TOjCYgRth7+1/OYLNiRNIotknkFtf/dnN7Q=
 github.com/gabriel-vasile/mimetype v1.4.1/go.mod h1:05Vi0w3Y9c/lNvJOdmIwvrrAhX3rYhfQQCaf9VJcv7M=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -345,7 +345,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 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 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -489,8 +489,8 @@ github.com/ipfs/go-datastore v0.4.5/go.mod h1:eXTcaaiN6uOlVCLS9GjJUJtlvJfM3xk23w
 github.com/ipfs/go-datastore v0.5.0/go.mod h1:9zhEApYMTl17C8YDp7JmU7sQZi2/wqiYh73hakZ90Bk=
 github.com/ipfs/go-datastore v0.6.0 h1:JKyz+Gvz1QEZw0LsX1IBn+JFCJQH4SJVFtM4uWU0Myk=
 github.com/ipfs/go-datastore v0.6.0/go.mod h1:rt5M3nNbSO/8q1t4LNkLyUwRs8HupMeN/8O4Vn9YAT8=
-github.com/ipfs/go-delegated-routing v0.6.0 h1:+M1siyTB2H4mHzEnbWjepQxlmKbapVWdbYLexSDODpg=
-github.com/ipfs/go-delegated-routing v0.6.0/go.mod h1:FJjhCChfcWK9z6OXo2jwKKJoxq1JlEWG7YTvwaA7UbI=
+github.com/ipfs/go-delegated-routing v0.7.0 h1:43FyMnKA+8XnyX68Fwg6aoGkqrf8NS5aG7p644s26PU=
+github.com/ipfs/go-delegated-routing v0.7.0/go.mod h1:u4zxjUWIe7APUW5ds9CfD0tJX3vM9JhIeNqA8kE4vHE=
 github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
 github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
 github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8=
@@ -552,8 +552,8 @@ github.com/ipfs/go-ipfs-exchange-offline v0.3.0 h1:c/Dg8GDPzixGd0MC8Jh6mjOwU57uY
 github.com/ipfs/go-ipfs-exchange-offline v0.3.0/go.mod h1:MOdJ9DChbb5u37M1IcbrRB02e++Z7521fMxqCNRrz9s=
 github.com/ipfs/go-ipfs-files v0.0.3/go.mod h1:INEFm0LL2LWXBhNJ2PMIIb2w45hpXgPjNoE7yA8Y1d4=
 github.com/ipfs/go-ipfs-files v0.0.8/go.mod h1:wiN/jSG8FKyk7N0WyctKSvq3ljIa2NNTiZB55kpTdOs=
-github.com/ipfs/go-ipfs-files v0.1.1 h1:/MbEowmpLo9PJTEQk16m9rKzUHjeP4KRU9nWJyJO324=
-github.com/ipfs/go-ipfs-files v0.1.1/go.mod h1:8xkIrMWH+Y5P7HvJ4Yc5XWwIW2e52dyXUiC0tZyjDbM=
+github.com/ipfs/go-ipfs-files v0.2.0 h1:z6MCYHQSZpDWpUSK59Kf0ajP1fi4gLCf6fIulVsp8A8=
+github.com/ipfs/go-ipfs-files v0.2.0/go.mod h1:vT7uaQfIsprKktzbTPLnIsd+NGw9ZbYwSq0g3N74u0M=
 github.com/ipfs/go-ipfs-keystore v0.0.2 h1:Fa9xg9IFD1VbiZtrNLzsD0GuELVHUFXCWF64kCPfEXU=
 github.com/ipfs/go-ipfs-keystore v0.0.2/go.mod h1:H49tRmibOEs7gLMgbOsjC4dqh1u5e0R/SWuc2ScfgSo=
 github.com/ipfs/go-ipfs-pinner v0.2.1 h1:kw9hiqh2p8TatILYZ3WAfQQABby7SQARdrdA+5Z5QfY=
@@ -638,8 +638,8 @@ github.com/ipfs/go-pinning-service-http-client v0.1.2 h1:jdr7KelhL9gNHTU8jbqPMwI
 github.com/ipfs/go-pinning-service-http-client v0.1.2/go.mod h1:6wd5mjYhXJTiWU8b4RSWPpWdlzE5/csoXV0dWWMjun4=
 github.com/ipfs/go-unixfs v0.2.4/go.mod h1:SUdisfUjNoSDzzhGVxvCL9QO/nKdwXdr+gbMUdqcbYw=
 github.com/ipfs/go-unixfs v0.3.1/go.mod h1:h4qfQYzghiIc8ZNFKiLMFWOTzrWIAtzYQ59W/pCFf1o=
-github.com/ipfs/go-unixfs v0.4.0 h1:qSyyxfB/OiDdWHYiSbyaqKC7zfSE/TFL0QdwkRjBm20=
-github.com/ipfs/go-unixfs v0.4.0/go.mod h1:I7Nqtm06HgOOd+setAoCU6rf/HgVFHE+peeNuOv/5+g=
+github.com/ipfs/go-unixfs v0.4.1 h1:nmJFKvF+khK03PIWyCxxydD/nkQX315NZDcgvRqMXf0=
+github.com/ipfs/go-unixfs v0.4.1/go.mod h1:2SUDFhUSzrcL408B1qpIkJJ5HznnyTzweViPXUAvkNg=
 github.com/ipfs/go-unixfsnode v1.1.2/go.mod h1:5dcE2x03pyjHk4JjamXmunTMzz+VUtqvPwZjIEkfV6s=
 github.com/ipfs/go-unixfsnode v1.4.0 h1:9BUxHBXrbNi8mWHc6j+5C580WJqtVw9uoeEKn4tMhwA=
 github.com/ipfs/go-unixfsnode v1.4.0/go.mod h1:qc7YFFZ8tABc58p62HnIYbUMwj9chhUuFWmxSokfePo=
@@ -668,8 +668,8 @@ github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHt
 github.com/ipld/go-ipld-prime v0.14.0/go.mod h1:9ASQLwUFLptCov6lIYc70GRB4V7UTyLD0IJtrDJe6ZM=
 github.com/ipld/go-ipld-prime v0.14.1/go.mod h1:QcE4Y9n/ZZr8Ijg5bGPT0GqYWgZ1704nH0RDcQtgTP0=
 github.com/ipld/go-ipld-prime v0.16.0/go.mod h1:axSCuOCBPqrH+gvXr2w9uAOulJqBPhHPT2PjoiiU1qA=
-github.com/ipld/go-ipld-prime v0.18.0 h1:xUk7NUBSWHEXdjiOu2sLXouFJOMs0yoYzeI5RAqhYQo=
-github.com/ipld/go-ipld-prime v0.18.0/go.mod h1:735yXW548CKrLwVCYXzqx90p5deRJMVVxM9eJ4Qe+qE=
+github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04=
+github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4=
 github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73 h1:TsyATB2ZRRQGTwafJdgEUQkmjOExRV0DNokcihZxbnQ=
 github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY=
 github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
@@ -722,13 +722,13 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
 github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
 github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo=
-github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
+github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
+github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
 github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
-github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
+github.com/klauspost/cpuid/v2 v2.1.2 h1:XhdX4fqAJUA0yj+kUwMavO0hHrSPAecYdYf1ZmxHvak=
+github.com/klauspost/cpuid/v2 v2.1.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
@@ -785,8 +785,8 @@ github.com/libp2p/go-libp2p v0.14.3/go.mod h1:d12V4PdKbpL0T1/gsUNN8DfgMuRPDX8bS2
 github.com/libp2p/go-libp2p v0.14.4/go.mod h1:EIRU0Of4J5S8rkockZM7eJp2S0UrCyi55m2kJVru3rM=
 github.com/libp2p/go-libp2p v0.16.0/go.mod h1:ump42BsirwAWxKzsCiFnTtN1Yc+DuPu76fyMX364/O4=
 github.com/libp2p/go-libp2p v0.18.0/go.mod h1:+veaZ9z1SZQhmc5PW78jvnnxZ89Mgvmh4cggO11ETmw=
-github.com/libp2p/go-libp2p v0.23.2 h1:yqyTeKQJyofWXxEv/eEVUvOrGdt/9x+0PIQ4N1kaxmE=
-github.com/libp2p/go-libp2p v0.23.2/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI=
+github.com/libp2p/go-libp2p v0.23.4 h1:hWi9XHSOVFR1oDWRk7rigfyA4XNMuYL20INNybP9LP8=
+github.com/libp2p/go-libp2p v0.23.4/go.mod h1:s9DEa5NLR4g+LZS+md5uGU4emjMWFiqkZr6hBTY8UxI=
 github.com/libp2p/go-libp2p-asn-util v0.0.0-20200825225859-85005c6cf052/go.mod h1:nRMRTab+kZuk0LnKZpxhOVH/ndsdr2Nr//Zltc/vwgo=
 github.com/libp2p/go-libp2p-asn-util v0.1.0/go.mod h1:wu+AnM9Ii2KgO5jMmS1rz9dvzTdj8BXqsPR9HR0XB7I=
 github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw=
@@ -1042,8 +1042,9 @@ github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdm
 github.com/libp2p/go-netroute v0.1.3/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk=
 github.com/libp2p/go-netroute v0.1.5/go.mod h1:V1SR3AaECRkEQCoFFzYwVYWvYIEtlxx89+O3qcpCl4A=
 github.com/libp2p/go-netroute v0.1.6/go.mod h1:AqhkMh0VuWmfgtxKPp3Oc1LdU5QSWS7wl0QLhSZqXxQ=
-github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4nWRE=
 github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI=
+github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
+github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
 github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIYDXnvOXkc0=
 github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
 github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
@@ -1126,8 +1127,9 @@ github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0Q
 github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
 github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
+github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE=
+github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g=
 github.com/marten-seemann/qtls v0.10.0/go.mod h1:UvMd1oaYDACI99/oZUYLzMCkBXQVT0aGm99sJhbT8hs=
 github.com/marten-seemann/qtls-go1-15 v0.1.1/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
 github.com/marten-seemann/qtls-go1-15 v0.1.4/go.mod h1:GyFwywLKkRt+6mfU99csTEY1joMZz5vmB1WNZH3P81I=
@@ -1136,10 +1138,10 @@ github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZE
 github.com/marten-seemann/qtls-go1-17 v0.1.0-rc.1/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
 github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
 github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI=
-github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM=
-github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
-github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU=
-github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
+github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI=
+github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4=
+github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE=
+github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI=
 github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk=
 github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU=
 github.com/marten-seemann/webtransport-go v0.1.1 h1:TnyKp3pEXcDooTaNn4s9dYpMJ7kMnTp7k5h+SgYP/mc=
@@ -1163,8 +1165,9 @@ github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnU
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
 github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
@@ -1256,8 +1259,8 @@ github.com/multiformats/go-multicodec v0.2.0/go.mod h1:/y4YVwkfMyry5kFbMTbLJKErh
 github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaIPw8yFgAZgFG0V7p1qQ=
 github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ=
 github.com/multiformats/go-multicodec v0.3.1-0.20211210143421-a526f306ed2c/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ=
-github.com/multiformats/go-multicodec v0.6.0 h1:KhH2kSuCARyuJraYMFxrNO3DqIaYhOdS039kbhgVwpE=
-github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw=
+github.com/multiformats/go-multicodec v0.7.0 h1:rTUjGOwjlhGHbEMbPoSUJowG1spZTVsITRANCjKTUAQ=
+github.com/multiformats/go-multicodec v0.7.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw=
 github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
 github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po=
 github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
@@ -1311,6 +1314,8 @@ github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvw
 github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
+github.com/onsi/ginkgo/v2 v2.5.0 h1:TRtrvv2vdQqzkwrQ1ke6vtXf7IK34RBUJafIy1wMwls=
+github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
 github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
@@ -1318,8 +1323,9 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
 github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
-github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
 github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
+github.com/onsi/gomega v1.24.0 h1:+0glovB9Jd6z3VR+ScSwQqXVTIfJcGA9UBM8yzQxhqg=
+github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
 github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
 github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0=
 github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
@@ -1372,15 +1378,16 @@ github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66Id
 github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
 github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
-github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
+github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
+github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
 github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
+github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
 github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@@ -1508,12 +1515,6 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP
 github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
 github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e h1:T5PdfK/M1xyrHwynxMIVMWLS7f/qHwfslZphxtGnw7s=
 github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g=
-github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w=
-github.com/tidwall/gjson v1.14.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
-github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
-github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 github.com/tj/assert v0.0.3 h1:Df/BlaZ20mq6kuai7f5z2TvPFiwC3xaWJSDQNiIS3Rk=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ=
@@ -1528,8 +1529,6 @@ github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2
 github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
 github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
 github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
-github.com/wI2L/jsondiff v0.2.0 h1:dE00WemBa1uCjrzQUUTE/17I6m5qAaN0EMFOg2Ynr/k=
-github.com/wI2L/jsondiff v0.2.0/go.mod h1:axTcwtBkY4TsKuV+RgoMhHyHKKFRI6nnjRLi8LLYQnA=
 github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE=
 github.com/warpfork/go-testmark v0.3.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0=
 github.com/warpfork/go-testmark v0.9.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0=
@@ -1696,8 +1695,8 @@ golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5
 golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210813211128-0a44fdfbc16e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM=
-golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
+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=
@@ -1710,8 +1709,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
 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/exp v0.0.0-20210615023648-acb5c1269671/go.mod h1:DVyR6MI7P4kEQgvZJSj1fQGrWIi2RzIrfYWycwheUAc=
-golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4=
-golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
+golang.org/x/exp v0.0.0-20221106115401-f9659909a136 h1:Fq7F/w7MAa1KJ5bt2aJ62ihqp9HDcRuyILskkpIAurw=
+golang.org/x/exp v0.0.0-20221106115401-f9659909a136/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
 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-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1736,8 +1735,8 @@ golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hM
 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.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
-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-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 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=
@@ -1798,8 +1797,8 @@ golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qx
 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-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5 h1:KafLifaRFIuSJ5C+7CyFJOF9haxKNC1CEIDk8GX6X0k=
-golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5/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-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -1823,8 +1822,8 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
 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-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
-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-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1911,16 +1910,17 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/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-20220114195835-da31bd327af9/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41 h1:ohgcoMbSofXygzo6AD2I1kz3BFmW1QArPYTtwEM3UXc=
-golang.org/x/sys v0.0.0-20220915200043-7b5979e65e41/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
+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=
@@ -1928,8 +1928,9 @@ 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.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 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 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=
@@ -1993,8 +1994,8 @@ 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.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/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/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
 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=
diff --git a/repo/fsrepo/fsrepo.go b/repo/fsrepo/fsrepo.go
index f3e9841e8b0b169542d99c62a051aae791748ac2..219f136f71e65028985df198fb1d6e9583b4e785 100644
--- a/repo/fsrepo/fsrepo.go
+++ b/repo/fsrepo/fsrepo.go
@@ -35,7 +35,7 @@ const LockFile = "repo.lock"
 
 var log = logging.Logger("fsrepo")
 
-// version number that we are currently expecting to see
+// RepoVersion is the version number that we are currently expecting to see
 var RepoVersion = 12
 
 var migrationInstructions = `See https://github.com/ipfs/fs-repo-migrations/blob/master/run.md
@@ -383,7 +383,7 @@ func (r *FSRepo) SetAPIAddr(addr ma.Multiaddr) error {
 	}
 	// Remove the temp file when rename return error
 	if err1 := os.Remove(filepath.Join(r.path, "."+apiFile+".tmp")); err1 != nil {
-		return fmt.Errorf("File Rename error: %s, File remove error: %s", err.Error(),
+		return fmt.Errorf("file Rename error: %s, file remove error: %s", err.Error(),
 			err1.Error())
 	}
 	return err
@@ -422,7 +422,7 @@ func (r *FSRepo) SetGatewayAddr(addr net.Addr) error {
 	}
 	// Remove the temp file when rename return error
 	if err1 := os.Remove(tmpPath); err1 != nil {
-		return fmt.Errorf("File Rename error: %w, File remove error: %s", err, err1.Error())
+		return fmt.Errorf("file Rename error: %w, file remove error: %s", err, err1.Error())
 	}
 	return err
 }
diff --git a/repo/fsrepo/migrations/httpfetcher.go b/repo/fsrepo/migrations/httpfetcher.go
index 9be7f9f0a104bf29b9e97ce1986638607c690749..81e0e406d22a1ea139c52d6806c15db6c80ed88d 100644
--- a/repo/fsrepo/migrations/httpfetcher.go
+++ b/repo/fsrepo/migrations/httpfetcher.go
@@ -16,7 +16,7 @@ const (
 )
 
 // HttpFetcher fetches files over HTTP
-type HttpFetcher struct {
+type HttpFetcher struct { //nolint
 	distPath  string
 	gateway   string
 	limit     int64
@@ -30,7 +30,7 @@ var _ Fetcher = (*HttpFetcher)(nil)
 // Specifying "" for distPath sets the default IPNS path.
 // Specifying "" for gateway sets the default.
 // Specifying 0 for fetchLimit sets the default, -1 means no limit.
-func NewHttpFetcher(distPath, gateway, userAgent string, fetchLimit int64) *HttpFetcher {
+func NewHttpFetcher(distPath, gateway, userAgent string, fetchLimit int64) *HttpFetcher { //nolint
 	f := &HttpFetcher{
 		distPath: LatestIpfsDist,
 		gateway:  defaultGatewayURL,
diff --git a/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go b/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go
index cc10de4eba58c243db5ab16bd76b47922a67896f..ce7b490efd20ebee1a04b30ecec4b1aca8d7ebcd 100644
--- a/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go
+++ b/repo/fsrepo/migrations/ipfsfetcher/ipfsfetcher.go
@@ -28,7 +28,7 @@ const (
 	// Default maximum download size
 	defaultFetchLimit = 1024 * 1024 * 512
 
-	tempNodeTcpAddr = "/ip4/127.0.0.1/tcp/0"
+	tempNodeTCPAddr = "/ip4/127.0.0.1/tcp/0"
 )
 
 type IpfsFetcher struct {
@@ -193,7 +193,7 @@ func initTempNode(ctx context.Context, bootstrap []string, peers []peer.AddrInfo
 	// Disable listening for inbound connections
 	cfg.Addresses.Gateway = []string{}
 	cfg.Addresses.API = []string{}
-	cfg.Addresses.Swarm = []string{tempNodeTcpAddr}
+	cfg.Addresses.Swarm = []string{tempNodeTCPAddr}
 
 	if len(bootstrap) != 0 {
 		cfg.Bootstrap = bootstrap
diff --git a/repo/fsrepo/migrations/migrations.go b/repo/fsrepo/migrations/migrations.go
index 9c556fe9722c00f99faeca1bdfeeb262f6bf6b46..7e764b7312df032cd0dcc117021f85d96c9dfc32 100644
--- a/repo/fsrepo/migrations/migrations.go
+++ b/repo/fsrepo/migrations/migrations.go
@@ -166,6 +166,8 @@ func GetMigrationFetcher(downloadSources []string, distPath string, newIpfsFetch
 			if newIpfsFetcher != nil {
 				fetchers = append(fetchers, newIpfsFetcher(distPath))
 			}
+		case "":
+			// Ignore empty string
 		default:
 			u, err := url.Parse(src)
 			if err != nil {
@@ -179,8 +181,6 @@ func GetMigrationFetcher(downloadSources []string, distPath string, newIpfsFetch
 				return nil, errors.New("bad gateway address: url scheme must be http or https")
 			}
 			fetchers = append(fetchers, &RetryFetcher{NewHttpFetcher(distPath, u.String(), httpUserAgent, 0), numTriesPerHTTP})
-		case "":
-			// Ignore empty string
 		}
 	}
 
diff --git a/repo/repo.go b/repo/repo.go
index 3c61031774b355379d8f927b4ddf59cff93bdbcf..bec02049d1819b29a2f011e9aec43125c73a57fe 100644
--- a/repo/repo.go
+++ b/repo/repo.go
@@ -15,7 +15,7 @@ import (
 )
 
 var (
-	ErrApiNotRunning = errors.New("api not running")
+	ErrApiNotRunning = errors.New("api not running") //nolint
 )
 
 // Repo represents all persistent data of a given ipfs node.
diff --git a/routing/delegated.go b/routing/delegated.go
index d8e818bc3c2cced9e3e2f6cc1df8439952ccc17b..61420efa7be335cf613b82b9e859b9854897b1d0 100644
--- a/routing/delegated.go
+++ b/routing/delegated.go
@@ -23,6 +23,7 @@ import (
 	"github.com/libp2p/go-libp2p/core/routing"
 	ma "github.com/multiformats/go-multiaddr"
 	"github.com/multiformats/go-multicodec"
+	"go.opencensus.io/stats/view"
 )
 
 var log = logging.Logger("routing/delegated")
@@ -178,6 +179,11 @@ func reframeRoutingFromConfig(conf config.Router, extraReframe *ExtraReframePara
 
 	var c *drc.Client
 
+	err = view.Register(drc.DefaultViews...)
+	if err != nil {
+		return nil, fmt.Errorf("registering delegated routing views: %w", err)
+	}
+
 	// this path is for tests only
 	if extraReframe == nil {
 		c, err = drc.NewClient(dr, nil, nil)
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
deleted file mode 100644
index 46107e0453107f139bb880b8ff618a823db2448f..0000000000000000000000000000000000000000
--- a/snap/snapcraft.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-# This snap will build kubo from source.
-name: ipfs
-summary: global, versioned, peer-to-peer filesystem # 79 char long summary
-description: |
-  IPFS combines good ideas from Git, BitTorrent, Kademlia, SFS, and the Web.
-  It is like a single bittorrent swarm, exchanging git objects. IPFS provides
-  an interface as simple as the HTTP web, but with permanence built in. You
-  can also mount the world at /ipfs.
-
-# fetch the version number in the `ipfs` part rather than hardcode it here
-# see: https://snapcraft.io/docs/using-external-metadata#heading--scriptlet
-adopt-info: ipfs
-base: core18
-grade: stable
-confinement: strict
-
-apps:
-  ipfs:
-    command: ipfs
-    # the home plug is included so the user can `ipfs add` files from their home dir without a permission error.
-    plugs: [home, network, network-bind, removable-media]
-    environment:
-      # Snaps versions are isolated by default. This keeps the same ipfs repo across upgrades.
-      IPFS_PATH: "$SNAP_USER_COMMON"
-
-parts:
-  ipfs:
-    source: '.'
-    source-tag: master
-    plugin: go
-    # keep me up to date with the go version that kubo expects to be built with.
-    go-channel: 1.19/stable
-    go-importpath: github.com/ipfs/kubo
-    build-packages:
-        - build-essential
-
-    # use `make` to build and set the snap version from `ipfs version` output
-    override-build: |
-      export GOPATH=$SNAPCRAFT_PART_BUILD/go
-      make install
-      cp $SNAPCRAFT_PART_BUILD/go/bin/ipfs $SNAPCRAFT_PART_INSTALL
-      export IPFS_VERSION=$($SNAPCRAFT_PART_BUILD/go/bin/ipfs version --commit | cut -c 14-)
-      echo "found version $IPFS_VERSION"
-      snapcraftctl set-version $IPFS_VERSION
diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod
index 978ab919e006421bcdf51deed91056b68862ec1a..51e8021506f37b79e06f3d038c57f25ee1f967bd 100644
--- a/test/dependencies/go.mod
+++ b/test/dependencies/go.mod
@@ -6,7 +6,7 @@ require (
 	github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd
 	github.com/golangci/golangci-lint v1.49.0
 	github.com/ipfs/go-blockservice v0.3.0
-	github.com/ipfs/go-cid v0.3.0
+	github.com/ipfs/go-cid v0.3.2
 	github.com/ipfs/go-cidutil v0.1.0
 	github.com/ipfs/go-datastore v0.5.1
 	github.com/ipfs/go-graphsync v0.13.2
@@ -18,7 +18,7 @@ require (
 	github.com/ipfs/hang-fds v0.1.0
 	github.com/ipfs/iptb v1.4.0
 	github.com/ipfs/iptb-plugins v0.3.0
-	github.com/ipld/go-ipld-prime v0.17.0
+	github.com/ipld/go-ipld-prime v0.19.0
 	github.com/jbenet/go-random v0.0.0-20190219211222-123a90aedc0c
 	github.com/jbenet/go-random-files v0.0.0-20190219210431-31b3f20ebded
 	github.com/libp2p/go-libp2p v0.22.0
@@ -100,7 +100,7 @@ require (
 	github.com/golangci/misspell v0.3.5 // indirect
 	github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect
 	github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect
-	github.com/google/go-cmp v0.5.8 // indirect
+	github.com/google/go-cmp v0.5.9 // indirect
 	github.com/google/gopacket v1.1.19 // indirect
 	github.com/google/uuid v1.3.0 // indirect
 	github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 // indirect
@@ -198,7 +198,7 @@ require (
 	github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect
 	github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
 	github.com/multiformats/go-multibase v0.1.1 // indirect
-	github.com/multiformats/go-multicodec v0.5.0 // indirect
+	github.com/multiformats/go-multicodec v0.6.0 // indirect
 	github.com/multiformats/go-multistream v0.3.3 // indirect
 	github.com/multiformats/go-varint v0.0.6 // indirect
 	github.com/nakabonne/nestif v0.3.1 // indirect
diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum
index 33e07553b67c41d99c0accfd5f29ab1517e416a8..3c148639d0c032719ed22c8d111cde420e9f5d36 100644
--- a/test/dependencies/go.sum
+++ b/test/dependencies/go.sum
@@ -411,8 +411,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 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 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
 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/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
 github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@@ -559,8 +560,8 @@ github.com/ipfs/go-cid v0.0.6/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqg
 github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I=
 github.com/ipfs/go-cid v0.1.0/go.mod h1:rH5/Xv83Rfy8Rw6xG+id3DYAMUVmem1MowoKwdXmN2o=
 github.com/ipfs/go-cid v0.2.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro=
-github.com/ipfs/go-cid v0.3.0 h1:gT6Cbs6YePaBNc7l6v5EXt0xTMup1jGV5EU1N+QLVpY=
-github.com/ipfs/go-cid v0.3.0/go.mod h1:P+HXFDF4CVhaVayiEb4wkAy7zBHxBwsJyt0Y5U6MLro=
+github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc=
+github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw=
 github.com/ipfs/go-cidutil v0.1.0 h1:RW5hO7Vcf16dplUU60Hs0AKDkQAVPVplr7lk97CFL+Q=
 github.com/ipfs/go-cidutil v0.1.0/go.mod h1:e7OEVBMIv9JaOxt9zaGEmAoSlXW9jdFZ5lP/0PwcfpA=
 github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE=
@@ -700,8 +701,9 @@ github.com/ipld/go-ipld-prime v0.9.1-0.20210324083106-dc342a9917db/go.mod h1:KvB
 github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHtEaLglS3ZeV8=
 github.com/ipld/go-ipld-prime v0.14.0/go.mod h1:9ASQLwUFLptCov6lIYc70GRB4V7UTyLD0IJtrDJe6ZM=
 github.com/ipld/go-ipld-prime v0.16.0/go.mod h1:axSCuOCBPqrH+gvXr2w9uAOulJqBPhHPT2PjoiiU1qA=
-github.com/ipld/go-ipld-prime v0.17.0 h1:+U2peiA3aQsE7mrXjD2nYZaZrCcakoz2Wge8K42Ld8g=
 github.com/ipld/go-ipld-prime v0.17.0/go.mod h1:aYcKm5TIvGfY8P3QBKz/2gKcLxzJ1zDaD+o0bOowhgs=
+github.com/ipld/go-ipld-prime v0.19.0 h1:5axC7rJmPc17Emw6TelxGwnzALk0PdupZ2oj2roDj04=
+github.com/ipld/go-ipld-prime v0.19.0/go.mod h1:Q9j3BaVXwaA3o5JUDNvptDDr/x8+F7FG6XJ8WI3ILg4=
 github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY=
 github.com/jackpal/gateway v1.0.5/go.mod h1:lTpwd4ACLXmpyiCTRtfiNyVnUmqT9RivzCDQetPfnjA=
 github.com/jackpal/go-nat-pmp v1.0.1/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
@@ -1281,8 +1283,9 @@ github.com/multiformats/go-multicodec v0.3.0/go.mod h1:qGGaQmioCDh+TeFOnxrbU0DaI
 github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ=
 github.com/multiformats/go-multicodec v0.3.1-0.20211210143421-a526f306ed2c/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ=
 github.com/multiformats/go-multicodec v0.4.1/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ=
-github.com/multiformats/go-multicodec v0.5.0 h1:EgU6cBe/D7WRwQb1KmnBvU7lrcFGMggZVTPtOW9dDHs=
 github.com/multiformats/go-multicodec v0.5.0/go.mod h1:DiY2HFaEp5EhEXb/iYzVAunmyX/aSFMxq2KMKfWEues=
+github.com/multiformats/go-multicodec v0.6.0 h1:KhH2kSuCARyuJraYMFxrNO3DqIaYhOdS039kbhgVwpE=
+github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw=
 github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U=
 github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po=
 github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
diff --git a/test/integration/addcat_test.go b/test/integration/addcat_test.go
index 3564a20dd00e8f852b8488bfb19719fedc61987e..45e8729aca632979222b6ae7bcf953975260da91 100644
--- a/test/integration/addcat_test.go
+++ b/test/integration/addcat_test.go
@@ -122,12 +122,12 @@ func DirectAddCat(data []byte, conf testutil.LatencyConfig) error {
 	}
 	defer catter.Close()
 
-	adderApi, err := coreapi.NewCoreAPI(adder)
+	adderAPI, err := coreapi.NewCoreAPI(adder)
 	if err != nil {
 		return err
 	}
 
-	catterApi, err := coreapi.NewCoreAPI(catter)
+	catterAPI, err := coreapi.NewCoreAPI(catter)
 	if err != nil {
 		return err
 	}
@@ -147,12 +147,12 @@ func DirectAddCat(data []byte, conf testutil.LatencyConfig) error {
 		return err
 	}
 
-	added, err := adderApi.Unixfs().Add(ctx, files.NewBytesFile(data))
+	added, err := adderAPI.Unixfs().Add(ctx, files.NewBytesFile(data))
 	if err != nil {
 		return err
 	}
 
-	readerCatted, err := catterApi.Unixfs().Get(ctx, added)
+	readerCatted, err := catterAPI.Unixfs().Get(ctx, added)
 	if err != nil {
 		return err
 	}
diff --git a/test/integration/bench_cat_test.go b/test/integration/bench_cat_test.go
index 952f8e48bb18ef72b4053da7251a4629e3cb40a7..d7e37dbb9ae891000d0e5b00276ba05c10484f09 100644
--- a/test/integration/bench_cat_test.go
+++ b/test/integration/bench_cat_test.go
@@ -65,12 +65,12 @@ func benchCat(b *testing.B, data []byte, conf testutil.LatencyConfig) error {
 	}
 	defer catter.Close()
 
-	adderApi, err := coreapi.NewCoreAPI(adder)
+	adderAPI, err := coreapi.NewCoreAPI(adder)
 	if err != nil {
 		return err
 	}
 
-	catterApi, err := coreapi.NewCoreAPI(catter)
+	catterAPI, err := coreapi.NewCoreAPI(catter)
 	if err != nil {
 		return err
 	}
@@ -90,13 +90,13 @@ func benchCat(b *testing.B, data []byte, conf testutil.LatencyConfig) error {
 		return err
 	}
 
-	added, err := adderApi.Unixfs().Add(ctx, files.NewBytesFile(data))
+	added, err := adderAPI.Unixfs().Add(ctx, files.NewBytesFile(data))
 	if err != nil {
 		return err
 	}
 
 	b.StartTimer()
-	readerCatted, err := catterApi.Unixfs().Get(ctx, added)
+	readerCatted, err := catterAPI.Unixfs().Get(ctx, added)
 	if err != nil {
 		return err
 	}
diff --git a/test/integration/three_legged_cat_test.go b/test/integration/three_legged_cat_test.go
index f41c9a7ddea4e8d0cfeabb9906910f33c346b10b..f0358272b092c5488967a73cb38971a643134426 100644
--- a/test/integration/three_legged_cat_test.go
+++ b/test/integration/three_legged_cat_test.go
@@ -93,12 +93,12 @@ func RunThreeLeggedCat(data []byte, conf testutil.LatencyConfig) error {
 	}
 	defer catter.Close()
 
-	adderApi, err := coreapi.NewCoreAPI(adder)
+	adderAPI, err := coreapi.NewCoreAPI(adder)
 	if err != nil {
 		return err
 	}
 
-	catterApi, err := coreapi.NewCoreAPI(catter)
+	catterAPI, err := coreapi.NewCoreAPI(catter)
 	if err != nil {
 		return err
 	}
@@ -117,12 +117,12 @@ func RunThreeLeggedCat(data []byte, conf testutil.LatencyConfig) error {
 		return err
 	}
 
-	added, err := adderApi.Unixfs().Add(ctx, files.NewBytesFile(data))
+	added, err := adderAPI.Unixfs().Add(ctx, files.NewBytesFile(data))
 	if err != nil {
 		return err
 	}
 
-	readerCatted, err := catterApi.Unixfs().Get(ctx, added)
+	readerCatted, err := catterAPI.Unixfs().Get(ctx, added)
 	if err != nil {
 		return err
 	}
diff --git a/test/sharness/t0012-completion-fish.sh b/test/sharness/t0012-completion-fish.sh
new file mode 100755
index 0000000000000000000000000000000000000000..9c794d993db0a106357fcf708740a60640c5f919
--- /dev/null
+++ b/test/sharness/t0012-completion-fish.sh
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+test_description="Test generated fish completions"
+
+. lib/test-lib.sh
+
+test_expect_success "'ipfs commands completion fish' succeeds" '
+  ipfs commands completion fish > completions.fish
+'
+
+test_expect_success "generated completions completes 'ipfs version'" '
+  fish -c "source completions.fish && complete -C \"ipfs ver\" | grep -q \"version.Show IPFS version information.\" "
+'
+
+test_done
+
diff --git a/test/sharness/t0054-dag-car-import-export.sh b/test/sharness/t0054-dag-car-import-export.sh
index 6f32cf277751087df21710544d93c25dc2532789..f378e9128e5667c0ec87fb9d6d7903b464e8025f 100755
--- a/test/sharness/t0054-dag-car-import-export.sh
+++ b/test/sharness/t0054-dag-car-import-export.sh
@@ -263,4 +263,32 @@ test_expect_success "version 2 import output as expected" '
   test_cmp_sorted version_2_import_expected version_2_import_actual
 '
 
+test_expect_success "'ipfs dag import' decode IPLD 'dag-json' codec works" '
+  NEW_HASH=$(echo "{ \"test\": \"dag-json\" }" | ipfs dag put --store-codec dag-json) &&
+  ipfs dag export $NEW_HASH > dag-json.car &&
+  ipfs dag import dag-json.car &&
+  rm dag-json.car
+'
+
+test_expect_success "'ipfs dag import' decode IPLD 'dag-cbor' codec works" '
+  NEW_HASH=$(echo "{ \"test\": \"dag-cbor\" }" | ipfs dag put --store-codec dag-cbor) &&
+  ipfs dag export $NEW_HASH > dag-cbor.car &&
+  ipfs dag import dag-cbor.car &&
+  rm dag-cbor.car
+'
+
+test_expect_success "'ipfs dag import' decode IPLD 'json' codec works" '
+  NEW_HASH=$(echo "{ \"test\": \"json\" }" | ipfs dag put --store-codec json) &&
+  ipfs dag export $NEW_HASH > json.car &&
+  ipfs dag import json.car &&
+  rm json.car
+'
+
+test_expect_success "'ipfs dag import' decode IPLD 'cbor' codec works" '
+  NEW_HASH=$(echo "{ \"test\": \"cbor\" }" | ipfs dag put --store-codec cbor) &&
+  ipfs dag export $NEW_HASH > cbor.car &&
+  ipfs dag import cbor.car &&
+  rm cbor.car
+'
+
 test_done
diff --git a/test/sharness/t0114-gateway-subdomains.sh b/test/sharness/t0114-gateway-subdomains.sh
index 0dad1e95c1a35d96516ac269dc7688a316ba24e6..a7e5a59c938268605587094af2e15c8a848a4109 100755
--- a/test/sharness/t0114-gateway-subdomains.sh
+++ b/test/sharness/t0114-gateway-subdomains.sh
@@ -323,6 +323,38 @@ test_localhost_gateway_response_should_contain \
   "http://api.localhost:$GWAY_PORT/api/v0/refs?arg=$DIR_CID&r=true" \
   "Ref"
 
+## ============================================================================
+## Test DNSLink inlining on HTTP gateways
+## ============================================================================
+
+# set explicit subdomain gateway config for the hostname
+ipfs config --json Gateway.PublicGateways '{
+  "localhost": {
+    "UseSubdomains": true,
+    "InlineDNSLink": true,
+    "Paths": ["/ipfs", "/ipns", "/api"]
+  },
+  "example.com": {
+    "UseSubdomains": true,
+    "InlineDNSLink": true,
+    "Paths": ["/ipfs", "/ipns", "/api"]
+  }
+}' || exit 1
+# restart daemon to apply config changes
+test_kill_ipfs_daemon
+test_launch_ipfs_daemon_without_network
+
+test_localhost_gateway_response_should_contain \
+  "request for localhost/ipns/{fqdn} redirects to DNSLink in subdomain with DNS inlining" \
+  "http://localhost:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \
+  "Location: http://en-wikipedia--on--ipfs-org.ipns.localhost:$GWAY_PORT/wiki"
+
+test_hostname_gateway_response_should_contain \
+  "request for example.com/ipns/{fqdn} redirects to DNSLink in subdomain with DNS inlining" \
+  "example.com" \
+  "http://127.0.0.1:$GWAY_PORT/ipns/en.wikipedia-on-ipfs.org/wiki" \
+  "Location: http://en-wikipedia--on--ipfs-org.ipns.example.com/wiki"
+
 ## ============================================================================
 ## Test subdomain-based requests with a custom hostname config
 ## (origin per content root at http://*.example.com)
diff --git a/test/sharness/t0119-prometheus.sh b/test/sharness/t0119-prometheus.sh
index cd7346735711b410d0dd4663f8d503511bcc9c6a..e96b8b96f8a0a3cdfdceef3f73a36a4be0d6601a 100755
--- a/test/sharness/t0119-prometheus.sh
+++ b/test/sharness/t0119-prometheus.sh
@@ -10,6 +10,10 @@ test_description="Test prometheus metrics are exposed correctly"
 
 test_init_ipfs
 
+test_expect_success "enable ResourceMgr in the config" '
+  ipfs config --json Swarm.ResourceMgr.Enabled false
+'
+
 test_launch_ipfs_daemon
 
 test_expect_success "collect metrics" '
diff --git a/test/sharness/t0122-gateway-tar-data/inside-root.car b/test/sharness/t0122-gateway-tar-data/inside-root.car
new file mode 100644
index 0000000000000000000000000000000000000000..c37b594f8d6d132f3d3ef955b7b2270302f3b574
Binary files /dev/null and b/test/sharness/t0122-gateway-tar-data/inside-root.car differ
diff --git a/test/sharness/t0122-gateway-tar-data/outside-root.car b/test/sharness/t0122-gateway-tar-data/outside-root.car
new file mode 100644
index 0000000000000000000000000000000000000000..7587dcb7efae5c7a5fac5d94539c131eacc83537
Binary files /dev/null and b/test/sharness/t0122-gateway-tar-data/outside-root.car differ
diff --git a/test/sharness/t0122-gateway-tar.sh b/test/sharness/t0122-gateway-tar.sh
new file mode 100755
index 0000000000000000000000000000000000000000..34dc1ba12c85a1f109197b8837d0fd7ed3ffb67f
--- /dev/null
+++ b/test/sharness/t0122-gateway-tar.sh
@@ -0,0 +1,91 @@
+#!/usr/bin/env bash
+
+test_description="Test HTTP Gateway TAR (application/x-tar) Support"
+
+. lib/test-lib.sh
+
+test_init_ipfs
+test_launch_ipfs_daemon_without_network
+
+OUTSIDE_ROOT_CID="bafybeicaj7kvxpcv4neaqzwhrqqmdstu4dhrwfpknrgebq6nzcecfucvyu"
+INSIDE_ROOT_CID="bafybeibfevfxlvxp5vxobr5oapczpf7resxnleb7tkqmdorc4gl5cdva3y"
+
+test_expect_success "Add the test directory" '
+  mkdir -p rootDir/ipfs &&
+  mkdir -p rootDir/ipns &&
+  mkdir -p rootDir/api &&
+  mkdir -p rootDir/ą/ę &&
+  echo "I am a txt file on path with utf8" > rootDir/ą/ę/file-źł.txt &&
+  echo "I am a txt file in confusing /api dir" > rootDir/api/file.txt &&
+  echo "I am a txt file in confusing /ipfs dir" > rootDir/ipfs/file.txt &&
+  echo "I am a txt file in confusing /ipns dir" > rootDir/ipns/file.txt &&
+  DIR_CID=$(ipfs add -Qr --cid-version 1 rootDir) &&
+  FILE_CID=$(ipfs files stat --enc=json /ipfs/$DIR_CID/ą/ę/file-źł.txt | jq -r .Hash) &&
+  FILE_SIZE=$(ipfs files stat --enc=json /ipfs/$DIR_CID/ą/ę/file-źł.txt | jq -r .Size)
+  echo "$FILE_CID / $FILE_SIZE"
+'
+
+test_expect_success "GET TAR with format=tar and extract" '
+  curl "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID?format=tar" | tar -x
+'
+
+test_expect_success "GET TAR with 'Accept: application/x-tar' and extract" '
+  curl -H "Accept: application/x-tar" "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID" | tar -x
+'
+
+test_expect_success "GET TAR with format=tar has expected Content-Type" '
+  curl -sD - "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID?format=tar" > curl_output_filename 2>&1 &&
+  test_should_contain "Content-Disposition: attachment;" curl_output_filename &&
+  test_should_contain "Etag: W/\"$FILE_CID.x-tar" curl_output_filename &&
+  test_should_contain "Content-Type: application/x-tar" curl_output_filename
+'
+
+test_expect_success "GET TAR with 'Accept: application/x-tar' has expected Content-Type" '
+  curl -sD - -H "Accept: application/x-tar" "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID" > curl_output_filename 2>&1 &&
+  test_should_contain "Content-Disposition: attachment;" curl_output_filename &&
+  test_should_contain "Etag: W/\"$FILE_CID.x-tar" curl_output_filename &&
+  test_should_contain "Content-Type: application/x-tar" curl_output_filename
+'
+
+test_expect_success "GET TAR has expected root file" '
+  rm -rf outputDir && mkdir outputDir &&
+  curl "http://127.0.0.1:$GWAY_PORT/ipfs/$FILE_CID?format=tar" | tar -x -C outputDir &&
+  test -f "outputDir/$FILE_CID" &&
+  echo "I am a txt file on path with utf8" > expected &&
+  test_cmp expected outputDir/$FILE_CID
+'
+
+test_expect_success "GET TAR has expected root directory" '
+  rm -rf outputDir && mkdir outputDir &&
+  curl "http://127.0.0.1:$GWAY_PORT/ipfs/$DIR_CID?format=tar" | tar -x -C outputDir &&
+  test -d "outputDir/$DIR_CID" &&
+  echo "I am a txt file on path with utf8" > expected &&
+  test_cmp expected outputDir/$DIR_CID/ą/ę/file-źł.txt
+'
+
+test_expect_success "GET TAR with explicit ?filename= succeeds with modified Content-Disposition header" "
+  curl -fo actual -D actual_headers 'http://127.0.0.1:$GWAY_PORT/ipfs/$DIR_CID?filename=testтест.tar&format=tar' &&
+  grep -F 'Content-Disposition: attachment; filename=\"test____.tar\"; filename*=UTF-8'\'\''test%D1%82%D0%B5%D1%81%D1%82.tar' actual_headers
+"
+
+test_expect_success "Add CARs with relative paths to test with" '
+  ipfs dag import ../t0122-gateway-tar-data/outside-root.car > import_output &&
+  test_should_contain $OUTSIDE_ROOT_CID import_output &&
+  ipfs dag import ../t0122-gateway-tar-data/inside-root.car > import_output &&
+  test_should_contain $INSIDE_ROOT_CID import_output
+'
+
+test_expect_success "GET TAR with relative paths outside root fails" '
+  curl -o - "http://127.0.0.1:$GWAY_PORT/ipfs/$OUTSIDE_ROOT_CID?format=tar" > curl_output_filename &&
+  test_should_contain "relative UnixFS paths outside the root are now allowed" curl_output_filename
+'
+
+test_expect_success "GET TAR with relative paths inside root works" '
+  rm -rf outputDir && mkdir outputDir &&
+  curl "http://127.0.0.1:$GWAY_PORT/ipfs/$INSIDE_ROOT_CID?format=tar" | tar -x -C outputDir &&
+  test -f outputDir/$INSIDE_ROOT_CID/foobar/file
+'
+
+test_kill_ipfs_daemon
+
+test_done
diff --git a/test/sharness/t0139-swarm-rcmgr.sh b/test/sharness/t0139-swarm-rcmgr.sh
index 7d7ecef8c6a725541b9af94eddde07f9e1b9fe23..ca63639577e2c3b79084bb117fbfa9160a87fd17 100755
--- a/test/sharness/t0139-swarm-rcmgr.sh
+++ b/test/sharness/t0139-swarm-rcmgr.sh
@@ -2,13 +2,15 @@
 #
 test_description="Test ipfs swarm ResourceMgr config and commands"
 
-export IPFS_CHECK_RCMGR_DEFAULTS=1
-
 . lib/test-lib.sh
 
 test_init_ipfs
 
-# test correct behavior when resource manager is disabled (default behavior)
+test_expect_success 'Disable resource manager' '
+  ipfs config --bool Swarm.ResourceMgr.Enabled false
+'
+
+# test correct behavior when resource manager is disabled
 test_launch_ipfs_daemon
 
 test_expect_success 'Swarm limit should fail since RM is disabled' '
@@ -54,16 +56,38 @@ test_expect_success 'ResourceMgr enabled: swarm limit' '
   jq -e .StreamsInbound < json &&
   jq -e .StreamsOutbound < json
 '
+test_expect_success 'ResourceMgr enabled: swarm limit reset' '
+  ipfs swarm limit system --reset --enc=json 2> reset &&
+  ipfs swarm limit system --enc=json 2> actual &&
+  test_cmp reset actual
+'
+
+test_expect_success 'ResourceMgr enabled: swarm limit reset on map values' '
+  ipfs swarm limit peer:12D3KooWL7i1T9VSPeF8AgQApbyM51GNKZsYPvNvL347aMDmvNzG --reset --enc=json 2> reset &&
+  ipfs swarm limit peer:12D3KooWL7i1T9VSPeF8AgQApbyM51GNKZsYPvNvL347aMDmvNzG --enc=json 2> actual &&
+  test_cmp reset actual
+'
+
+test_expect_success 'ResourceMgr enabled: scope is required using reset flag' '
+  test_expect_code 1 ipfs swarm limit --reset 2> actual &&
+  test_should_contain "Error: argument \"scope\" is required" actual
+'
+
+test_expect_success 'connected: swarm stats all working properly' '
+  test_expect_code 0 ipfs swarm stats all
+'
 
 # every scope has the same fields, so we only inspect System
 test_expect_success 'ResourceMgr enabled: swarm stats' '
   ipfs swarm stats all --enc=json | tee json &&
   jq -e .System.Memory < json &&
-  jq -e .System.NumConnsInbound < json &&
-  jq -e .System.NumConnsOutbound < json &&
-  jq -e .System.NumFD < json &&
-  jq -e .System.NumStreamsInbound < json &&
-  jq -e .System.NumStreamsOutbound < json &&
+  jq -e .System.FD < json &&
+  jq -e .System.Conns < json &&
+  jq -e .System.ConnsInbound < json &&
+  jq -e .System.ConnsOutbound < json &&
+  jq -e .System.Streams < json &&
+  jq -e .System.StreamsInbound < json &&
+  jq -e .System.StreamsOutbound < json &&
   jq -e .Transient.Memory < json
 '
 
diff --git a/test/sharness/t0290-cid.sh b/test/sharness/t0290-cid.sh
index 905410d62874ded58cc3db38b7cb87b5db106816..8fb36e30e5089758a916fa32bebff4e9ef35e6d5 100755
--- a/test/sharness/t0290-cid.sh
+++ b/test/sharness/t0290-cid.sh
@@ -142,9 +142,12 @@ cat <<EOF > codecs_expect
   241  dash-tx
   250  swarm-manifest
   251  swarm-feed
+  252  beeson
   297  dag-json
   496  swhid-1-snp
   512  json
+46083  urdca-2015-canon
+46593  json-jcs
 EOF
 
 cat <<EOF > supported_codecs_expect
diff --git a/test/sharness/t0320-pubsub.sh b/test/sharness/t0320-pubsub.sh
index 676d890c21845b77be092467bb81229a9fcc697b..5635b842ec2fef59616c6efab21475a479afea26 100755
--- a/test/sharness/t0320-pubsub.sh
+++ b/test/sharness/t0320-pubsub.sh
@@ -158,7 +158,7 @@ test_expect_success 'pubsub cmd fails because it was disabled via cli flag' '
 '
 
 test_expect_success "pubsub cmd produces error" '
-  echo "Error: experimental pubsub feature not enabled. Run daemon with --enable-pubsub-experiment to use." > expected &&
+  echo "Error: experimental pubsub feature not enabled, run daemon with --enable-pubsub-experiment to use" > expected &&
   test_cmp expected pubsub_cmd_out
 '
 
diff --git a/version.go b/version.go
index f465487663b5277299e41bb2f03193f8bcdb1218..adc9471043622f946e9b149eefb3aa33f1616aba 100644
--- a/version.go
+++ b/version.go
@@ -11,9 +11,9 @@ import (
 var CurrentCommit string
 
 // CurrentVersionNumber is the current application's version literal
-const CurrentVersionNumber = "0.16.0"
+const CurrentVersionNumber = "0.17.0"
 
-const ApiVersion = "/kubo/" + CurrentVersionNumber + "/"
+const ApiVersion = "/kubo/" + CurrentVersionNumber + "/" //nolint
 
 // GetUserAgentVersion is the libp2p user agent used by go-ipfs.
 //