repos / pico

pico services mono repo
git clone https://github.com/picosh/pico.git

commit
588159a
parent
7c27c34
author
Eric Bower
date
2026-02-26 21:36:17 -0500 EST
refactor(pgs): custom rfc9111 compliant cache
12 files changed,  +1913, -1321
M go.mod
M go.sum
M Makefile
+5, -1
 1@@ -85,12 +85,16 @@ build-pico:
 2 	go build -o "build/pico-ssh" "./cmd/pico/ssh"
 3 .PHONY: build-auth
 4 
 5+build-pgs-cdn:
 6+	go build -o "build/pgs-cdn" "./cmd/pgs/cdn"
 7+.PHONY: build-cdn
 8+
 9 build-%:
10 	go build -o "build/$*-web" "./cmd/$*/web"
11 	go build -o "build/$*-ssh" "./cmd/$*/ssh"
12 .PHONY: build-%
13 
14-build: build-prose build-pastes build-feeds build-pgs build-auth build-pico build-pipe
15+build: build-prose build-pastes build-feeds build-pgs build-pgs-cdn build-auth build-pico build-pipe
16 .PHONY: build
17 
18 scripts:
M cmd/pgs/cdn/main.go
+58, -49
  1@@ -3,22 +3,22 @@ package main
  2 import (
  3 	"context"
  4 	"fmt"
  5+	"io"
  6 	"log/slog"
  7 	"net"
  8 	"net/http"
  9-	"net/http/httputil"
 10 	"net/url"
 11 	"strings"
 12 
 13-	"github.com/darkweak/souin/pkg/middleware"
 14-	"github.com/hashicorp/golang-lru/v2/expirable"
 15 	"github.com/picosh/pico/pkg/apps/pgs"
 16+	"github.com/picosh/pico/pkg/httpcache"
 17 	"github.com/picosh/pico/pkg/shared"
 18 	"github.com/prometheus/client_golang/prometheus/promhttp"
 19 )
 20 
 21 func main() {
 22-	withPipe := strings.ToLower(shared.GetEnv("PICO_PIPE_ENABLED", "true")) == "true"
 23+	pipeEnabled := shared.GetEnv("PICO_PIPE_ENABLED", "true")
 24+	withPipe := strings.ToLower(pipeEnabled) == "true"
 25 	logger := shared.CreateLogger("pgs-cdn", withPipe)
 26 	ctx := context.Background()
 27 	drain := pgs.CreateSubCacheDrain(ctx, logger)
 28@@ -27,19 +27,14 @@ func main() {
 29 		_ = pubsub.Close()
 30 	}()
 31 	cfg := pgs.NewPgsConfig(logger, nil, nil, drain)
 32-	httpCache := pgs.SetupCache(cfg)
 33-	router := &pgs.WebRouter{
 34-		Cfg:            cfg,
 35-		RedirectsCache: expirable.NewLRU[string, []*pgs.RedirectRule](2048, nil, shared.CacheTimeout),
 36-		HeadersCache:   expirable.NewLRU[string, []*pgs.HeaderRule](2048, nil, shared.CacheTimeout),
 37-	}
 38+	proxy := newProxyServe(cfg.Logger)
 39+	httpCache := pgs.NewPgsHttpCache(cfg, proxy)
 40 	cacher := &cachedHttp{
 41-		handler: httpCache,
 42-		routes:  router,
 43+		Logger: cfg.Logger,
 44+		Cache:  httpCache,
 45 	}
 46 
 47-	go router.WatchCacheClear()
 48-	go router.CacheMgmt(ctx, httpCache, cfg.CacheClearingQueue)
 49+	go pgs.CacheMgmt(ctx, cfg.CacheClearingQueue, cfg, httpCache.Cache)
 50 
 51 	portStr := fmt.Sprintf(":%s", cfg.WebPort)
 52 	cfg.Logger.Info(
 53@@ -51,29 +46,54 @@ func main() {
 54 	cfg.Logger.Error("listen and serve", "err", err)
 55 }
 56 
 57-type cachedHttp struct {
 58-	handler *middleware.SouinBaseHandler
 59-	routes  *pgs.WebRouter
 60+type proxyServe struct {
 61+	Logger    *slog.Logger
 62+	transport *http.Transport
 63 }
 64 
 65-type CustomTransport struct {
 66-	*http.Transport
 67-	Logger *slog.Logger
 68+func newProxyServe(logger *slog.Logger) *proxyServe {
 69+	defaultTransport := http.DefaultTransport.(*http.Transport)
 70+	oldDial := defaultTransport.DialContext
 71+	transport := &http.Transport{
 72+		DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
 73+			return oldDial(ctx, "tcp", "ash.pgs.sh:443")
 74+		},
 75+	}
 76+	return &proxyServe{Logger: logger, transport: transport}
 77 }
 78 
 79-func (t *CustomTransport) RoundTrip(request *http.Request) (*http.Response, error) {
 80-	// reqDump, _ := httputil.DumpRequestOut(request, false)
 81-	// t.Logger.Info("request", "dump", string(reqDump))
 82-	response, err := http.DefaultTransport.RoundTrip(request)
 83+func (p *proxyServe) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 84+	target, _ := url.Parse(partialURL(req))
 85+	p.Logger.Info("proxying request to ash.pgs.sh", "url", target.String())
 86+
 87+	proxyReq := req.Clone(req.Context())
 88+	proxyReq.URL.Scheme = target.Scheme
 89+	proxyReq.URL.Host = target.Host
 90+	proxyReq.URL.Path = target.Path
 91+	proxyReq.URL.RawQuery = target.RawQuery
 92+	proxyReq.RequestURI = ""
 93+
 94+	resp, err := p.transport.RoundTrip(proxyReq)
 95+	if err != nil {
 96+		http.Error(w, err.Error(), http.StatusBadGateway)
 97+		return
 98+	}
 99+	defer func() {
100+		_ = resp.Body.Close()
101+	}()
102 
103-	// body, err := httputil.DumpResponse(response, false)
104-	// if err != nil {
105-	// 	// copying the response body did not work
106-	// 	return nil, err
107-	// }
108-	// t.Logger.Info("response", "dump", string(body))
109+	for k, vals := range resp.Header {
110+		for _, v := range vals {
111+			w.Header().Set(k, v)
112+		}
113+	}
114+	w.WriteHeader(resp.StatusCode)
115+	_, _ = io.Copy(w, resp.Body)
116+}
117 
118-	return response, err
119+type cachedHttp struct {
120+	Logger *slog.Logger
121+	Cache  *httpcache.HttpCache
122 }
123 
124 func (c *cachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
125@@ -83,7 +103,7 @@ func (c *cachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
126 	}
127 
128 	if req.URL.Path == "/check" {
129-		c.routes.Cfg.Logger.Info("proxying `/check` request to ash.pgs.sh", "query", req.URL.RawQuery)
130+		c.Logger.Info("proxying `/check` request to ash.pgs.sh", "query", req.URL.RawQuery)
131 		req, _ := http.NewRequest("GET", "https://ash.pgs.sh/check?"+req.URL.RawQuery, nil)
132 		req.Host = "pgs.sh"
133 		// reqDump, _ := httputil.DumpRequestOut(req, true)
134@@ -91,27 +111,16 @@ func (c *cachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
135 
136 		resp, err := http.DefaultClient.Do(req)
137 		if err != nil {
138-			c.routes.Cfg.Logger.Error("check request", "err", err)
139+			c.Logger.Error("check request", "err", err)
140 		}
141 		writer.WriteHeader(resp.StatusCode)
142-		return
143+		defer func() {
144+			_ = resp.Body.Close()
145+		}()
146+		_, _ = io.Copy(writer, resp.Body)
147 	}
148 
149-	_ = c.handler.ServeHTTP(writer, req, func(w http.ResponseWriter, r *http.Request) error {
150-		url, _ := url.Parse(partialURL(r))
151-
152-		c.routes.Cfg.Logger.Info("proxying request to ash.pgs.sh", "url", url.String())
153-		defaultTransport := http.DefaultTransport.(*http.Transport)
154-		oldDialContext := defaultTransport.DialContext
155-		newTransport := CustomTransport{Transport: defaultTransport, Logger: c.routes.Cfg.Logger}
156-		newTransport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
157-			return oldDialContext(ctx, "tcp", "ash.pgs.sh:443")
158-		}
159-		proxy := httputil.NewSingleHostReverseProxy(url)
160-		proxy.Transport = &newTransport
161-		proxy.ServeHTTP(w, r)
162-		return nil
163-	})
164+	c.Cache.ServeHTTP(writer, req)
165 }
166 
167 func partialURL(r *http.Request) string {
M go.mod
+3, -150
  1@@ -26,9 +26,6 @@ require (
  2 	github.com/antoniomika/syncmap v1.0.0
  3 	github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de
  4 	github.com/containerd/console v1.0.5
  5-	github.com/darkweak/souin v1.7.8
  6-	github.com/darkweak/souin/plugins/souin/storages v1.7.8
  7-	github.com/darkweak/storages/core v0.0.16
  8 	github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6
  9 	github.com/emersion/go-smtp v0.24.0
 10 	github.com/gkampitakis/go-snaps v0.5.15
 11@@ -61,86 +58,37 @@ require (
 12 	go.abhg.dev/goldmark/hashtag v0.4.0
 13 	go.abhg.dev/goldmark/toc v0.12.0
 14 	golang.org/x/crypto v0.47.0
 15-	google.golang.org/protobuf v1.36.11
 16 	gopkg.in/yaml.v2 v2.4.0
 17 	modernc.org/sqlite v1.44.3
 18 )
 19 
 20 require (
 21-	cel.dev/expr v0.25.1 // indirect
 22-	cloud.google.com/go/auth v0.18.1 // indirect
 23-	cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
 24-	cloud.google.com/go/compute/metadata v0.9.0 // indirect
 25 	codeberg.org/emersion/go-scfg v0.1.0 // indirect
 26 	dario.cat/mergo v1.0.2 // indirect
 27-	filippo.io/edwards25519 v1.1.0 // indirect
 28-	github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
 29 	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
 30-	github.com/KimMachineGun/automemlimit v0.7.5 // indirect
 31-	github.com/Masterminds/goutils v1.1.1 // indirect
 32-	github.com/Masterminds/semver/v3 v3.4.0 // indirect
 33-	github.com/Masterminds/sprig/v3 v3.3.0 // indirect
 34 	github.com/Microsoft/go-winio v0.6.2 // indirect
 35 	github.com/PuerkitoBio/goquery v1.11.0 // indirect
 36-	github.com/RoaringBitmap/roaring v1.9.4 // indirect
 37 	github.com/andybalholm/cascadia v1.3.3 // indirect
 38-	github.com/antlabs/stl v0.0.2 // indirect
 39-	github.com/antlabs/timer v0.1.4 // indirect
 40-	github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
 41-	github.com/armon/go-metrics v0.4.1 // indirect
 42-	github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
 43 	github.com/aymerick/douceur v0.2.0 // indirect
 44 	github.com/beorn7/perks v1.0.1 // indirect
 45-	github.com/bits-and-blooms/bitset v1.24.4 // indirect
 46-	github.com/buraksezer/consistent v0.10.0 // indirect
 47-	github.com/buraksezer/olric v0.5.7 // indirect
 48-	github.com/bwmarrin/snowflake v0.3.0 // indirect
 49-	github.com/caddyserver/caddy/v2 v2.10.2 // indirect
 50-	github.com/caddyserver/certmagic v0.25.1 // indirect
 51-	github.com/caddyserver/zerossl v0.1.4 // indirect
 52-	github.com/ccoveille/go-safecast v1.6.1 // indirect
 53 	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
 54-	github.com/cespare/xxhash v1.1.0 // indirect
 55 	github.com/cespare/xxhash/v2 v2.3.0 // indirect
 56-	github.com/chzyer/readline v1.5.1 // indirect
 57 	github.com/clipperhouse/stringish v0.1.1 // indirect
 58 	github.com/clipperhouse/uax29/v2 v2.5.0 // indirect
 59-	github.com/cloudflare/circl v1.6.3 // indirect
 60 	github.com/containerd/errdefs v1.0.0 // indirect
 61 	github.com/containerd/errdefs/pkg v0.3.0 // indirect
 62 	github.com/containerd/log v0.1.0 // indirect
 63 	github.com/containerd/platforms v0.2.1 // indirect
 64-	github.com/coreos/go-oidc/v3 v3.17.0 // indirect
 65-	github.com/coreos/go-semver v0.3.1 // indirect
 66-	github.com/coreos/go-systemd/v22 v22.7.0 // indirect
 67 	github.com/cpuguy83/dockercfg v0.3.2 // indirect
 68-	github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
 69-	github.com/darkweak/go-esi v0.0.6 // indirect
 70-	github.com/darkweak/storages/badger v0.0.16 // indirect
 71-	github.com/darkweak/storages/etcd v0.0.16 // indirect
 72-	github.com/darkweak/storages/nats v0.0.16 // indirect
 73-	github.com/darkweak/storages/nuts v0.0.16 // indirect
 74-	github.com/darkweak/storages/olric v0.0.16 // indirect
 75-	github.com/darkweak/storages/otter v0.0.16 // indirect
 76-	github.com/darkweak/storages/redis v0.0.16 // indirect
 77-	github.com/darkweak/storages/simplefs v0.0.16 // indirect
 78 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
 79 	github.com/delthas/go-libnp v0.2.0 // indirect
 80 	github.com/delthas/go-localeinfo v0.2.0 // indirect
 81-	github.com/dgraph-io/badger v1.6.2 // indirect
 82-	github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
 83-	github.com/dgraph-io/badger/v4 v4.9.1 // indirect
 84-	github.com/dgraph-io/ristretto v0.2.0 // indirect
 85-	github.com/dgraph-io/ristretto/v2 v2.4.0 // indirect
 86-	github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect
 87-	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 88 	github.com/disintegration/imaging v1.6.2 // indirect
 89 	github.com/distribution/reference v0.6.0 // indirect
 90 	github.com/dlclark/regexp2 v1.11.5 // indirect
 91 	github.com/docker/docker v28.5.1+incompatible // indirect
 92 	github.com/docker/go-connections v0.6.0 // indirect
 93 	github.com/docker/go-units v0.5.0 // indirect
 94-	github.com/dolthub/maphash v0.1.0 // indirect
 95 	github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d // indirect
 96 	github.com/dsoprea/go-exif/v2 v2.0.0-20230826092837-6579e82b732d // indirect
 97 	github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 // indirect
 98@@ -150,76 +98,34 @@ require (
 99 	github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 // indirect
100 	github.com/dustin/go-humanize v1.0.1 // indirect
101 	github.com/ebitengine/purego v0.8.4 // indirect
102-	github.com/edsrzf/mmap-go v1.2.0 // indirect
103 	github.com/felixge/httpsnoop v1.0.4 // indirect
104 	github.com/forPelevin/gomoji v1.4.1 // indirect
105-	github.com/francoispqt/gojay v1.2.13 // indirect
106-	github.com/gammazero/deque v1.2.0 // indirect
107 	github.com/gkampitakis/ciinfo v0.3.2 // indirect
108 	github.com/gkampitakis/go-diff v1.3.2 // indirect
109 	github.com/go-errors/errors v1.5.1 // indirect
110-	github.com/go-jose/go-jose/v3 v3.0.4 // indirect
111-	github.com/go-jose/go-jose/v4 v4.1.3 // indirect
112 	github.com/go-logr/logr v1.4.3 // indirect
113 	github.com/go-logr/stdr v1.2.2 // indirect
114 	github.com/go-ole/go-ole v1.3.0 // indirect
115-	github.com/go-redis/redis/v8 v8.11.5 // indirect
116 	github.com/go-sql-driver/mysql v1.9.3 // indirect
117 	github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
118 	github.com/goccy/go-yaml v1.18.0 // indirect
119 	github.com/godbus/dbus/v5 v5.2.2 // indirect
120-	github.com/gofrs/flock v0.13.0 // indirect
121-	github.com/gogo/protobuf v1.3.2 // indirect
122 	github.com/golang/geo v0.0.0-20260129164528-943061e2742c // indirect
123-	github.com/golang/protobuf v1.5.4 // indirect
124-	github.com/golang/snappy v1.0.0 // indirect
125-	github.com/google/btree v1.1.3 // indirect
126-	github.com/google/cel-go v0.27.0 // indirect
127-	github.com/google/flatbuffers v25.12.19+incompatible // indirect
128 	github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef // indirect
129-	github.com/google/s2a-go v0.1.9 // indirect
130-	github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
131-	github.com/googleapis/gax-go/v2 v2.17.0 // indirect
132 	github.com/gorilla/css v1.0.1 // indirect
133 	github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
134-	github.com/hashicorp/errwrap v1.1.0 // indirect
135-	github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
136-	github.com/hashicorp/go-metrics v0.5.4 // indirect
137-	github.com/hashicorp/go-msgpack/v2 v2.1.5 // indirect
138-	github.com/hashicorp/go-multierror v1.1.1 // indirect
139-	github.com/hashicorp/go-sockaddr v1.0.7 // indirect
140-	github.com/hashicorp/golang-lru v1.0.2 // indirect
141-	github.com/hashicorp/logutils v1.0.0 // indirect
142-	github.com/hashicorp/memberlist v0.5.4 // indirect
143-	github.com/huandu/xstrings v1.5.0 // indirect
144-	github.com/inconshreveable/mousetrap v1.1.0 // indirect
145-	github.com/jackc/pgpassfile v1.0.0 // indirect
146-	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
147 	github.com/jackc/pgx/v5 v5.8.0 // indirect
148-	github.com/jackc/puddle/v2 v2.2.2 // indirect
149-	github.com/jellydator/ttlcache/v3 v3.4.0 // indirect
150 	github.com/json-iterator/go v1.1.12 // indirect
151 	github.com/klauspost/compress v1.18.3 // indirect
152-	github.com/klauspost/cpuid/v2 v2.3.0 // indirect
153 	github.com/kr/fs v0.1.0 // indirect
154 	github.com/kr/pretty v0.3.1 // indirect
155 	github.com/kr/text v0.2.0 // indirect
156-	github.com/libdns/libdns v1.1.1 // indirect
157 	github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
158 	github.com/magiconair/properties v1.8.10 // indirect
159-	github.com/manifoldco/promptui v0.9.0 // indirect
160 	github.com/maruel/natural v1.1.1 // indirect
161-	github.com/mattn/go-colorable v0.1.14 // indirect
162 	github.com/mattn/go-isatty v0.0.20 // indirect
163 	github.com/mattn/go-runewidth v0.0.19 // indirect
164 	github.com/mattn/go-sixel v0.0.8 // indirect
165-	github.com/maypok86/otter v1.2.4 // indirect
166-	github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
167-	github.com/mholt/acmez/v3 v3.1.4 // indirect
168-	github.com/miekg/dns v1.1.72 // indirect
169-	github.com/mitchellh/copystructure v1.2.0 // indirect
170-	github.com/mitchellh/go-ps v1.0.0 // indirect
171-	github.com/mitchellh/reflectwalk v1.0.2 // indirect
172 	github.com/mmcdole/goxpp v1.1.1 // indirect
173 	github.com/mmcloughlin/md4 v0.1.2 // indirect
174 	github.com/moby/docker-image-spec v1.3.1 // indirect
175@@ -232,105 +138,52 @@ require (
176 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
177 	github.com/modern-go/reflect2 v1.0.2 // indirect
178 	github.com/morikuni/aec v1.0.0 // indirect
179-	github.com/mschoch/smat v0.2.0 // indirect
180 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
181-	github.com/nats-io/nats.go v1.48.0 // indirect
182-	github.com/nats-io/nkeys v0.4.15 // indirect
183-	github.com/nats-io/nuid v1.0.1 // indirect
184 	github.com/ncruces/go-strftime v1.0.0 // indirect
185 	github.com/neurosnap/go-jpeg-image-structure v0.0.0-20221010133817-70b1c1ff679e // indirect
186-	github.com/nutsdb/nutsdb v1.1.0 // indirect
187-	github.com/onsi/gomega v1.39.0 // indirect
188 	github.com/opencontainers/go-digest v1.0.0 // indirect
189 	github.com/opencontainers/image-spec v1.1.1 // indirect
190-	github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
191-	github.com/pierrec/lz4/v4 v4.1.25 // indirect
192 	github.com/pkg/errors v0.9.1 // indirect
193 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
194 	github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
195-	github.com/pquerna/cachecontrol v0.2.0 // indirect
196 	github.com/prometheus/client_model v0.6.2 // indirect
197 	github.com/prometheus/common v0.67.5 // indirect
198 	github.com/prometheus/procfs v0.19.2 // indirect
199-	github.com/quic-go/qpack v0.5.1 // indirect
200-	github.com/quic-go/quic-go v0.54.0 // indirect
201-	github.com/redis/rueidis v1.0.71 // indirect
202 	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
203 	github.com/rivo/uniseg v0.4.7 // indirect
204 	github.com/rogpeppe/go-internal v1.14.1 // indirect
205-	github.com/rs/xid v1.6.0 // indirect
206-	github.com/russross/blackfriday/v2 v2.1.0 // indirect
207-	github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
208 	github.com/shirou/gopsutil/v4 v4.25.6 // indirect
209-	github.com/shopspring/decimal v1.4.0 // indirect
210-	github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
211 	github.com/sirupsen/logrus v1.9.4 // indirect
212-	github.com/slackhq/nebula v1.9.5 // indirect
213-	github.com/smallstep/certificates v0.28.4 // indirect
214-	github.com/smallstep/cli-utils v0.12.1 // indirect
215-	github.com/smallstep/linkedca v0.23.0 // indirect
216-	github.com/smallstep/nosql v0.7.0 // indirect
217-	github.com/smallstep/pkcs7 v0.2.1 // indirect
218-	github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492 // indirect
219-	github.com/smallstep/truststore v0.13.0 // indirect
220 	github.com/soniakeys/quant v1.0.0 // indirect
221-	github.com/spf13/cast v1.10.0 // indirect
222-	github.com/spf13/cobra v1.10.2 // indirect
223-	github.com/spf13/pflag v1.0.10 // indirect
224 	github.com/stretchr/testify v1.11.1 // indirect
225-	github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
226-	github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747 // indirect
227-	github.com/tidwall/btree v1.8.1 // indirect
228 	github.com/tidwall/gjson v1.18.0 // indirect
229 	github.com/tidwall/match v1.2.0 // indirect
230 	github.com/tidwall/pretty v1.2.1 // indirect
231-	github.com/tidwall/redcon v1.6.2 // indirect
232 	github.com/tidwall/sjson v1.2.5 // indirect
233 	github.com/tklauser/go-sysconf v0.3.15 // indirect
234 	github.com/tklauser/numcpus v0.10.0 // indirect
235-	github.com/urfave/cli v1.22.17 // indirect
236-	github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
237-	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
238-	github.com/xujiajun/utils v0.0.0-20220904132955-5f7c5b914235 // indirect
239 	github.com/yusufpapurcu/wmi v1.2.4 // indirect
240-	github.com/zeebo/blake3 v0.2.4 // indirect
241-	go.etcd.io/bbolt v1.4.3 // indirect
242-	go.etcd.io/etcd/api/v3 v3.6.7 // indirect
243-	go.etcd.io/etcd/client/pkg/v3 v3.6.7 // indirect
244-	go.etcd.io/etcd/client/v3 v3.6.7 // indirect
245 	go.opentelemetry.io/auto/sdk v1.2.1 // indirect
246 	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect
247 	go.opentelemetry.io/otel v1.40.0 // indirect
248+	go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
249 	go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 // indirect
250 	go.opentelemetry.io/otel/metric v1.40.0 // indirect
251 	go.opentelemetry.io/otel/trace v1.40.0 // indirect
252-	go.step.sm/crypto v0.76.0 // indirect
253-	go.uber.org/automaxprocs v1.6.0 // indirect
254-	go.uber.org/mock v0.6.0 // indirect
255-	go.uber.org/multierr v1.11.0 // indirect
256-	go.uber.org/zap v1.27.1 // indirect
257-	go.uber.org/zap/exp v0.3.0 // indirect
258+	go.opentelemetry.io/proto/otlp v1.7.0 // indirect
259 	go.yaml.in/yaml/v2 v2.4.3 // indirect
260-	go.yaml.in/yaml/v3 v3.0.4 // indirect
261-	golang.org/x/crypto/x509roots/fallback v0.0.0-20260113154411-7d0074ccc6f1 // indirect
262 	golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
263 	golang.org/x/image v0.35.0 // indirect
264-	golang.org/x/mod v0.32.0 // indirect
265 	golang.org/x/net v0.49.0 // indirect
266-	golang.org/x/oauth2 v0.34.0 // indirect
267 	golang.org/x/sync v0.19.0 // indirect
268 	golang.org/x/sys v0.40.0 // indirect
269-	golang.org/x/term v0.39.0 // indirect
270 	golang.org/x/text v0.33.0 // indirect
271 	golang.org/x/time v0.14.0 // indirect
272-	golang.org/x/tools v0.41.0 // indirect
273-	google.golang.org/api v0.265.0 // indirect
274 	google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect
275 	google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 // indirect
276 	google.golang.org/grpc v1.78.0 // indirect
277-	google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1 // indirect
278+	google.golang.org/protobuf v1.36.11 // indirect
279 	gopkg.in/yaml.v3 v3.0.1 // indirect
280-	howett.net/plist v1.0.1 // indirect
281 	modernc.org/libc v1.67.7 // indirect
282 	modernc.org/mathutil v1.7.1 // indirect
283 	modernc.org/memory v1.11.0 // indirect
M go.sum
+0, -765
   1@@ -1,64 +1,21 @@
   2-cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4=
   3-cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4=
   4-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
   5-cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
   6-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
   7-cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
   8-cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c=
   9-cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI=
  10-cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
  11-cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
  12-cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
  13-cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
  14-cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
  15-cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
  16-cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
  17-cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
  18-cloud.google.com/go/kms v1.24.0 h1:SWltUuoPhTdv9q/P0YEAWQfoYT32O5HdfPgTiWMvrH8=
  19-cloud.google.com/go/kms v1.24.0/go.mod h1:QDH3z2SJ50lfNOE8EokKC1G40i7I0f8xTMCoiptcb5g=
  20-cloud.google.com/go/longrunning v0.7.0 h1:FV0+SYF1RIj59gyoWDRi45GiYUMM3K1qO51qoboQT1E=
  21-cloud.google.com/go/longrunning v0.7.0/go.mod h1:ySn2yXmjbK9Ba0zsQqunhDkYi0+9rlXIwnoAf+h+TPY=
  22 codeberg.org/emersion/go-scfg v0.1.0 h1:6dnGU0ZI4gX+O5rMjwhoaySItzHG710eXL5TIQKl+uM=
  23 codeberg.org/emersion/go-scfg v0.1.0/go.mod h1:0nooW1ufBB4SlJEdTtiVN9Or+bnNM1icOkQ6Tbrq6O0=
  24 dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
  25 dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
  26-dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
  27-dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
  28-dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
  29-dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
  30 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
  31 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
  32-git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
  33 git.sr.ht/~delthas/senpai v0.4.1 h1:5CABpwJVzIBQaXtOngxG9TQDcxlaUqmGsXxlW+XcLuM=
  34 git.sr.ht/~delthas/senpai v0.4.1/go.mod h1:mzdu4o3wANA6cYzRnrz3w+uGPHA2z3j02JDrr/M3Myc=
  35 git.sr.ht/~rockorager/vaxis v0.15.1-0.20251218121515-cdf898cf10c7 h1:Hik9uYRclWV+mt5PnGc41aeGX6sh8MGzWcBnJt9EzpY=
  36 git.sr.ht/~rockorager/vaxis v0.15.1-0.20251218121515-cdf898cf10c7/go.mod h1:ptCAzb19xAhqhfAmCsN9Xnxdx01vlXkwZcuMYPnyqzE=
  37 github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
  38 github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
  39-github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
  40-github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
  41 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
  42 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
  43-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
  44-github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
  45-github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
  46-github.com/KimMachineGun/automemlimit v0.7.5 h1:RkbaC0MwhjL1ZuBKunGDjE/ggwAX43DwZrJqVwyveTk=
  47-github.com/KimMachineGun/automemlimit v0.7.5/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
  48-github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
  49-github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
  50-github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
  51-github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
  52-github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
  53-github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
  54 github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
  55 github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
  56-github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
  57-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
  58 github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
  59 github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
  60-github.com/RoaringBitmap/roaring v1.2.1/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
  61-github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ=
  62-github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
  63 github.com/adhocore/gronx v1.19.6 h1:5KNVcoR9ACgL9HhEqCm5QXsab/gI4QDIybTAWcXDKDc=
  64 github.com/adhocore/gronx v1.19.6/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg=
  65 github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
  66@@ -69,114 +26,24 @@ github.com/alecthomas/chroma/v2 v2.23.1/go.mod h1:NqVhfBR0lte5Ouh3DcthuUCTUpDC9c
  67 github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
  68 github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
  69 github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
  70-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
  71-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
  72-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
  73-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
  74-github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
  75 github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
  76 github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
  77-github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
  78-github.com/antlabs/stl v0.0.2 h1:sna1AXR5yIkNE9lWhCcKbheFJSVfCa3vugnGyakI79s=
  79-github.com/antlabs/stl v0.0.2/go.mod h1:kKrO4xrn9cfS1mJVo+/BqePZjAYMXqD0amGF2Ouq7ac=
  80-github.com/antlabs/timer v0.1.4 h1:MHdE00MDnNfhJCmqSOdLXs35uGNwfkMwfbynxrGmQ1c=
  81-github.com/antlabs/timer v0.1.4/go.mod h1:mpw4zlD5KVjstEyUDp43DGLWsY076Mdo4bS78NTseRE=
  82-github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ=
  83-github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
  84 github.com/antoniomika/syncmap v1.0.0 h1:iFSfbQFQOvHZILFZF+hqWosO0no+W9+uF4y2VEyMKWU=
  85 github.com/antoniomika/syncmap v1.0.0/go.mod h1:fK2829foEYnO4riNfyUn0SHQZt4ue3DStYjGU+sJj38=
  86 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
  87 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
  88-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
  89-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
  90-github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
  91-github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
  92-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
  93-github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
  94-github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
  95-github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
  96-github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
  97-github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY=
  98-github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY=
  99-github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8=
 100-github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw=
 101-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY=
 102-github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA=
 103-github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
 104-github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
 105-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
 106-github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
 107-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
 108-github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
 109-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
 110-github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
 111-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY=
 112-github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU=
 113-github.com/aws/aws-sdk-go-v2/service/kms v1.49.5 h1:DKibav4XF66XSeaXcrn9GlWGHos6D/vJ4r7jsK7z5CE=
 114-github.com/aws/aws-sdk-go-v2/service/kms v1.49.5/go.mod h1:1SdcmEGUEQE1mrU2sIgeHtcMSxHuybhPvuEPANzIDfI=
 115-github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
 116-github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M=
 117-github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk=
 118-github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw=
 119-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 h1:gd84Omyu9JLriJVCbGApcLzVR3XtmC4ZDPcAI6Ftvds=
 120-github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo=
 121-github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ=
 122-github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ=
 123-github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
 124-github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
 125 github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
 126 github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
 127-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 128-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 129 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 130 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 131-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 132-github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
 133-github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
 134-github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE=
 135-github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
 136-github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
 137-github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
 138-github.com/buraksezer/consistent v0.10.0 h1:hqBgz1PvNLC5rkWcEBVAL9dFMBWz6I0VgUCW25rrZlU=
 139-github.com/buraksezer/consistent v0.10.0/go.mod h1:6BrVajWq7wbKZlTOUPs/XVfR8c0maujuPowduSpZqmw=
 140-github.com/buraksezer/olric v0.5.7 h1:K8ypVViiPkXiqBz3UyDAY99cHvvofAR65fmH7ElPEWE=
 141-github.com/buraksezer/olric v0.5.7/go.mod h1:S1R+9Zt7P9TCbvQZvY/RYuRehLLRPDfbJNkukQsLJ4k=
 142-github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
 143-github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
 144-github.com/caddyserver/caddy/v2 v2.10.2 h1:g/gTYjGMD0dec+UgMw8SnfmJ3I9+M2TdvoRL/Ovu6U8=
 145-github.com/caddyserver/caddy/v2 v2.10.2/go.mod h1:TXLQHx+ev4HDpkO6PnVVHUbL6OXt6Dfe7VcIBdQnPL0=
 146-github.com/caddyserver/certmagic v0.25.1 h1:4sIKKbOt5pg6+sL7tEwymE1x2bj6CHr80da1CRRIPbY=
 147-github.com/caddyserver/certmagic v0.25.1/go.mod h1:VhyvndxtVton/Fo/wKhRoC46Rbw1fmjvQ3GjHYSQTEY=
 148-github.com/caddyserver/zerossl v0.1.4 h1:CVJOE3MZeFisCERZjkxIcsqIH4fnFdlYWnPYeFtBHRw=
 149-github.com/caddyserver/zerossl v0.1.4/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
 150-github.com/ccoveille/go-safecast v1.6.1 h1:Nb9WMDR8PqhnKCVs2sCB+OqhohwO5qaXtCviZkIff5Q=
 151-github.com/ccoveille/go-safecast v1.6.1/go.mod h1:QqwNjxQ7DAqY0C721OIO9InMk9zCwcsO7tnRuHytad8=
 152 github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
 153 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 154-github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 155-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 156-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 157-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 158 github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
 159 github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 160-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 161-github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
 162-github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
 163-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 164-github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
 165-github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
 166-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 167-github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
 168-github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
 169-github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
 170-github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
 171-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 172 github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
 173 github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
 174 github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U=
 175 github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
 176-github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
 177-github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
 178 github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc=
 179 github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
 180 github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
 181@@ -187,49 +54,11 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
 182 github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
 183 github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
 184 github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
 185-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 186-github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 187-github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
 188-github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
 189-github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 190-github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
 191-github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
 192-github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 193-github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
 194-github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
 195 github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
 196 github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
 197-github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 198-github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
 199-github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
 200-github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
 201 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
 202 github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
 203 github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
 204-github.com/darkweak/go-esi v0.0.6 h1:eVHCJfqrZwOHPfRK7JTlSYG9F8lfpX/d4lz/41RQkd8=
 205-github.com/darkweak/go-esi v0.0.6/go.mod h1:IJSayeQZDUh5R5ayyDC3wUEBykti12aUa0eUxZZeodk=
 206-github.com/darkweak/souin v1.7.8 h1:yENXy0oSzknu0Uzz/bO01rYLCcmAITlDgvEEtbT10PE=
 207-github.com/darkweak/souin v1.7.8/go.mod h1:kR97vndGl0jEWmHazJXNTdVkOzQis05kUdK9EGKUf+k=
 208-github.com/darkweak/souin/plugins/souin/storages v1.7.8 h1:7x+FU8CU7EKr96hnMTh6wuX0OHfyBBkSr03X2WY7MRs=
 209-github.com/darkweak/souin/plugins/souin/storages v1.7.8/go.mod h1:3OmumRhtRKzIalqlqIHDuvaaZMSJDoO5q1b1RRnCZRw=
 210-github.com/darkweak/storages/badger v0.0.16 h1:5NNHescfvT0YqqgeCvRB9cFtCeCvEetPliCYOI+aGqo=
 211-github.com/darkweak/storages/badger v0.0.16/go.mod h1:hpKJ7pGI27elfu1IJ1OnsBpsyYx627oeebtL+DbN0mI=
 212-github.com/darkweak/storages/core v0.0.16 h1:f7+XY8MJaKiukcOC5v48pTSpD0zpzEu4FnfgOAPbyF0=
 213-github.com/darkweak/storages/core v0.0.16/go.mod h1:3qJqrenCLpu+0bWPOAq36CmGpzL3SGWAz6KGZGnur1U=
 214-github.com/darkweak/storages/etcd v0.0.16 h1:l9p4+tinfc6AUITHld3g5lzBbLokfIudowEIPK2E1+U=
 215-github.com/darkweak/storages/etcd v0.0.16/go.mod h1:UnkdHq06kZS9QeQ9ItNIN9Bl7YZZVdVclX+kcvAd+k8=
 216-github.com/darkweak/storages/nats v0.0.16 h1:9KaHq9q+tK4Tm1JjAaj6YFGy8/9u5AJK9O9tFx+NvYI=
 217-github.com/darkweak/storages/nats v0.0.16/go.mod h1:yWkupn1y7R4wPzLgqgXc65c50X+7q94QQ9PCxwLqIWA=
 218-github.com/darkweak/storages/nuts v0.0.16 h1:EJ6b8YbXjK7PsdE6mMRKKs6qj3G+5dW4PfmzCNXrdsA=
 219-github.com/darkweak/storages/nuts v0.0.16/go.mod h1:MPNNFqFAuiFLDsPcsxXSG5h5dRea1Wd6cXAzHXnFD1Q=
 220-github.com/darkweak/storages/olric v0.0.16 h1:gv7S+eWAmjnYbhMVEmltEHG6Kzacil7nTXpKfXT47F4=
 221-github.com/darkweak/storages/olric v0.0.16/go.mod h1:PyZuWYNVN0y4lFNN1ZqEWxOW59nzQfXU6aiJnQegxcM=
 222-github.com/darkweak/storages/otter v0.0.16 h1:SFPlKIapzcrfSp16C3/K6fzIqRblqDWE/xyse9VhJFE=
 223-github.com/darkweak/storages/otter v0.0.16/go.mod h1:FSqYWBBSoojOZEH4hr19TmBnBxyH+ZQ4V8tGp4xVBGw=
 224-github.com/darkweak/storages/redis v0.0.16 h1:dlDUpco8V6DnOwqS5oYFIi4aLBtto9u0ZyJqbpyy22U=
 225-github.com/darkweak/storages/redis v0.0.16/go.mod h1:kBchmscY7C7URnruedPgb3jcijV1COzszfOe/KVTk5g=
 226-github.com/darkweak/storages/simplefs v0.0.16 h1:9duSjUTKjZWUv2Spk5+3MueENwxElEWlp0Tij2CihEY=
 227-github.com/darkweak/storages/simplefs v0.0.16/go.mod h1:TMjB3kGbYHZPZoRGTCW+Wph9taF2/ym0cUfP0jYU47s=
 228 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 229 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 230 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
 231@@ -238,23 +67,6 @@ github.com/delthas/go-libnp v0.2.0 h1:OVll7Z9CER0rYpgiglXN1QvuNtkyY5X9uzLqm+xDst
 232 github.com/delthas/go-libnp v0.2.0/go.mod h1:LHUapIgbv1vBcZJU4cJRPkyslKhKe7GNRF38yUbl9QA=
 233 github.com/delthas/go-localeinfo v0.2.0 h1:0K5F5GQ5p4ealZjuBs4eubHLOIL21/YWSxs557hHZ+M=
 234 github.com/delthas/go-localeinfo v0.2.0/go.mod h1:sG54BxlyQgIskYURLrg7mvhoGBe0Qq12DNtYRALwNa4=
 235-github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
 236-github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
 237-github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
 238-github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
 239-github.com/dgraph-io/badger/v4 v4.9.1 h1:DocZXZkg5JJHJPtUErA0ibyHxOVUDVoXLSCV6t8NC8w=
 240-github.com/dgraph-io/badger/v4 v4.9.1/go.mod h1:5/MEx97uzdPUHR4KtkNt8asfI2T4JiEiQlV7kWUo8c0=
 241-github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
 242-github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
 243-github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
 244-github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
 245-github.com/dgraph-io/ristretto/v2 v2.4.0 h1:I/w09yLjhdcVD2QV192UJcq8dPBaAJb9pOuMyNy0XlU=
 246-github.com/dgraph-io/ristretto/v2 v2.4.0/go.mod h1:0KsrXtXvnv0EqnzyowllbVJB8yBonswa2lTCK2gGo9E=
 247-github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
 248-github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
 249-github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
 250-github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 251-github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
 252 github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
 253 github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
 254 github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
 255@@ -269,8 +81,6 @@ github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pM
 256 github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
 257 github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
 258 github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 259-github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
 260-github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4=
 261 github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d h1:ygcRCGNKuEiA98k7X35hknEN8RIRUF1jrz7k1rZCvsk=
 262 github.com/dsoprea/go-exif v0.0.0-20230826092837-6579e82b732d/go.mod h1:lOaOt7+UEppOgyvRy749v3do836U/hw0YVJNjoyPaEs=
 263 github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
 264@@ -295,40 +105,24 @@ github.com/dsoprea/go-utility v0.0.0-20200711062821-fab8125e9bdf/go.mod h1:95+K3
 265 github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349 h1:/py11NlxDaOxkT9OKN+gXgT+QOH5xj1ZRoyusfRIlo4=
 266 github.com/dsoprea/go-utility v0.0.0-20221003172846-a3e1774ef349/go.mod h1:KVK+/Hul09ujXAGq+42UBgCTnXkiJZRnLYdURGjQUwo=
 267 github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
 268-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 269 github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
 270 github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 271 github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
 272 github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
 273-github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84=
 274-github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
 275 github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
 276 github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
 277 github.com/emersion/go-smtp v0.24.0 h1:g6AfoF140mvW0vLNPD/LuCBLEAdlxOjIXqbIkJIS6Wk=
 278 github.com/emersion/go-smtp v0.24.0/go.mod h1:ZtRRkbTyp2XTHCA+BmyTFTrj8xY4I+b4McvHxCU2gsQ=
 279-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 280 github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
 281 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 282-github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
 283 github.com/forPelevin/gomoji v1.4.1 h1:7U+Bl8o6RV/dOQz7coQFWj/jX6Ram6/cWFOuFDEPEUo=
 284 github.com/forPelevin/gomoji v1.4.1/go.mod h1:mM6GtmCgpoQP2usDArc6GjbXrti5+FffolyQfGgPboQ=
 285-github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
 286-github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
 287-github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
 288-github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
 289-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 290-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 291-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
 292-github.com/gammazero/deque v1.2.0 h1:scEFO8Uidhw6KDU5qg1HA5fYwM0+us2qdeJqm43bitU=
 293-github.com/gammazero/deque v1.2.0/go.mod h1:JVrR+Bj1NMQbPnYclvDlvSX0nVGReLrQZ0aUMuWLctg=
 294-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 295 github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
 296 github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
 297 github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
 298 github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
 299 github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE=
 300 github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
 301-github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
 302 github.com/go-andiamo/splitter v1.2.5 h1:P3NovWMY2V14TJJSolXBvlOmGSZo3Uz+LtTl2bsV/eY=
 303 github.com/go-andiamo/splitter v1.2.5/go.mod h1:8WHU24t9hcMKU5FXDQb1hysSEC/GPuivIp0uKY1J8gw=
 304 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
 305@@ -336,16 +130,6 @@ github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWE
 306 github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
 307 github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
 308 github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
 309-github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
 310-github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
 311-github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
 312-github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
 313-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 314-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 315-github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
 316-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 317-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 318-github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
 319 github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 320 github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
 321 github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
 322@@ -354,13 +138,9 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
 323 github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 324 github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
 325 github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
 326-github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
 327-github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
 328 github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
 329 github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
 330 github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
 331-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 332-github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 333 github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
 334 github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY=
 335 github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
 336@@ -368,135 +148,32 @@ github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
 337 github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
 338 github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ=
 339 github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c=
 340-github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
 341-github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
 342-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 343-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 344-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 345 github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
 346 github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
 347 github.com/golang/geo v0.0.0-20260129164528-943061e2742c h1:ysO2h2Odnl1AJM1I2Lm/fa6JvO0pECMSt2CwBaa+ITo=
 348 github.com/golang/geo v0.0.0-20260129164528-943061e2742c/go.mod h1:Mymr9kRGDc64JPr03TSZmuIBODZ3KyswLzm1xL0HFA8=
 349-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 350-github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
 351-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 352-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 353-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 354-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 355-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 356-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
 357-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
 358-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
 359-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
 360-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
 361-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 362-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
 363-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 364-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 365-github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 366-github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
 367-github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 368-github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
 369-github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 370-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 371-github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
 372-github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
 373-github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo=
 374-github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw=
 375-github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo=
 376-github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k=
 377-github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs=
 378-github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
 379-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 380-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 381-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 382-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 383-github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 384-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 385-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 386 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 387 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 388 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
 389-github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
 390-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
 391-github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
 392-github.com/google/go-tpm v0.9.8/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
 393-github.com/google/go-tpm-tools v0.4.7 h1:J3ycC8umYxM9A4eF73EofRZu4BxY0jjQnUnkhIBbvws=
 394-github.com/google/go-tpm-tools v0.4.7/go.mod h1:gSyXTZHe3fgbzb6WEGd90QucmsnT1SRdlye82gH8QjQ=
 395-github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
 396-github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
 397 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 398-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 399-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 400-github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 401 github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef h1:xpF9fUHpoIrrjX24DURVKiwHcFpw19ndIs+FwTSMbno=
 402 github.com/google/pprof v0.0.0-20260202012954-cb029daf43ef/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
 403 github.com/google/renameio/v2 v2.0.2 h1:qKZs+tfn+arruZZhQ7TKC/ergJunuJicWS6gLDt/dGw=
 404 github.com/google/renameio/v2 v2.0.2/go.mod h1:OX+G6WHHpHq3NVj7cAOleLOwJfcQ1s3uUJQCrr78SWo=
 405-github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
 406-github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
 407 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 408 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 409-github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao=
 410-github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8=
 411-github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
 412-github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
 413-github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=
 414-github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=
 415-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 416 github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
 417 github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
 418 github.com/gorilla/feeds v1.2.0 h1:O6pBiXJ5JHhPvqy53NsjKOThq+dNFm8+DFrxBEdzSCc=
 419 github.com/gorilla/feeds v1.2.0/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
 420 github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
 421 github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 422-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 423-github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
 424 github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak=
 425 github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII=
 426-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 427-github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
 428-github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 429-github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 430-github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 431-github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
 432-github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 433-github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY=
 434-github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI=
 435-github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
 436-github.com/hashicorp/go-msgpack/v2 v2.1.5 h1:Ue879bPnutj/hXfmUk6s/jtIK90XxgiUIcXRl656T44=
 437-github.com/hashicorp/go-msgpack/v2 v2.1.5/go.mod h1:bjCsRXpZ7NsJdk45PoCQnzRGDaK8TKm5ZnDI/9y3J4M=
 438-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
 439-github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
 440-github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 441-github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
 442-github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
 443-github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
 444-github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
 445-github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
 446-github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 447-github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
 448-github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
 449-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 450-github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
 451-github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
 452 github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
 453 github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
 454-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 455-github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y=
 456-github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
 457-github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
 458-github.com/hashicorp/memberlist v0.5.4 h1:40YY+3qq2tAUhZIMEK8kqusKZBBjdwJ3NUjvYkcxh74=
 459-github.com/hashicorp/memberlist v0.5.4/go.mod h1:OgN6xiIo6RlHUWk+ALjP9e32xWCoQrsOCmHrWCm2MWA=
 460 github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
 461 github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
 462-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 463-github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
 464-github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
 465-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 466-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 467-github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 468-github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 469 github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
 470 github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
 471 github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
 472@@ -505,41 +182,17 @@ github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=
 473 github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=
 474 github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
 475 github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
 476-github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
 477-github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=
 478-github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
 479 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 480 github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
 481 github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
 482-github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 483-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 484-github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 485-github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 486-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 487 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 488 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 489-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 490-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 491-github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
 492-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
 493-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 494-github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
 495 github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
 496 github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
 497-github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
 498-github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
 499-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 500-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 501 github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
 502 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
 503-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 504-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 505-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
 506 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
 507 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
 508-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 509-github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 510-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 511 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 512 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 513 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
 514@@ -547,25 +200,14 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
 515 github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 516 github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI=
 517 github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
 518-github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U=
 519-github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
 520 github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc=
 521 github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
 522-github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
 523-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 524 github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
 525 github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
 526-github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 527-github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
 528-github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
 529 github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
 530 github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
 531 github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
 532 github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
 533-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 534-github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
 535-github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
 536-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 537 github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
 538 github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 539 github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
 540@@ -575,32 +217,10 @@ github.com/mattn/go-sixel v0.0.8 h1:H0bBGQVOJoSvzvtTgCInxvg1IZiNlTcIIIx8A6uvjpQ=
 541 github.com/mattn/go-sixel v0.0.8/go.mod h1:wbDSbrwpykVI1qEHyjZYsDgaJTwpVg9wSwmmh2slnBw=
 542 github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
 543 github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
 544-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 545-github.com/maypok86/otter v1.2.4 h1:HhW1Pq6VdJkmWwcZZq19BlEQkHtI8xgsQzBVXJU0nfc=
 546-github.com/maypok86/otter v1.2.4/go.mod h1:mKLfoI7v1HOmQMwFgX4QkRk23mX6ge3RDvjdHOWG4R4=
 547 github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI=
 548 github.com/mdelapenya/tlscert v0.2.0/go.mod h1:O4njj3ELLnJjGdkN7M/vIVCpZ+Cf0L6muqOG4tLSl8o=
 549-github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
 550-github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
 551-github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ=
 552-github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
 553-github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
 554 github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
 555 github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
 556-github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
 557-github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
 558-github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
 559-github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
 560-github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
 561-github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
 562-github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
 563-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 564-github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
 565-github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
 566-github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
 567-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 568-github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
 569-github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
 570 github.com/mmcdole/gofeed v1.3.0 h1:5yn+HeqlcvjMeAI4gu6T+crm7d0anY85+M+v6fIFNG4=
 571 github.com/mmcdole/gofeed v1.3.0/go.mod h1:9TGv2LcJhdXePDzxiuMnukhV2/zb6VtnZt1mS+SjkLE=
 572 github.com/mmcdole/goxpp v1.1.1 h1:RGIX+D6iQRIunGHrKqnA2+700XMCnNv0bAOOv5MUhx8=
 573@@ -626,71 +246,27 @@ github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3
 574 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 575 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 576 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 577-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 578-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 579 github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
 580 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 581 github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
 582 github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
 583-github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
 584-github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
 585 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
 586 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 587-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 588-github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 589-github.com/nats-io/nats.go v1.48.0 h1:pSFyXApG+yWU/TgbKCjmm5K4wrHu86231/w84qRVR+U=
 590-github.com/nats-io/nats.go v1.48.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
 591-github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4=
 592-github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs=
 593-github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
 594-github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
 595 github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
 596 github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
 597-github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
 598-github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
 599 github.com/neurosnap/go-exif-remove v0.0.0-20221010134343-50d1e3c35577 h1:hVmVNttSLNloGsbFKVXAUHonXTd8KKrv30U/8UkloKI=
 600 github.com/neurosnap/go-exif-remove v0.0.0-20221010134343-50d1e3c35577/go.mod h1:G3Cu1AW+dmRLDFpOi8eUAfc3cGoRHUjTkGjeRcndgl4=
 601 github.com/neurosnap/go-jpeg-image-structure v0.0.0-20221010133817-70b1c1ff679e h1:76Dng5ms0fR+26doKZAvNqhi2UPfnLxGfPIDEr+BBlM=
 602 github.com/neurosnap/go-jpeg-image-structure v0.0.0-20221010133817-70b1c1ff679e/go.mod h1:nZBDA7+RD63GDJwjZmxhxac65MJqiCIHUUUvdYOsFkk=
 603-github.com/nutsdb/nutsdb v1.1.0 h1:fNGFzBHGqF2mB5BF8Qk8W94c3/ZzwdCdKAH7azwx70Y=
 604-github.com/nutsdb/nutsdb v1.1.0/go.mod h1:aKCtgSprZf2Mp1dIQD00Iya3DttoTErSSOnRx5ZtpAs=
 605-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 606-github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 607-github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
 608-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 609-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
 610-github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
 611-github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 612-github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
 613-github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
 614-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 615-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 616-github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
 617-github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
 618-github.com/onsi/gomega v1.39.0 h1:y2ROC3hKFmQZJNFeGAMeHZKkjBL65mIZcvrLQBF9k6Q=
 619-github.com/onsi/gomega v1.39.0/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
 620 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 621 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 622 github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
 623 github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
 624-github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
 625-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 626-github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
 627-github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
 628-github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
 629-github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
 630-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 631-github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
 632-github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
 633 github.com/picosh/go-rsync-receiver v0.0.0-20250304201040-fcc11dd22d79 h1:MyB9P43hlQ6A2FoP9LGeiTBL3WKToW4gcWd6lQPg/Zg=
 634 github.com/picosh/go-rsync-receiver v0.0.0-20250304201040-fcc11dd22d79/go.mod h1:4ZICsr6bESoHP8He9DqROlZiMw4hHHjcbDzhtTTDQzA=
 635 github.com/picosh/utils v0.0.0-20260125160622-5c3a9e231ec6 h1:9KfCtfcx7vrSyGU1K9whdE1crll9Aq+nAZ6c0FzuzvE=
 636 github.com/picosh/utils v0.0.0-20260125160622-5c3a9e231ec6/go.mod h1:HogYEyJ43IGXrOa3D/kjM1pkzNAyh+pejRyv8Eo//pk=
 637-github.com/pierrec/lz4/v4 v4.1.25 h1:kocOqRffaIbU5djlIBr7Wh+cx82C0vtFb0fOurZHqD0=
 638-github.com/pierrec/lz4/v4 v4.1.25/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
 639 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 640-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 641-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 642 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 643 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 644 github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
 645@@ -698,47 +274,16 @@ github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1Hbe
 646 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 647 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 648 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 649-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
 650 github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
 651 github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
 652-github.com/pquerna/cachecontrol v0.2.0 h1:vBXSNuE5MYP9IJ5kjsdo8uq+w41jSPgvba2DEnkRx9k=
 653-github.com/pquerna/cachecontrol v0.2.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI=
 654-github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
 655-github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
 656-github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 657-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 658-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 659-github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
 660-github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 661-github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 662 github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
 663 github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
 664-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 665-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 666-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 667 github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
 668 github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
 669-github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 670-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 671-github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
 672-github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
 673-github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
 674 github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
 675 github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
 676-github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 677-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 678-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 679-github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
 680-github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 681-github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
 682 github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
 683 github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
 684-github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
 685-github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
 686-github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg=
 687-github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
 688-github.com/redis/rueidis v1.0.71 h1:pODtnAR5GAB7j4ekhldZ29HKOxe4Hph0GTDGk1ayEQY=
 689-github.com/redis/rueidis v1.0.71/go.mod h1:lfdcZzJ1oKGKL37vh9fO3ymwt+0TdjkkUCJxbgpmcgQ=
 690 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
 691 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 692 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 693@@ -747,125 +292,29 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
 694 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 695 github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
 696 github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
 697-github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
 698-github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
 699-github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 700-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 701-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 702-github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 703 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
 704 github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
 705-github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
 706-github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
 707 github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
 708-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
 709-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 710-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 711 github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
 712 github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
 713-github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
 714-github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
 715-github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
 716-github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
 717-github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
 718-github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
 719-github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
 720-github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
 721-github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
 722-github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
 723-github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
 724-github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
 725-github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
 726-github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
 727-github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 728-github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
 729-github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
 730-github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
 731-github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
 732-github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
 733-github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
 734-github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 735-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
 736-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 737-github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
 738-github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
 739 github.com/simplesurance/go-ip-anonymizer v0.0.0-20200429124537-35a880f8e87d h1:4FkGkGts6gLznca6fgclIvbupwbq543mb/fFkog4VIg=
 740 github.com/simplesurance/go-ip-anonymizer v0.0.0-20200429124537-35a880f8e87d/go.mod h1:fTTj1EOmRdtuwYw3jF/1X2dTa0N1BdbZhrpA21N/S4I=
 741-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 742-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 743-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 744 github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
 745 github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
 746-github.com/slackhq/nebula v1.9.5 h1:ZrxcvP/lxwFglaijmiwXLuCSkybZMJnqSYI1S8DtGnY=
 747-github.com/slackhq/nebula v1.9.5/go.mod h1:1+4q4wd3dDAjO8rKCttSb9JIVbklQhuJiBp5I0lbIsQ=
 748-github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY=
 749-github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
 750-github.com/smallstep/certificates v0.28.4 h1:JTU6/A5Xes6m+OsR6fw1RACSA362vJc9SOFVG7poBEw=
 751-github.com/smallstep/certificates v0.28.4/go.mod h1:LUqo+7mKZE7FZldlTb0zhU4A0bq4G4+akieFMcTaWvA=
 752-github.com/smallstep/cli-utils v0.12.1 h1:D9QvfbFqiKq3snGZ2xDcXEFrdFJ1mQfPHZMq/leerpE=
 753-github.com/smallstep/cli-utils v0.12.1/go.mod h1:skV2Neg8qjiKPu2fphM89H9bIxNpKiiRTnX9Q6Lc+20=
 754-github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca h1:VX8L0r8vybH0bPeaIxh4NQzafKQiqvlOn8pmOXbFLO4=
 755-github.com/smallstep/go-attestation v0.4.4-0.20241119153605-2306d5b464ca/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
 756-github.com/smallstep/linkedca v0.23.0 h1:5W/7EudlK1HcCIdZM68dJlZ7orqCCCyv6bm2l/0JmLU=
 757-github.com/smallstep/linkedca v0.23.0/go.mod h1:7cyRM9soAYySg9ag65QwytcgGOM+4gOlkJ/YA58A9E8=
 758-github.com/smallstep/nosql v0.7.0 h1:YiWC9ZAHcrLCrayfaF+QJUv16I2bZ7KdLC3RpJcnAnE=
 759-github.com/smallstep/nosql v0.7.0/go.mod h1:H5VnKMCbeq9QA6SRY5iqPylfxLfYcLwvUff3onQ8+HU=
 760-github.com/smallstep/pkcs7 v0.2.1 h1:6Kfzr/QizdIuB6LSv8y1LJdZ3aPSfTNhTLqAx9CTLfA=
 761-github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0=
 762-github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492 h1:k23+s51sgYix4Zgbvpmy+1ZgXLjr4ZTkBTqXmpnImwA=
 763-github.com/smallstep/scep v0.0.0-20250318231241-a25cabb69492/go.mod h1:QQhwLqCS13nhv8L5ov7NgusowENUtXdEzdytjmJHdZQ=
 764-github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
 765-github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
 766 github.com/soniakeys/quant v1.0.0 h1:N1um9ktjbkZVcywBVAAYpZYSHxEfJGzshHCxx/DaI0Y=
 767 github.com/soniakeys/quant v1.0.0/go.mod h1:HI1k023QuVbD4H8i9YdfZP2munIHU4QpjsImz6Y6zds=
 768-github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
 769-github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
 770-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 771-github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
 772-github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 773-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 774-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 775-github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
 776-github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
 777-github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 778-github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
 779-github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
 780-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 781-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 782-github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 783-github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
 784-github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 785-github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 786 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 787-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 788-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
 789-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 790 github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
 791 github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
 792-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 793 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 794-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 795-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 796 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 797 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 798-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 799-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 800-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 801-github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 802 github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
 803 github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
 804-github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
 805-github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
 806-github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747 h1:RnBbFMmodYzhC6adOjTbtUQXyzV8dcvKYbolzs6Qch0=
 807-github.com/tailscale/tscert v0.0.0-20251216020129-aea342f6d747/go.mod h1:ejPAJui3kVK4u5TgMtqtXlWf5HnKh9fLy5kvpaeuas0=
 808-github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
 809 github.com/testcontainers/testcontainers-go v0.40.0 h1:pSdJYLOVgLE8YdUY2FHQ1Fxu+aMnb6JfVz1mxk7OeMU=
 810 github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY=
 811 github.com/testcontainers/testcontainers-go/modules/postgres v0.40.0 h1:s2bIayFXlbDFexo96y+htn7FzuhpXLYJNnIuglNKqOk=
 812 github.com/testcontainers/testcontainers-go/modules/postgres v0.40.0/go.mod h1:h+u/2KoREGTnTl9UwrQ/g+XhasAT8E6dClclAADeXoQ=
 813-github.com/tidwall/btree v1.1.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4=
 814-github.com/tidwall/btree v1.8.1 h1:27ehoXvm5AG/g+1VxLS1SD3vRhp/H7LuEfwNvddEdmA=
 815-github.com/tidwall/btree v1.8.1/go.mod h1:jBbTdUWhSZClZWoDg54VnvV7/54modSOzDN7VXftj1A=
 816 github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 817 github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
 818 github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 819@@ -875,33 +324,14 @@ github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
 820 github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 821 github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
 822 github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 823-github.com/tidwall/redcon v1.6.2 h1:5qfvrrybgtO85jnhSravmkZyC0D+7WstbfCs3MmPhow=
 824-github.com/tidwall/redcon v1.6.2/go.mod h1:p5Wbsgeyi2VSTBWOcA5vRXrOb9arFTcU2+ZzFjqV75Y=
 825 github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
 826 github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
 827 github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
 828 github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
 829 github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
 830 github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
 831-github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
 832-github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 833-github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
 834-github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
 835-github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
 836-github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
 837-github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
 838-github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
 839-github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
 840-github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
 841-github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
 842 github.com/x-way/crawlerdetect v0.2.30 h1:U43R8+TZ7AZwBZehWRPdRdW53NmPoVZSOptevJKo1mE=
 843 github.com/x-way/crawlerdetect v0.2.30/go.mod h1:BPHLsB3FOuiwoWyhAvnqeiUSAEKd34O7BcsTCcxHRj4=
 844-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 845-github.com/xujiajun/utils v0.0.0-20220904132955-5f7c5b914235 h1:w0si+uee0iAaCJO9q86T6yrhdadgcsoNuh47LrUykzg=
 846-github.com/xujiajun/utils v0.0.0-20220904132955-5f7c5b914235/go.mod h1:MR4+0R6A9NS5IABnIM3384FfOq8QFVnm7WDrBOhIaMU=
 847-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 848-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 849-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 850 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 851 github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 852 github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
 853@@ -912,31 +342,14 @@ github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUei
 854 github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
 855 github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
 856 github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
 857-github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
 858-github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
 859-github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
 860-github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
 861-github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
 862-github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
 863 go.abhg.dev/goldmark/anchor v0.2.0 h1:RQZTodRc6VHSUoQYKFlyH0pokbhk1klwUuGgDmjGp2E=
 864 go.abhg.dev/goldmark/anchor v0.2.0/go.mod h1:Ym74zBV+QBKxK9ITOty680N9FT8otgGYvtYXroJUWms=
 865 go.abhg.dev/goldmark/hashtag v0.4.0 h1:jkYcsIRUpog/rhWMnIOjj14qj8p5p4um+IzPbPUxFww=
 866 go.abhg.dev/goldmark/hashtag v0.4.0/go.mod h1:5DQwSo2dEHKtsfXOC1+Po7WgVwR5IoGjgeVIerddz9g=
 867 go.abhg.dev/goldmark/toc v0.12.0 h1:kiEBBIOB7jEzNpXmGdiL2L/zGSELKw/p3mosm2+RSuo=
 868 go.abhg.dev/goldmark/toc v0.12.0/go.mod h1:kskbM5l9y8wOFEFfyEe9wnwhWeykvmHB6xEPCVrZIvg=
 869-go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
 870-go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
 871-go.etcd.io/etcd/api/v3 v3.6.7 h1:7BNJ2gQmc3DNM+9cRkv7KkGQDayElg8x3X+tFDYS+E0=
 872-go.etcd.io/etcd/api/v3 v3.6.7/go.mod h1:xJ81TLj9hxrYYEDmXTeKURMeY3qEDN24hqe+q7KhbnI=
 873-go.etcd.io/etcd/client/pkg/v3 v3.6.7 h1:vvzgyozz46q+TyeGBuFzVuI53/yd133CHceNb/AhBVs=
 874-go.etcd.io/etcd/client/pkg/v3 v3.6.7/go.mod h1:2IVulJ3FZ/czIGl9T4lMF1uxzrhRahLqe+hSgy+Kh7Q=
 875-go.etcd.io/etcd/client/v3 v3.6.7 h1:9WqA5RpIBtdMxAy1ukXLAdtg2pAxNqW5NUoO2wQrE6U=
 876-go.etcd.io/etcd/client/v3 v3.6.7/go.mod h1:2XfROY56AXnUqGsvl+6k29wrwsSbEh1lAouQB1vHpeE=
 877-go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
 878 go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
 879 go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
 880-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
 881-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
 882 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
 883 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
 884 go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
 885@@ -955,57 +368,23 @@ go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZY
 886 go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
 887 go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
 888 go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
 889-go.step.sm/crypto v0.76.0 h1:K23BSaeoiY7Y5dvvijTeYC9EduDBetNwQYMBwMhi1aA=
 890-go.step.sm/crypto v0.76.0/go.mod h1:PXYJdKkK8s+GHLwLguFaLxHNAFsFL3tL1vSBrYfey5k=
 891-go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
 892-go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
 893 go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
 894 go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
 895-go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
 896-go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
 897-go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
 898-go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
 899-go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
 900-go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
 901-go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
 902-go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
 903 go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
 904 go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
 905-go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
 906-go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
 907-go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
 908-golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
 909-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 910-golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 911-golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 912 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 913-golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 914-golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
 915-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 916-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 917 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 918 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
 919 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 920 golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
 921-golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
 922 golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
 923-golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
 924 golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
 925 golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
 926-golang.org/x/crypto/x509roots/fallback v0.0.0-20260113154411-7d0074ccc6f1 h1:EBHQuS9qI8xJ96+YRgVV2ahFLUYbWpt1rf3wPfXN2wQ=
 927-golang.org/x/crypto/x509roots/fallback v0.0.0-20260113154411-7d0074ccc6f1/go.mod h1:MEIPiCnxvQEjA4astfaKItNwEVZA5Ki+3+nyGbJ5N18=
 928-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 929 golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
 930 golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
 931 golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 932 golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=
 933 golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=
 934-golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 935-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 936-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 937-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 938-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 939-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 940 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 941 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 942 golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 943@@ -1013,110 +392,40 @@ golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 944 golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
 945 golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
 946 golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
 947-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 948-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 949-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 950-golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 951-golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 952-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 953-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 954-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 955-golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 956-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 957-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 958 golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 959-golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 960 golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 961-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 962 golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 963 golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 964 golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 965-golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 966 golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
 967-golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 968-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 969 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 970-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 971-golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 972-golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 973 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 974 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 975 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
 976 golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
 977 golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
 978 golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
 979-golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
 980 golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
 981 golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
 982 golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
 983-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 984-golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 985-golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 986-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 987-golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
 988-golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
 989-golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
 990-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 991-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 992-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 993-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 994 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 995-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 996-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 997-golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 998-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 999 golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
1000 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
1001 golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
1002 golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
1003 golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
1004-golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
1005 golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
1006-golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
1007 golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
1008 golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
1009-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1010-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1011-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1012-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1013-golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1014-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1015-golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1016-golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1017 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
1018-golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1019-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1020-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1021-golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1022-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1023 golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1024-golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1025-golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1026-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1027-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1028-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1029-golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1030-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1031 golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1032-golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1033-golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1034-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1035 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1036 golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1037-golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1038-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1039-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1040-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
1041-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1042-golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1043 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1044 golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1045-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1046-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1047-golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1048 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1049 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1050-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1051 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1052 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1053 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1054@@ -1124,9 +433,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1055 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1056 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
1057 golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
1058-golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
1059 golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
1060-golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
1061 golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
1062 golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
1063 golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
1064@@ -1137,42 +444,24 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
1065 golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
1066 golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
1067 golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
1068-golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
1069 golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
1070-golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
1071 golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
1072 golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
1073 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
1074-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
1075-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
1076 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
1077-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
1078 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
1079 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
1080 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
1081 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
1082 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
1083 golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
1084-golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
1085 golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
1086-golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
1087 golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
1088 golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
1089-golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
1090-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
1091 golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
1092 golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
1093-golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
1094 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
1095-golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
1096-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
1097-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
1098-golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
1099 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
1100-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
1101-golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
1102-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
1103-golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
1104 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
1105 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
1106 golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
1107@@ -1180,63 +469,17 @@ golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxb
1108 golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
1109 golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
1110 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
1111-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
1112-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
1113-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
1114-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
1115-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
1116-google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
1117-google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
1118-google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
1119-google.golang.org/api v0.265.0 h1:FZvfUdI8nfmuNrE34aOWFPmLC+qRBEiNm3JdivTvAAU=
1120-google.golang.org/api v0.265.0/go.mod h1:uAvfEl3SLUj/7n6k+lJutcswVojHPp2Sp08jWCu8hLY=
1121-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
1122-google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
1123-google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
1124-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
1125-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
1126-google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
1127-google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
1128-google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
1129-google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
1130-google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
1131-google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
1132 google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0=
1133 google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE=
1134 google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20 h1:Jr5R2J6F6qWyzINc+4AM8t5pfUz6beZpHp678GNrMbE=
1135 google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
1136-google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
1137-google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
1138-google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
1139-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
1140 google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
1141 google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
1142-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1 h1:/WILD1UcXj/ujCxgoL/DvRgt2CP3txG8+FwkUbb9110=
1143-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.1/go.mod h1:YNKnb2OAApgYn2oYY47Rn7alMr1zWjb2U8Q0aoGWiNc=
1144-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
1145-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
1146-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
1147-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
1148-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
1149-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
1150-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
1151-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
1152 google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
1153 google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
1154-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
1155 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1156-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1157 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
1158 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
1159-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
1160-gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
1161-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
1162-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
1163-gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
1164-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
1165-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
1166-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
1167-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
1168 gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
1169 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
1170 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
1171@@ -1246,12 +489,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
1172 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
1173 gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
1174 gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
1175-grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
1176-honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
1177-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
1178-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
1179-howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
1180-howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
1181 modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
1182 modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
1183 modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
1184@@ -1284,5 +521,3 @@ mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
1185 mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=
1186 pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=
1187 pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
1188-sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
1189-sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
M pkg/apps/pgs/config.go
+0, -5
 1@@ -12,7 +12,6 @@ import (
 2 )
 3 
 4 type PgsConfig struct {
 5-	CacheControl       string
 6 	CacheTTL           time.Duration
 7 	Domain             string
 8 	MaxAssetSize       int64
 9@@ -75,15 +74,11 @@ func NewPgsConfig(logger *slog.Logger, dbpool pgsdb.PgsDB, st storage.StorageSer
10 	if err != nil {
11 		cacheTTL = 600 * time.Second
12 	}
13-	cacheControl := shared.GetEnv(
14-		"PGS_CACHE_CONTROL",
15-		fmt.Sprintf("max-age=%d", int(cacheTTL.Seconds())))
16 
17 	sshHost := shared.GetEnv("PGS_SSH_HOST", "0.0.0.0")
18 	sshPort := shared.GetEnv("PGS_SSH_PORT", "2222")
19 
20 	cfg := PgsConfig{
21-		CacheControl:       cacheControl,
22 		CacheTTL:           cacheTTL,
23 		Domain:             domain,
24 		MaxAssetSize:       maxAssetSize,
D pkg/apps/pgs/souin_truncation_test.go
+0, -183
  1@@ -1,183 +0,0 @@
  2-package pgs
  3-
  4-import (
  5-	"bytes"
  6-	"log/slog"
  7-	"net/http"
  8-	"net/http/httptest"
  9-	"strings"
 10-	"testing"
 11-
 12-	"github.com/picosh/pico/pkg/shared"
 13-	"github.com/picosh/pico/pkg/storage"
 14-)
 15-
 16-// TestLargeFileNotTruncatedOnCacheHit reproduces the Souin truncation bug.
 17-// Large files (3MB) are cached as truncated (~4KB) on the second request.
 18-//
 19-// Bug behavior:
 20-// - First request (cache miss): Returns full 3MB file ✓
 21-// - Second request (cache hit): Returns only ~4KB (truncated!) ✗
 22-//
 23-// Root cause: Souin's Store() snapshots the buffer mid-stream while
 24-// io.Copy() is still writing data, resulting in partial cache entries.
 25-func TestLargeFileNotTruncatedOnCacheHit(t *testing.T) {
 26-	logger := slog.Default()
 27-	dbpool := NewPgsDb(logger)
 28-	bucketName := shared.GetAssetBucketName(dbpool.Users[0].ID)
 29-
 30-	// 3MB payload - reproduces the exact bug scenario
 31-	largePayload := bytes.Repeat([]byte("x"), 3*1024*1024)
 32-	expectedSize := len(largePayload)
 33-
 34-	st, err := storage.NewStorageMemory(map[string]map[string]string{
 35-		bucketName: {
 36-			"/test/large-file.bin": string(largePayload),
 37-		},
 38-	})
 39-	if err != nil {
 40-		t.Fatalf("storage setup failed: %v", err)
 41-	}
 42-
 43-	pubsub := NewPubsubChan()
 44-	defer func() {
 45-		_ = pubsub.Close()
 46-	}()
 47-
 48-	cfg := NewPgsConfig(logger, dbpool, st, pubsub)
 49-	cfg.Domain = "pgs.test"
 50-	// Increase max asset size for testing large files
 51-	cfg.MaxAssetSize = 10 * 1024 * 1024 // 10MB
 52-
 53-	// Set up the full stack WITH Souin HTTP caching middleware
 54-	httpCache := SetupCache(cfg)
 55-	routes := NewWebRouter(cfg)
 56-	cacher := &CachedHttp{
 57-		handler: httpCache,
 58-		routes:  routes,
 59-	}
 60-
 61-	// First request (cache miss)
 62-	req1 := httptest.NewRequest("GET", dbpool.mkpath("/large-file.bin"), strings.NewReader(""))
 63-	rec1 := httptest.NewRecorder()
 64-	cacher.ServeHTTP(rec1, req1)
 65-
 66-	if rec1.Code != http.StatusOK {
 67-		t.Fatalf("first request failed with status %d", rec1.Code)
 68-	}
 69-
 70-	body1 := rec1.Body.String()
 71-	size1 := len(body1)
 72-
 73-	if size1 != expectedSize {
 74-		t.Errorf("first request: expected %d bytes, got %d bytes", expectedSize, size1)
 75-	}
 76-
 77-	t.Logf("Cache miss: received %d bytes (expected %d)", size1, expectedSize)
 78-
 79-	// Second request (cache hit) - This is where the bug manifests
 80-	req2 := httptest.NewRequest("GET", dbpool.mkpath("/large-file.bin"), strings.NewReader(""))
 81-	rec2 := httptest.NewRecorder()
 82-	cacher.ServeHTTP(rec2, req2)
 83-
 84-	if rec2.Code != http.StatusOK {
 85-		t.Fatalf("second request failed with status %d", rec2.Code)
 86-	}
 87-
 88-	body2 := rec2.Body.String()
 89-	size2 := len(body2)
 90-
 91-	t.Logf("Cache hit: received %d bytes (expected %d)", size2, expectedSize)
 92-
 93-	// CRITICAL ASSERTION: Both requests must return the full file
 94-	if size2 != expectedSize {
 95-		t.Errorf("SOUIN_TRUNCATION_BUG: cache hit returned %d bytes instead of %d bytes",
 96-			size2, expectedSize)
 97-
 98-		// Show evidence of truncation
 99-		if size2 < 10000 {
100-			t.Logf("Truncated response: only %d bytes (about %dKB)", size2, size2/1024)
101-		}
102-	}
103-
104-	// Verify both responses are identical
105-	if body1 != body2 {
106-		t.Errorf("cache hit response differs from cache miss")
107-		t.Errorf("Cache miss: %d bytes, Cache hit: %d bytes", size1, size2)
108-	}
109-}
110-
111-// TestMediumFileNotTruncatedOnCacheHit tests with 512KB files
112-// which can trigger the race condition due to buffering behavior.
113-func TestMediumFileNotTruncatedOnCacheHit(t *testing.T) {
114-	logger := slog.Default()
115-	dbpool := NewPgsDb(logger)
116-	bucketName := shared.GetAssetBucketName(dbpool.Users[0].ID)
117-
118-	// 512KB payload with repeating pattern
119-	payload := bytes.Repeat([]byte("0123456789ABCDEF"), 512*1024/16)
120-	expectedSize := len(payload)
121-
122-	st, err := storage.NewStorageMemory(map[string]map[string]string{
123-		bucketName: {
124-			"/test/medium-file.bin": string(payload),
125-		},
126-	})
127-	if err != nil {
128-		t.Fatalf("storage setup failed: %v", err)
129-	}
130-
131-	pubsub := NewPubsubChan()
132-	defer func() {
133-		_ = pubsub.Close()
134-	}()
135-
136-	cfg := NewPgsConfig(logger, dbpool, st, pubsub)
137-	cfg.Domain = "pgs.test"
138-	// Increase max asset size for testing large files
139-	cfg.MaxAssetSize = 10 * 1024 * 1024 // 10MB
140-
141-	httpCache := SetupCache(cfg)
142-	routes := NewWebRouter(cfg)
143-	cacher := &CachedHttp{
144-		handler: httpCache,
145-		routes:  routes,
146-	}
147-
148-	// First request (cache miss)
149-	req1 := httptest.NewRequest("GET", dbpool.mkpath("/medium-file.bin"), strings.NewReader(""))
150-	rec1 := httptest.NewRecorder()
151-	cacher.ServeHTTP(rec1, req1)
152-
153-	body1 := rec1.Body.String()
154-	size1 := len(body1)
155-
156-	// Second request (cache hit)
157-	req2 := httptest.NewRequest("GET", dbpool.mkpath("/medium-file.bin"), strings.NewReader(""))
158-	rec2 := httptest.NewRecorder()
159-	cacher.ServeHTTP(rec2, req2)
160-
161-	body2 := rec2.Body.String()
162-	size2 := len(body2)
163-
164-	t.Logf("Cache miss: %d bytes, Cache hit: %d bytes (expected %d)", size1, size2, expectedSize)
165-
166-	// Verify complete responses
167-	if size1 != expectedSize {
168-		t.Errorf("cache miss: expected %d bytes, got %d", expectedSize, size1)
169-	}
170-
171-	if size2 != expectedSize {
172-		t.Errorf("SOUIN_TRUNCATION_BUG: cache hit returned %d bytes instead of %d bytes",
173-			size2, expectedSize)
174-	}
175-
176-	// Verify content matches original
177-	if body1 != string(payload) {
178-		t.Errorf("cache miss response body doesn't match")
179-	}
180-
181-	if body2 != string(payload) {
182-		t.Errorf("cache hit response body doesn't match")
183-	}
184-}
M pkg/apps/pgs/web.go
+126, -168
  1@@ -8,7 +8,6 @@ import (
  2 	"html/template"
  3 	"log/slog"
  4 	"net/http"
  5-	"net/url"
  6 	"os"
  7 	"path/filepath"
  8 	"regexp"
  9@@ -17,71 +16,116 @@ import (
 10 
 11 	_ "net/http/pprof"
 12 
 13-	"github.com/darkweak/souin/configurationtypes"
 14-	"github.com/darkweak/souin/pkg/middleware"
 15-	"github.com/darkweak/souin/plugins/souin/storages"
 16-	"github.com/darkweak/storages/core"
 17 	"github.com/gorilla/feeds"
 18 	"github.com/hashicorp/golang-lru/v2/expirable"
 19 	"github.com/picosh/pico/pkg/db"
 20+	"github.com/picosh/pico/pkg/httpcache"
 21 	"github.com/picosh/pico/pkg/shared"
 22 	"github.com/picosh/pico/pkg/shared/router"
 23 	"github.com/picosh/pico/pkg/storage"
 24+	"github.com/prometheus/client_golang/prometheus"
 25+	"github.com/prometheus/client_golang/prometheus/promauto"
 26 	"github.com/prometheus/client_golang/prometheus/promhttp"
 27-	"google.golang.org/protobuf/proto"
 28 )
 29 
 30-type CachedHttp struct {
 31-	handler *middleware.SouinBaseHandler
 32-	routes  *WebRouter
 33+type PgsCacheKey struct {
 34+	Domain    string
 35+	TxtPrefix string
 36+}
 37+
 38+func (c *PgsCacheKey) GetCacheKey(r *http.Request) string {
 39+	subdomain := router.GetSubdomainFromRequest(r, c.Domain, c.TxtPrefix)
 40+	return subdomain + "__" + r.Method + "__" + r.URL.RequestURI()
 41+}
 42+
 43+type PromCacheMetrics struct {
 44+	Cache          httpcache.Cacher
 45+	CacheItems     prometheus.Gauge
 46+	CacheSizeBytes prometheus.Gauge
 47+	CacheHit       prometheus.Counter
 48+	CacheMiss      prometheus.Counter
 49+	UpstreamReq    prometheus.Counter
 50+}
 51+
 52+func NewPromCacheMetrics(reg prometheus.Registerer) *PromCacheMetrics {
 53+	name := "pgs"
 54+	auto := promauto.With(reg)
 55+	return &PromCacheMetrics{
 56+		CacheItems: auto.NewGauge(prometheus.GaugeOpts{
 57+			Namespace: name,
 58+			Subsystem: "http_cache",
 59+			Name:      "total_items",
 60+			Help:      "Number of items in the http cache",
 61+		}),
 62+		CacheSizeBytes: auto.NewGauge(prometheus.GaugeOpts{
 63+			Namespace: name,
 64+			Subsystem: "http_cache",
 65+			Name:      "total_size",
 66+			Help:      "The total size of the http cache in bytes",
 67+		}),
 68+		CacheHit: auto.NewCounter(prometheus.CounterOpts{
 69+			Namespace: name,
 70+			Subsystem: "http_cache",
 71+			Name:      "cache_hit",
 72+			Help:      "The number of times there was a cache hit",
 73+		}),
 74+		CacheMiss: auto.NewCounter(prometheus.CounterOpts{
 75+			Namespace: name,
 76+			Subsystem: "http_cache",
 77+			Name:      "cache_miss",
 78+			Help:      "The number of times there was a cache miss",
 79+		}),
 80+		UpstreamReq: auto.NewCounter(prometheus.CounterOpts{
 81+			Namespace: name,
 82+			Subsystem: "http_cache",
 83+			Name:      "upstream_request",
 84+			Help:      "The number of times the upstream http server was requested",
 85+		}),
 86+	}
 87 }
 88-
 89-func (c *CachedHttp) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
 90-	_ = c.handler.ServeHTTP(writer, req, func(w http.ResponseWriter, r *http.Request) error {
 91-		c.routes.ServeHTTP(w, r)
 92-		return nil
 93-	})
 94+func (p *PromCacheMetrics) AddCacheItem(size float64) {
 95+	p.CacheItems.Add(1)
 96+	p.CacheSizeBytes.Add(size)
 97+}
 98+func (p *PromCacheMetrics) EvictCacheItem(key string, value []byte) {
 99+	p.CacheItems.Add(-1)
100+	p.CacheSizeBytes.Add(-float64(len(value)))
101+}
102+func (p *PromCacheMetrics) AddCacheHit() {
103+	p.CacheHit.Add(1)
104+}
105+func (p *PromCacheMetrics) AddCacheMiss() {
106+	p.CacheMiss.Add(1)
107+}
108+func (p *PromCacheMetrics) AddUpstreamRequest() {
109+	p.UpstreamReq.Add(1)
110 }
111 
112-func SetupCache(cfg *PgsConfig) *middleware.SouinBaseHandler {
113-	ttl := configurationtypes.Duration{Duration: cfg.CacheTTL}
114-	stale := configurationtypes.Duration{Duration: cfg.CacheTTL * 2}
115-	c := &middleware.BaseConfiguration{
116-		API: configurationtypes.API{
117-			Prometheus: configurationtypes.APIEndpoint{
118-				Enable: true,
119-			},
120-		},
121-		DefaultCache: &configurationtypes.DefaultCache{
122-			TTL:   ttl,
123-			Stale: stale,
124-			Otter: configurationtypes.CacheProvider{
125-				Uuid:          fmt.Sprintf("OTTER-%s", stale),
126-				Configuration: map[string]interface{}{},
127-			},
128-			Regex: configurationtypes.Regex{
129-				Exclude: "/check|/_metrics",
130-			},
131-			MaxBodyBytes:        uint64(cfg.MaxAssetSize),
132-			DefaultCacheControl: cfg.CacheControl,
133+func NewPgsHttpCache(cfg *PgsConfig, upstream http.Handler) *httpcache.HttpCache {
134+	ttl := cfg.CacheTTL
135+	metrics := NewPromCacheMetrics(prometheus.DefaultRegisterer)
136+	cache := expirable.NewLRU(0, metrics.EvictCacheItem, ttl)
137+	httpCache := &httpcache.HttpCache{
138+		Ttl:      ttl,
139+		Logger:   cfg.Logger,
140+		Upstream: upstream,
141+		Cache:    cache,
142+		CacheKey: &PgsCacheKey{
143+			Domain:    cfg.Domain,
144+			TxtPrefix: cfg.TxtPrefix,
145 		},
146+		CacheMetrics: metrics,
147 	}
148-	c.SetLogger(&CompatLogger{Logger: cfg.Logger})
149-	storages.InitFromConfiguration(c)
150-	return middleware.NewHTTPCacheHandler(c)
151+	httpCache.Logger.Info("httpcache initiated", "ttl", httpCache.Ttl, "storage", "lru")
152+	return httpCache
153 }
154 
155 func StartApiServer(cfg *PgsConfig) {
156 	ctx := context.Background()
157 
158-	httpCache := SetupCache(cfg)
159-	routes := NewWebRouter(cfg)
160-	cacher := &CachedHttp{
161-		handler: httpCache,
162-		routes:  routes,
163-	}
164-
165-	go routes.CacheMgmt(ctx, httpCache, cfg.CacheClearingQueue)
166+	router := NewWebRouter(cfg)
167+	httpCache := NewPgsHttpCache(router.Cfg, router)
168+	go CacheMgmt(ctx, cfg.CacheClearingQueue, cfg, httpCache.Cache)
169 
170 	portStr := fmt.Sprintf(":%s", cfg.WebPort)
171 	cfg.Logger.Info(
172@@ -89,7 +133,7 @@ func StartApiServer(cfg *PgsConfig) {
173 		"port", cfg.WebPort,
174 		"domain", cfg.Domain,
175 	)
176-	err := http.ListenAndServe(portStr, cacher)
177+	err := http.ListenAndServe(portStr, httpCache)
178 	cfg.Logger.Error(
179 		"listen and serve",
180 		"err", err.Error(),
181@@ -122,6 +166,26 @@ func newWebRouter(cfg *PgsConfig) *WebRouter {
182 	return router
183 }
184 
185+func (web *WebRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
186+	subdomain := router.GetSubdomainFromRequest(r, web.Cfg.Domain, web.Cfg.TxtPrefix)
187+	if web.RootRouter == nil || web.UserRouter == nil {
188+		web.Cfg.Logger.Error("routers not initialized")
189+		http.Error(w, "routers not initialized", http.StatusInternalServerError)
190+		return
191+	}
192+
193+	var mux *http.ServeMux
194+	if subdomain == "" {
195+		mux = web.RootRouter
196+	} else {
197+		mux = web.UserRouter
198+	}
199+
200+	ctx := r.Context()
201+	ctx = context.WithValue(ctx, router.CtxSubdomainKey{}, subdomain)
202+	mux.ServeHTTP(w, r.WithContext(ctx))
203+}
204+
205 func (web *WebRouter) WatchCacheClear() {
206 	for key := range web.Cfg.CacheClearingQueue {
207 		web.Cfg.Logger.Info("lru cache clear request", "key", key)
208@@ -288,56 +352,27 @@ func (web *WebRouter) checkHandler(w http.ResponseWriter, r *http.Request) {
209 	w.WriteHeader(http.StatusNotFound)
210 }
211 
212-func (web *WebRouter) CacheMgmt(ctx context.Context, httpCache *middleware.SouinBaseHandler, notify chan string) {
213-	storer := httpCache.Storers[0]
214-
215+func CacheMgmt(ctx context.Context, notify chan string, cfg *PgsConfig, cacher httpcache.Cacher) {
216+	cfg.Logger.Info("cache mgmt initiated")
217 	for {
218-		scanner := bufio.NewScanner(web.Cfg.Pubsub)
219+		scanner := bufio.NewScanner(cfg.Pubsub)
220 		scanner.Buffer(make([]byte, 32*1024), 32*1024)
221 		for scanner.Scan() {
222-			surrogateKey := strings.TrimSpace(scanner.Text())
223-			web.Cfg.Logger.Info("received cache-drain item", "surrogateKey", surrogateKey)
224-			notify <- surrogateKey
225-
226-			if surrogateKey == "*" {
227-				storer.DeleteMany(".+")
228-				err := httpCache.SurrogateKeyStorer.Destruct()
229-				if err != nil {
230-					web.Cfg.Logger.Error("could not clear cache and surrogate key store", "err", err)
231-				} else {
232-					web.Cfg.Logger.Info("successfully cleared cache and surrogate keys store")
233-				}
234+			subdomain := strings.TrimSpace(scanner.Text())
235+			cfg.Logger.Info("received cache-drain item", "subdomain", subdomain)
236+			notify <- subdomain
237+
238+			if subdomain == "*" {
239+				cacher.Purge()
240+				cfg.Logger.Info("successfully cleared cache from remote cli request")
241 				continue
242 			}
243 
244-			var header http.Header = map[string][]string{}
245-			header.Add("Surrogate-Key", surrogateKey)
246-
247-			ck, _ := httpCache.SurrogateKeyStorer.Purge(header)
248-			for _, key := range ck {
249-				key, _ = strings.CutPrefix(key, core.MappingKeyPrefix)
250-				if b := storer.Get(core.MappingKeyPrefix + key); len(b) > 0 {
251-					var mapping core.StorageMapper
252-					if e := proto.Unmarshal(b, &mapping); e == nil {
253-						for k := range mapping.GetMapping() {
254-							qkey, _ := url.QueryUnescape(k)
255-							web.Cfg.Logger.Info(
256-								"deleting key from surrogate cache",
257-								"surrogateKey", surrogateKey,
258-								"key", qkey,
259-							)
260-							storer.Delete(qkey)
261-						}
262-					}
263+			for _, key := range cacher.Keys() {
264+				if strings.HasPrefix(key, subdomain) {
265+					cfg.Logger.Info("deleting cache item", "subdomain", subdomain, "key", key)
266+					_ = cacher.Remove(key)
267 				}
268-
269-				qkey, _ := url.QueryUnescape(key)
270-				web.Cfg.Logger.Info(
271-					"deleting from cache",
272-					"surrogateKey", surrogateKey,
273-					"key", core.MappingKeyPrefix+qkey,
274-				)
275-				storer.Delete(core.MappingKeyPrefix + qkey)
276 			}
277 		}
278 	}
279@@ -573,80 +608,3 @@ func (web *WebRouter) handleLogin(w http.ResponseWriter, r *http.Request) {
280 func (web *WebRouter) handleAutoForm(w http.ResponseWriter, r *http.Request) {
281 	handleAutoForm(w, r, web.Cfg)
282 }
283-
284-func (web *WebRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
285-	subdomain := router.GetSubdomainFromRequest(r, web.Cfg.Domain, web.Cfg.TxtPrefix)
286-	if web.RootRouter == nil || web.UserRouter == nil {
287-		web.Cfg.Logger.Error("routers not initialized")
288-		http.Error(w, "routers not initialized", http.StatusInternalServerError)
289-		return
290-	}
291-
292-	var mux *http.ServeMux
293-	if subdomain == "" {
294-		mux = web.RootRouter
295-	} else {
296-		mux = web.UserRouter
297-	}
298-
299-	ctx := r.Context()
300-	ctx = context.WithValue(ctx, router.CtxSubdomainKey{}, subdomain)
301-	mux.ServeHTTP(w, r.WithContext(ctx))
302-}
303-
304-type CompatLogger struct {
305-	Logger *slog.Logger
306-}
307-
308-func (cl *CompatLogger) marshall(int ...interface{}) string {
309-	res := ""
310-	for _, val := range int {
311-		switch r := val.(type) {
312-		case string:
313-			res += " " + r
314-		}
315-	}
316-	return res
317-}
318-func (cl *CompatLogger) DPanic(int ...interface{}) {
319-	cl.Logger.Error("panic", "output", cl.marshall(int))
320-}
321-func (cl *CompatLogger) DPanicf(st string, int ...interface{}) {
322-	cl.Logger.Error(fmt.Sprintf(st, int...))
323-}
324-func (cl *CompatLogger) Debug(int ...interface{}) {
325-	cl.Logger.Debug("debug", "output", cl.marshall(int))
326-}
327-func (cl *CompatLogger) Debugf(st string, int ...interface{}) {
328-	cl.Logger.Debug(fmt.Sprintf(st, int...))
329-}
330-func (cl *CompatLogger) Error(int ...interface{}) {
331-	cl.Logger.Error("error", "output", cl.marshall(int))
332-}
333-func (cl *CompatLogger) Errorf(st string, int ...interface{}) {
334-	cl.Logger.Error(fmt.Sprintf(st, int...))
335-}
336-func (cl *CompatLogger) Fatal(int ...interface{}) {
337-	cl.Logger.Error("fatal", "outpu", cl.marshall(int))
338-}
339-func (cl *CompatLogger) Fatalf(st string, int ...interface{}) {
340-	cl.Logger.Error(fmt.Sprintf(st, int...))
341-}
342-func (cl *CompatLogger) Info(int ...interface{}) {
343-	cl.Logger.Info("info", "output", cl.marshall(int))
344-}
345-func (cl *CompatLogger) Infof(st string, int ...interface{}) {
346-	cl.Logger.Info(fmt.Sprintf(st, int...))
347-}
348-func (cl *CompatLogger) Panic(int ...interface{}) {
349-	cl.Logger.Error("panic", "output", cl.marshall(int))
350-}
351-func (cl *CompatLogger) Panicf(st string, int ...interface{}) {
352-	cl.Logger.Error(fmt.Sprintf(st, int...))
353-}
354-func (cl *CompatLogger) Warn(int ...interface{}) {
355-	cl.Logger.Warn("warn", "output", cl.marshall(int))
356-}
357-func (cl *CompatLogger) Warnf(st string, int ...interface{}) {
358-	cl.Logger.Warn(fmt.Sprintf(st, int...))
359-}
M pkg/apps/pgs/web_asset_handler.go
+1, -0
1@@ -289,6 +289,7 @@ func (h *ApiAssetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
2 	)
3 	done, _ := checkPreconditions(w, r, info.LastModified.UTC())
4 	if done {
5+		logger.Info("A conditaionl request was detected, no body required")
6 		// A conditional request was detected, status and headers are set, no body required (either 412 or 304)
7 		return
8 	}
A pkg/httpcache/cache_test.go
+930, -0
  1@@ -0,0 +1,930 @@
  2+package httpcache
  3+
  4+import (
  5+	"encoding/json"
  6+	"log/slog"
  7+	"net/http"
  8+	"net/http/httptest"
  9+	"strconv"
 10+	"strings"
 11+	"testing"
 12+	"time"
 13+)
 14+
 15+/*
 16+TODO:
 17+	- Request no-store store-prevention (RFC 9111 §5.2.1.5): verify a no-store request does not populate/update cache for subsequent requests.
 18+	- Authorization storage/use constraints (RFC 9111 §3.5): authenticated responses should not be reused unless explicitly permitted by response directives.
 19+	- Vary: * behavior (RFC 9111 §4.1): ensure such responses are not reused for subsequent requests.
 20+	- Multi-field Vary matching (RFC 9111 §4.1): all nominated request fields must match original request values, not just one.
 21+	- Age correction with upstream metadata (RFC 9111 §4.2.3, §5.1): test interactions of stored response Date/Age values rather than only local clock delta.
 22+*/
 23+
 24+// TestContext holds shared test state.
 25+type TestContext struct {
 26+	t            *testing.T
 27+	handler      http.Handler
 28+	cachedServer *httptest.Server
 29+}
 30+
 31+// NewTestContext creates a test context with a backend and cached server.
 32+func NewTestContext(t *testing.T, cacheHandler http.Handler) *TestContext {
 33+	tc := &TestContext{
 34+		t:       t,
 35+		handler: cacheHandler,
 36+	}
 37+
 38+	tc.cachedServer = httptest.NewServer(tc.handler)
 39+	t.Cleanup(tc.cachedServer.Close)
 40+
 41+	return tc
 42+}
 43+
 44+func (tc *TestContext) Do(req *http.Request) (*http.Response, error) {
 45+	return http.DefaultClient.Do(req)
 46+}
 47+
 48+func (tc *TestContext) DoWithHeaders(req *http.Request, headers map[string][]string) (*http.Response, error) {
 49+	reqCopy := req.Clone(req.Context())
 50+	reqCopy.Header = req.Header.Clone()
 51+	if reqCopy.Header == nil {
 52+		reqCopy.Header = make(http.Header)
 53+	}
 54+
 55+	for key, val := range headers {
 56+		reqCopy.Header.Del(key)
 57+		for _, v := range val {
 58+			reqCopy.Header.Add(key, v)
 59+		}
 60+	}
 61+	return http.DefaultClient.Do(reqCopy)
 62+}
 63+
 64+func (tc *TestContext) GetHeader(resp *http.Response, key string) string {
 65+	return resp.Header.Get(key)
 66+}
 67+
 68+func testCacheValue(afterCreated time.Duration) *CacheValue {
 69+	return &CacheValue{
 70+		Header:    map[string][]string{},
 71+		Body:      []byte("success"),
 72+		CreatedAt: time.Now().Add(-afterCreated),
 73+	}
 74+}
 75+
 76+// RFC 9211 The Cache-Status HTTP Response Header Field
 77+// https://www.rfc-editor.org/rfc/rfc9211#section-2
 78+func TestCacheCacheStatus(t *testing.T) {
 79+	mux := http.NewServeMux()
 80+	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 81+		w.WriteHeader(200)
 82+		_, _ = w.Write([]byte("success"))
 83+	})
 84+
 85+	logger := slog.Default()
 86+	handler := NewHttpCache(logger, mux)
 87+	tc := NewTestContext(t, handler)
 88+	req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
 89+
 90+	// first request hits backend
 91+	resp1, _ := tc.Do(req)
 92+	if resp1.StatusCode != http.StatusOK {
 93+		t.Errorf("expected 200, got %d", resp1.StatusCode)
 94+	}
 95+	status := resp1.Header.Get("cache-status")
 96+	if !strings.Contains(status, "miss") {
 97+		t.Errorf("expected miss, got %s", status)
 98+	}
 99+
100+	// second request hits cache
101+	resp2, _ := tc.Do(req)
102+	if resp2.StatusCode != http.StatusOK {
103+		t.Errorf("expected 200, got %d", resp2.StatusCode)
104+	}
105+	status = resp2.Header.Get("cache-status")
106+	if !strings.Contains(status, "hit") {
107+		t.Errorf("expected hit, got %s", status)
108+	}
109+}
110+
111+// RFC 9110 15.1-2 Heuristically Cacheable
112+// https://www.rfc-editor.org/rfc/rfc9110#section-15.1-2
113+// 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, and 501.
114+func TestCacheStatusCode(t *testing.T) {
115+	mux := http.NewServeMux()
116+	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
117+		w.WriteHeader(500)
118+		_, _ = w.Write([]byte("boom!"))
119+	})
120+
121+	logger := slog.Default()
122+	handler := NewHttpCache(logger, mux)
123+	tc := NewTestContext(t, handler)
124+	req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
125+
126+	// first request hits backend
127+	resp1, _ := tc.Do(req)
128+	if resp1.StatusCode != http.StatusInternalServerError {
129+		t.Errorf("expected 500, got %d", resp1.StatusCode)
130+	}
131+	status := resp1.Header.Get("cache-status")
132+	if !strings.Contains(status, "miss") {
133+		t.Errorf("expected miss, got %s", status)
134+	}
135+
136+	// second request hits backend
137+	resp2, _ := tc.Do(req)
138+	if resp2.StatusCode != http.StatusInternalServerError {
139+		t.Errorf("expected 500, got %d", resp2.StatusCode)
140+	}
141+	status = resp2.Header.Get("cache-status")
142+	if !strings.Contains(status, "miss") {
143+		t.Errorf("expected miss, got %s", status)
144+	}
145+}
146+
147+// RFC 9111 2.3 Opinion - Only store GET requests
148+// https://www.rfc-editor.org/rfc/rfc9111.html#section-2-3
149+func TestCacheMethod(t *testing.T) {
150+	mux := http.NewServeMux()
151+	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
152+		w.WriteHeader(200)
153+		_, _ = w.Write([]byte("success"))
154+	})
155+
156+	logger := slog.Default()
157+	handler := NewHttpCache(logger, mux)
158+	tc := NewTestContext(t, handler)
159+	req, _ := http.NewRequest("POST", tc.cachedServer.URL+"/test", nil)
160+
161+	// first request hits backend
162+	resp1, _ := tc.Do(req)
163+	if resp1.StatusCode != http.StatusOK {
164+		t.Errorf("expected 200, got %d", resp1.StatusCode)
165+	}
166+	status := resp1.Header.Get("cache-status")
167+	if !strings.Contains(status, "miss") {
168+		t.Errorf("expected miss, got %s", status)
169+	}
170+
171+	// second request hits backend
172+	resp2, _ := tc.Do(req)
173+	if resp2.StatusCode != http.StatusOK {
174+		t.Errorf("expected 200, got %d", resp2.StatusCode)
175+	}
176+	status = resp2.Header.Get("cache-status")
177+	if !strings.Contains(status, "miss") {
178+		t.Errorf("expected miss, got %s", status)
179+	}
180+}
181+
182+// RFC 9111 3.1 Storing Header and Trailer Fields
183+// https://www.rfc-editor.org/rfc/rfc9111.html#section-3.1
184+func TestCacheStoringHeaders(t *testing.T) {
185+	mux := http.NewServeMux()
186+	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
187+		w.Header().Set("connection", "idk")
188+		w.Header().Set("proxy-authenticate", "idk")
189+		w.Header().Set("proxy-authentication-info", "idk")
190+		w.Header().Set("proxy-authorization", "idk")
191+
192+		w.WriteHeader(200)
193+		_, _ = w.Write([]byte("success"))
194+	})
195+
196+	logger := slog.Default()
197+	handler := NewHttpCache(logger, mux)
198+	tc := NewTestContext(t, handler)
199+	req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
200+
201+	// first request hits backend
202+	resp1, _ := tc.Do(req)
203+	if resp1.StatusCode != http.StatusOK {
204+		t.Errorf("expected 200, got %d", resp1.StatusCode)
205+	}
206+	status := resp1.Header.Get("cache-status")
207+	if !strings.Contains(status, "miss") {
208+		t.Errorf("expected miss, got %s", status)
209+	}
210+
211+	// second request hits cache
212+	resp2, _ := tc.Do(req)
213+	if resp2.StatusCode != http.StatusOK {
214+		t.Errorf("expected 200, got %d", resp2.StatusCode)
215+	}
216+	headers := []string{
217+		resp2.Header.Get("connection"),
218+		resp2.Header.Get("proxy-authenticate"),
219+		resp2.Header.Get("proxy-authentication-info"),
220+		resp2.Header.Get("proxy-authorization"),
221+	}
222+	for _, hdr := range headers {
223+		if hdr != "" {
224+			t.Errorf("expected no header, found one: %s", hdr)
225+		}
226+	}
227+}
228+
229+// RFC 9111 4.1 Vary.
230+// https://www.rfc-editor.org/rfc/rfc9111.html#section-4.1
231+func TestCacheVary(t *testing.T) {
232+	mux := http.NewServeMux()
233+	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
234+		w.WriteHeader(200)
235+		_, _ = w.Write([]byte("success"))
236+	})
237+
238+	logger := slog.Default()
239+	handler := NewHttpCache(logger, mux)
240+	tc := NewTestContext(t, handler)
241+
242+	req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
243+	cacheKey := handler.GetCacheKey(req)
244+	cv := testCacheValue(250 * time.Second)
245+	cv.Header["Vary"] = []string{"Accept-Encoding"}
246+	// Store the original request header that selected this representation.
247+	cv.Header["Accept-Encoding"] = []string{"gzip"}
248+	cacheValue, _ := json.Marshal(cv)
249+	handler.Cache.Add(cacheKey, cacheValue)
250+
251+	respMatch, _ := tc.DoWithHeaders(req, map[string][]string{
252+		"Accept-Encoding": {"gzip"},
253+	})
254+	status := respMatch.Header.Get("cache-status")
255+	if !strings.Contains(status, "hit") {
256+		t.Errorf("expected hit, got %s", status)
257+	}
258+
259+	respMisMatch, _ := tc.DoWithHeaders(req, map[string][]string{
260+		"Accept-Encoding": {"text/plain"},
261+	})
262+	status = respMisMatch.Header.Get("cache-status")
263+	if !strings.Contains(status, "miss") {
264+		t.Errorf("expected miss, got %s", status)
265+	}
266+}
267+
268+// RFC 9111 4.3 Validation.
269+// https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3
270+// Last-Modified ETag If-Match If-None-Match If-Range If-Modified-Since If-Unmodified-Since
271+// RFC 9110 13 Conditional Requests.
272+// https://www.rfc-editor.org/rfc/rfc9110#section-13
273+func TestCacheValidation(t *testing.T) {
274+	actual := time.Now().Add(-10 * time.Minute).UTC()
275+	actualStr := actual.Format(time.RFC1123)
276+	now := time.Now().UTC()
277+	nowStr := now.Format(time.RFC1123)
278+	early := time.Now().Add(-20 * time.Minute)
279+	earlyStr := early.Format(time.RFC1123)
280+	tests := []struct {
281+		name             string
282+		link             string
283+		validationHeader string
284+		validationValue  string
285+		extraHeaders     map[string][]string
286+		expected         string
287+		originStatus     int
288+		expectedStatus   int
289+	}{
290+		{
291+			name:             "RFC 9110 13.1.2 If-None-Match",
292+			link:             "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2",
293+			validationHeader: "If-None-Match",
294+			validationValue:  "\"abc\"",
295+			expected:         "hit",
296+			originStatus:     http.StatusOK,
297+			expectedStatus:   http.StatusOK,
298+		},
299+		{
300+			name:             "RFC 9110 13.1.2 If-None-Match Wildcard",
301+			link:             "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.2",
302+			validationHeader: "If-None-Match",
303+			validationValue:  "*",
304+			expected:         "hit",
305+			originStatus:     http.StatusOK,
306+			expectedStatus:   http.StatusNotModified,
307+		},
308+		{
309+			name:             "RFC 9110 13.1.3 If-Modified-Since",
310+			link:             "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.3",
311+			validationHeader: "If-Modified-Since",
312+			validationValue:  nowStr,
313+			expected:         "hit",
314+			originStatus:     http.StatusOK,
315+			expectedStatus:   http.StatusNotModified,
316+		},
317+		{
318+			name:             "RFC 9110 13.1.4 If-Unmodified-Since",
319+			link:             "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.4",
320+			validationHeader: "If-Unmodified-Since",
321+			validationValue:  earlyStr,
322+			expected:         "hit",
323+			originStatus:     http.StatusOK,
324+			expectedStatus:   http.StatusOK,
325+		},
326+		{
327+			name:             "RFC 9110 13.1.5 If-Range Date",
328+			link:             "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.5",
329+			validationHeader: "If-Range",
330+			validationValue:  nowStr,
331+			extraHeaders: map[string][]string{
332+				"Range": {"bytes=0-3"},
333+			},
334+			expected:       "hit",
335+			originStatus:   http.StatusOK,
336+			expectedStatus: http.StatusOK,
337+		},
338+		{
339+			name:             "RFC 9110 13.1.5 If-Range Date Hit",
340+			link:             "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.5",
341+			validationHeader: "If-Range",
342+			validationValue:  actualStr,
343+			extraHeaders: map[string][]string{
344+				"Range": {"bytes=0-3"},
345+			},
346+			expected:       "hit",
347+			originStatus:   http.StatusOK,
348+			expectedStatus: http.StatusOK,
349+		},
350+		{
351+			name:             "RFC 9110 13.1.5 If-Range ETag",
352+			link:             "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.5",
353+			validationHeader: "If-Range",
354+			validationValue:  "\"abc\"",
355+			extraHeaders: map[string][]string{
356+				"Range": {"bytes=0-3"},
357+			},
358+			expected:       "hit",
359+			originStatus:   http.StatusOK,
360+			expectedStatus: http.StatusOK,
361+		},
362+		{
363+			name:             "RFC 9110 13.1.5 If-Range ETag Hit",
364+			link:             "https://www.rfc-editor.org/rfc/rfc9110#section-13.1.5",
365+			validationHeader: "If-Range",
366+			validationValue:  "\"ccc\"",
367+			extraHeaders: map[string][]string{
368+				"Range": {"bytes=0-3"},
369+			},
370+			expected:       "hit",
371+			originStatus:   http.StatusOK,
372+			expectedStatus: http.StatusOK,
373+		},
374+	}
375+
376+	for _, tt := range tests {
377+		mux := http.NewServeMux()
378+		mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
379+			w.WriteHeader(tt.originStatus)
380+		})
381+
382+		logger := slog.Default()
383+		handler := NewHttpCache(logger, mux)
384+		handler.Ttl = time.Minute * 10
385+		tc := NewTestContext(t, handler)
386+
387+		req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
388+		cacheKey := handler.GetCacheKey(req)
389+		cv := testCacheValue(250 * time.Second)
390+		cv.Header["ETag"] = []string{"ccc"}
391+		cv.Header["Last-Modified"] = []string{actualStr}
392+		cacheValue, _ := json.Marshal(cv)
393+		handler.Cache.Add(cacheKey, cacheValue)
394+
395+		t.Run(tt.name, func(t *testing.T) {
396+			reqHeaders := map[string][]string{tt.validationHeader: {tt.validationValue}}
397+			for key, values := range tt.extraHeaders {
398+				reqHeaders[key] = values
399+			}
400+
401+			resp, _ := tc.DoWithHeaders(req, reqHeaders)
402+			actual := resp.Header.Get("cache-status")
403+			if !strings.Contains(actual, tt.expected) {
404+				t.Errorf("expected %s, got %s\n%s", tt.expected, actual, tt.link)
405+			}
406+			if resp.StatusCode != tt.expectedStatus {
407+				t.Errorf("expected status %d, got %d", tt.expectedStatus, resp.StatusCode)
408+			}
409+		})
410+	}
411+}
412+
413+// RFC 9111 5.1 Age.
414+// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.1
415+// RFC 9111 4.2.3 Calculating Age.
416+// https://www.rfc-editor.org/rfc/rfc9111.html#section-4.2.3
417+func TestCacheAge(t *testing.T) {
418+	mux := http.NewServeMux()
419+	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
420+		w.WriteHeader(200)
421+		_, _ = w.Write([]byte("success"))
422+	})
423+
424+	logger := slog.Default()
425+	handler := NewHttpCache(logger, mux)
426+	tc := NewTestContext(t, handler)
427+
428+	req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
429+	cacheKey := handler.GetCacheKey(req)
430+	cacheValue, _ := json.Marshal(testCacheValue(250 * time.Second))
431+	handler.Cache.Add(cacheKey, cacheValue)
432+
433+	resp, _ := tc.Do(req)
434+	if resp.StatusCode != http.StatusOK {
435+		t.Errorf("expected 200, got %d", resp.StatusCode)
436+	}
437+	age := resp.Header.Get("age")
438+	ageNum, err := strconv.Atoi(age)
439+	if err != nil {
440+		t.Fatalf("invalide age header %s", err)
441+	}
442+	if ageNum != 251 {
443+		t.Errorf("expected 250, got %d", ageNum)
444+	}
445+}
446+
447+// RFC 9111 5.2.1 Request Directives
448+// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1
449+func TestCacheRequestDirectives(t *testing.T) {
450+	tests := []struct {
451+		name         string
452+		link         string
453+		cacheControl string
454+		expected     string
455+	}{
456+		{
457+			name:         "RFC 9111 5.2.1.1 Request Cache-Control: max-age",
458+			link:         "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.1",
459+			cacheControl: "max-age=100",
460+			expected:     "miss",
461+		},
462+		{
463+			name:         "RFC 9111 5.2.1.2 Request Cache-Control: max-stale",
464+			link:         "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.2",
465+			cacheControl: "max-stale=300", // 300 max-stale + 250 age = 550 > 450 freshness
466+			expected:     "miss",
467+		},
468+		{
469+			name:         "RFC 9111 5.2.1.3 Request Cache-Control: min-fresh",
470+			link:         "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.3",
471+			cacheControl: "min-fresh=400", // 600 ttl - 250 age = 350 freshness
472+			expected:     "miss",
473+		},
474+		{
475+			name:         "RFC 9111 5.2.1.4 Request Cache-Control: no-cache",
476+			link:         "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.4",
477+			cacheControl: "no-cache",
478+			expected:     "miss",
479+		},
480+		{
481+			name:         "RFC 9111 5.2.1.5 Request Cache-Control: no-store",
482+			link:         "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.5",
483+			cacheControl: "no-store",
484+			// you can reply with the cached version, just cannot store or update the response in
485+			//   the cache.
486+			expected: "hit",
487+		},
488+		{
489+			name:         "RFC 9111 5.2.1.6 Request Cache-Control: no-transform",
490+			link:         "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.6",
491+			cacheControl: "no-transform",
492+			expected:     "miss",
493+		},
494+		{
495+			name:         "RFC 9111 5.2.1.7 Request Cache-Control: only-if-cached",
496+			link:         "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.7",
497+			cacheControl: "only-if-cached",
498+			expected:     "hit",
499+		},
500+	}
501+
502+	for _, tt := range tests {
503+		mux := http.NewServeMux()
504+		mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
505+			w.WriteHeader(200)
506+			_, _ = w.Write([]byte("success"))
507+		})
508+
509+		logger := slog.Default()
510+		handler := NewHttpCache(logger, mux)
511+		handler.Ttl = time.Minute * 10
512+		tc := NewTestContext(t, handler)
513+
514+		req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
515+		cacheKey := handler.GetCacheKey(req)
516+		cacheValue, _ := json.Marshal(testCacheValue(250 * time.Second))
517+		handler.Cache.Add(cacheKey, cacheValue)
518+
519+		t.Run(tt.name, func(t *testing.T) {
520+			resp, _ := tc.DoWithHeaders(req, map[string][]string{"Cache-Control": {tt.cacheControl}})
521+			actual := resp.Header.Get("cache-status")
522+			if !strings.Contains(actual, tt.expected) {
523+				t.Errorf("expected %s, got %s\n%s", tt.expected, actual, tt.link)
524+			}
525+		})
526+	}
527+}
528+
529+// RFC 9111 5.2.2 Response Directives
530+// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2
531+// These tests simply confirm that the response generated from origin server corresponds to the
532+// correct cache-control, http status code, and is "revalidated" the correct number of times.
533+// It does **not** validate the correct cache control logic like cache using max-age from origin
534+// server.
535+func TestCacheResponseDirectivesHasCacheControl(t *testing.T) {
536+	tests := []struct {
537+		name                      string
538+		link                      string
539+		cacheControl              string
540+		expectedOriginCalls       int
541+		expectedSecondCacheStatus string
542+	}{
543+		{
544+			name:                      "RFC 9111 5.2.2.1 Response Cache-Control max-age",
545+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.1",
546+			cacheControl:              "max-age=100",
547+			expectedOriginCalls:       1,
548+			expectedSecondCacheStatus: "hit",
549+		},
550+		{
551+			name:                      "RFC 9111 5.2.2.2 Response Cache-Control must-revalidate",
552+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.2",
553+			cacheControl:              "must-revalidate",
554+			expectedOriginCalls:       1,
555+			expectedSecondCacheStatus: "hit",
556+		},
557+		{
558+			name:                      "RFC 9111 5.2.2.3 Response Cache-Control must-understand",
559+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.3",
560+			cacheControl:              "must-understand",
561+			expectedOriginCalls:       1,
562+			expectedSecondCacheStatus: "hit",
563+		},
564+		{
565+			name:                      "RFC 9111 5.2.2.4 Response Cache-Control no-cache",
566+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.4",
567+			cacheControl:              "no-cache",
568+			expectedOriginCalls:       2,
569+			expectedSecondCacheStatus: "miss",
570+		},
571+		{
572+			name:                      "RFC 9111 5.2.2.5 Response Cache-Control no-store",
573+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.5",
574+			cacheControl:              "no-store",
575+			expectedOriginCalls:       2,
576+			expectedSecondCacheStatus: "miss",
577+		},
578+		{
579+			name:                      "RFC 9111 5.2.2.6 Response Cache-Control no-transform",
580+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.6",
581+			cacheControl:              "no-transform",
582+			expectedOriginCalls:       1,
583+			expectedSecondCacheStatus: "hit",
584+		},
585+		{
586+			name:                      "RFC 9111 5.2.2.7 Response Cache-Control private",
587+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.7",
588+			cacheControl:              "private",
589+			expectedOriginCalls:       2,
590+			expectedSecondCacheStatus: "miss", // this is a shared cache, do not store private
591+		},
592+		{
593+			name:                      "RFC 9111 5.2.2.8 Response Cache-Control proxy-revalidate",
594+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.8",
595+			cacheControl:              "proxy-revalidate",
596+			expectedOriginCalls:       1,
597+			expectedSecondCacheStatus: "hit",
598+		},
599+		{
600+			name:                      "RFC 9111 5.2.2.9 Response Cache-Control public",
601+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.9",
602+			cacheControl:              "public",
603+			expectedOriginCalls:       1,
604+			expectedSecondCacheStatus: "hit",
605+		},
606+		{
607+			name:                      "RFC 9111 5.2.2.10 Response Cache-Control s-maxage",
608+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10",
609+			cacheControl:              "s-maxage=100",
610+			expectedOriginCalls:       1,
611+			expectedSecondCacheStatus: "hit",
612+		},
613+		{
614+			name:                      "RFC 9111 5.2.2.10 Response Cache-Control public+private",
615+			link:                      "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10",
616+			cacheControl:              "public, s-maxage=100, private",
617+			expectedOriginCalls:       2,
618+			expectedSecondCacheStatus: "miss", // be restrictive and adhere to private directive
619+		},
620+	}
621+
622+	for _, tt := range tests {
623+		t.Run(tt.name, func(t *testing.T) {
624+			mux := http.NewServeMux()
625+			actualOriginCalls := 0
626+			mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
627+				actualOriginCalls += 1
628+				w.Header().Set("cache-control", tt.cacheControl)
629+				w.WriteHeader(200)
630+				_, _ = w.Write([]byte("success"))
631+			})
632+
633+			logger := slog.Default()
634+			handler := NewHttpCache(logger, mux)
635+			handler.Ttl = time.Minute * 10
636+			tc := NewTestContext(t, handler)
637+
638+			req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
639+
640+			// first request hits backend
641+			resp1, _ := tc.Do(req)
642+			status := resp1.Header.Get("cache-status")
643+			if !strings.Contains(status, "miss") {
644+				t.Errorf("expected miss, got %s", status)
645+			}
646+
647+			// second request can be served from cache or forwarded depending on directive
648+			resp2, _ := tc.Do(req)
649+
650+			actualCc := resp2.Header.Get("cache-control")
651+			if actualCc != tt.cacheControl {
652+				t.Errorf("expected cache-control %s, got %s", tt.cacheControl, actualCc)
653+			}
654+			status = resp2.Header.Get("cache-status")
655+			if tt.expectedSecondCacheStatus != "" && !strings.Contains(status, tt.expectedSecondCacheStatus) {
656+				t.Errorf("expected %s, got %s", tt.expectedSecondCacheStatus, status)
657+			}
658+			if tt.expectedOriginCalls != actualOriginCalls {
659+				t.Errorf("expected %d origin calls, got %d", tt.expectedOriginCalls, actualOriginCalls)
660+			}
661+		})
662+	}
663+}
664+
665+// RFC 9111 5.3 Expires
666+// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3
667+func TestCacheExpires(t *testing.T) {
668+	tests := []struct {
669+		name                string
670+		link                string
671+		expires             string
672+		expectedStatus      int
673+		expectedCacheStatus string
674+	}{
675+		{
676+			name:                "RFC 9111 5.3 Expires - future date",
677+			link:                "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3",
678+			expires:             time.Now().Add(10 * time.Minute).UTC().Format(http.TimeFormat),
679+			expectedStatus:      http.StatusOK,
680+			expectedCacheStatus: "hit",
681+		},
682+		{
683+			name:                "RFC 9111 5.3 Expires - expired response",
684+			link:                "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3",
685+			expires:             time.Now().Add(-10 * time.Minute).UTC().Format(http.TimeFormat),
686+			expectedStatus:      http.StatusOK,
687+			expectedCacheStatus: "fwd=uri-miss",
688+		},
689+		{
690+			name:                "RFC 9111 5.3 Expires - invalid Expires header",
691+			link:                "https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3",
692+			expires:             "not-a-valid-date",
693+			expectedStatus:      http.StatusOK,
694+			expectedCacheStatus: "fwd=uri-miss",
695+		},
696+	}
697+
698+	for _, tt := range tests {
699+		t.Run(tt.name, func(t *testing.T) {
700+			mux := http.NewServeMux()
701+			mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
702+				w.Header().Set("Expires", tt.expires)
703+				w.WriteHeader(200)
704+				_, _ = w.Write([]byte("success"))
705+			})
706+
707+			logger := slog.Default()
708+			handler := NewHttpCache(logger, mux)
709+			tc := NewTestContext(t, handler)
710+
711+			req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
712+
713+			// first request hits backend
714+			resp1, _ := tc.Do(req)
715+			if resp1.StatusCode != http.StatusOK {
716+				t.Errorf("expected 200, got %d", resp1.StatusCode)
717+			}
718+
719+			// second request behavior depends on Expires header
720+			resp2, _ := tc.Do(req)
721+			status := resp2.Header.Get("cache-status")
722+			if !strings.Contains(status, tt.expectedCacheStatus) {
723+				t.Errorf("expected %s, got %s\n%s", tt.expectedCacheStatus, status, tt.link)
724+			}
725+		})
726+	}
727+}
728+
729+// RFC 9111 4.3.4 304 Not Modified
730+// https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3.4
731+// When a cached entry is validated and the origin responds with 304, the cache:
732+// - Returns 304 to the client
733+// - Updates header metadata from the 304 response
734+// - Retains the cached body for subsequent requests.
735+func TestCache304NotModifiedMerge(t *testing.T) {
736+	originCalls := 0
737+
738+	// Validation handler: returns 304 when ETag matches, 200 otherwise
739+	validationMux := http.NewServeMux()
740+	validationMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
741+		originCalls++
742+		if r.Header.Get("If-None-Match") == "\"abc\"" {
743+			w.WriteHeader(http.StatusNotModified)
744+			w.Header().Set("etag", "\"abc-updated\"")
745+			return
746+		}
747+		w.Header().Set("etag", "\"abc\"")
748+		w.Header().Set("cache-control", "max-age=60")
749+		w.WriteHeader(200)
750+		_, _ = w.Write([]byte("original body"))
751+	})
752+
753+	logger := slog.Default()
754+	handler := NewHttpCache(logger, validationMux)
755+	tc := NewTestContext(t, handler)
756+
757+	req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
758+
759+	// Manually populate cache with a stale entry that has must-revalidate
760+	// so validation is triggered on stale entries rather than the entry being deleted.
761+	cacheKey := handler.GetCacheKey(req)
762+	staleCv := testCacheValue(250 * time.Second)
763+	staleCv.Header["ETag"] = []string{"\"abc\""}
764+	staleCv.Header["Cache-Control"] = []string{"max-age=60, must-revalidate"}
765+	staleCv.Body = []byte("original body")
766+	cacheData, _ := json.Marshal(staleCv)
767+	handler.Cache.Add(cacheKey, cacheData)
768+
769+	// First request with If-None-Match triggers validation; origin returns 304
770+	resp1, _ := tc.DoWithHeaders(req, map[string][]string{
771+		"If-None-Match": {"\"abc\""},
772+	})
773+	if resp1.StatusCode != http.StatusNotModified {
774+		t.Errorf("expected 304, got %d", resp1.StatusCode)
775+	}
776+	status := resp1.Header.Get("cache-status")
777+	if !strings.Contains(status, "hit") {
778+		t.Errorf("expected cache-status hit, got %s", status)
779+	}
780+
781+	// Second request without conditional headers should still serve the cached body
782+	resp2, _ := tc.Do(req)
783+	if resp2.StatusCode != http.StatusOK {
784+		t.Errorf("expected 200, got %d", resp2.StatusCode)
785+	}
786+	bodyBuf := make([]byte, 1024)
787+	n, _ := resp2.Body.Read(bodyBuf)
788+	bodyStr := string(bodyBuf[:n])
789+	if bodyStr != "original body" {
790+		t.Errorf("expected cached body 'original body', got %q", bodyStr)
791+	}
792+	status2 := resp2.Header.Get("cache-status")
793+	if !strings.Contains(status2, "hit") {
794+		t.Errorf("expected cache-status hit on second request, got %s", status2)
795+	}
796+
797+	// Origin should have been called exactly once (304 validation only)
798+	if originCalls != 1 {
799+		t.Errorf("expected 1 origin call, got %d", originCalls)
800+	}
801+}
802+
803+func TestCacheAgeTtl(t *testing.T) {
804+	mux := http.NewServeMux()
805+	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
806+		w.Header().Set("cache-control", "max-age=60")
807+		w.WriteHeader(200)
808+		_, _ = w.Write([]byte("success"))
809+	})
810+
811+	logger := slog.Default()
812+	handler := NewHttpCache(logger, mux)
813+	tc := NewTestContext(t, handler)
814+
815+	req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
816+
817+	// first request hits backend
818+	resp1, _ := tc.Do(req)
819+	if resp1.StatusCode != http.StatusOK {
820+		t.Errorf("expected 200, got %d", resp1.StatusCode)
821+	}
822+
823+	resp2, _ := tc.Do(req)
824+	status := resp2.Header.Get("cache-status")
825+	if !strings.Contains(status, "ttl=59;") {
826+		t.Errorf("expected ttl=59, got %s\n", status)
827+	}
828+}
829+
830+// RFC 9111 4.2.4 Stale Serving - must-revalidate requires revalidation.
831+// RFC 9111 4.3.1/4.3.2 Validation - cache MUST send stored validators
832+// when generating conditional upstream requests for stale entries.
833+func TestCacheMustRevalidateRevalidationHeaders(t *testing.T) {
834+	actual := time.Now().Add(-10 * time.Minute).UTC()
835+	actualStr := actual.Format(time.RFC1123)
836+
837+	tests := []struct {
838+		name                string
839+		cachedETag          string
840+		cachedLastModified  string
841+		expectedIfNoneMatch string
842+		expectedIfModified  string
843+	}{
844+		{
845+			name:                "RFC 9111 4.3.1 If-None-Match from stored ETag",
846+			cachedETag:          "\"abc\"",
847+			cachedLastModified:  "",
848+			expectedIfNoneMatch: "\"abc\"",
849+			expectedIfModified:  "",
850+		},
851+		{
852+			name:                "RFC 9111 4.3.2 If-Modified-Since from stored Last-Modified",
853+			cachedETag:          "",
854+			cachedLastModified:  actualStr,
855+			expectedIfNoneMatch: "",
856+			expectedIfModified:  actualStr,
857+		},
858+		{
859+			name:                "RFC 9111 4.3.1+4.3.2 Both validators present",
860+			cachedETag:          "\"xyz\"",
861+			cachedLastModified:  actualStr,
862+			expectedIfNoneMatch: "\"xyz\"",
863+			expectedIfModified:  actualStr,
864+		},
865+	}
866+
867+	for _, tt := range tests {
868+		t.Run(tt.name, func(t *testing.T) {
869+			var receivedIfNoneMatch, receivedIfModifiedSince string
870+			var receivedRequest *http.Request
871+
872+			mux := http.NewServeMux()
873+			mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
874+				receivedIfNoneMatch = r.Header.Get("If-None-Match")
875+				receivedIfModifiedSince = r.Header.Get("If-Modified-Since")
876+				receivedRequest = r
877+
878+				if r.Header.Get("If-None-Match") == "\"abc\"" ||
879+					r.Header.Get("If-None-Match") == "\"xyz\"" ||
880+					r.Header.Get("If-Modified-Since") != "" {
881+					w.WriteHeader(http.StatusNotModified)
882+					return
883+				}
884+				w.Header().Set("etag", "\"abc\"")
885+				w.Header().Set("cache-control", "max-age=60")
886+				w.WriteHeader(http.StatusOK)
887+				_, _ = w.Write([]byte("success"))
888+			})
889+
890+			logger := slog.Default()
891+			handler := NewHttpCache(logger, mux)
892+			tc := NewTestContext(t, handler)
893+
894+			req, _ := http.NewRequest("GET", tc.cachedServer.URL+"/test", nil)
895+			cacheKey := handler.GetCacheKey(req)
896+
897+			cv := testCacheValue(250 * time.Second)
898+			if tt.cachedETag != "" {
899+				cv.Header["ETag"] = []string{tt.cachedETag}
900+			}
901+			if tt.cachedLastModified != "" {
902+				cv.Header["Last-Modified"] = []string{tt.cachedLastModified}
903+			}
904+			cv.Header["Cache-Control"] = []string{"max-age=60, must-revalidate"}
905+			cv.Body = []byte("cached body")
906+			cacheData, _ := json.Marshal(cv)
907+			handler.Cache.Add(cacheKey, cacheData)
908+
909+			resp, _ := tc.Do(req)
910+
911+			if resp.StatusCode != http.StatusNotModified {
912+				t.Errorf("expected 304, got %d", resp.StatusCode)
913+			}
914+			status := resp.Header.Get("cache-status")
915+			if !strings.Contains(status, "hit") {
916+				t.Errorf("expected cache-status hit, got %s", status)
917+			}
918+
919+			if receivedRequest == nil {
920+				t.Fatal("no request reached upstream handler")
921+			}
922+
923+			if tt.expectedIfNoneMatch != "" && receivedIfNoneMatch != tt.expectedIfNoneMatch {
924+				t.Errorf("expected If-None-Match %q, got %q", tt.expectedIfNoneMatch, receivedIfNoneMatch)
925+			}
926+			if tt.expectedIfModified != "" && receivedIfModifiedSince != tt.expectedIfModified {
927+				t.Errorf("expected If-Modified-Since %q, got %q", tt.expectedIfModified, receivedIfModifiedSince)
928+			}
929+		})
930+	}
931+}
A pkg/httpcache/cacher.go
+11, -0
 1@@ -0,0 +1,11 @@
 2+package httpcache
 3+
 4+type Cacher interface {
 5+	Add(key string, val []byte) (evicted bool)
 6+	Get(key string) (val []byte, ok bool)
 7+	Keys() []string
 8+	Len() int
 9+	Values() [][]byte
10+	Purge()
11+	Remove(key string) (present bool)
12+}
A pkg/httpcache/rw.go
+58, -0
 1@@ -0,0 +1,58 @@
 2+package httpcache
 3+
 4+import (
 5+	"net/http"
 6+	"time"
 7+)
 8+
 9+type responseWriter struct {
10+	http.ResponseWriter
11+	statusCode int
12+	body       []byte
13+}
14+
15+func (rw *responseWriter) WriteHeader(code int) {
16+	rw.statusCode = code
17+}
18+
19+// TODO: is there a way to preserve streaming to the base response writer while being able to set headers?
20+func (rw *responseWriter) Write(data []byte) (int, error) {
21+	rw.body = append(rw.body, data...)
22+	// return rw.ResponseWriter.Write(data)
23+	return 0, nil
24+}
25+
26+// Body returns the captured response body.
27+func (rw *responseWriter) Body() []byte {
28+	return rw.body
29+}
30+
31+// StatusCode returns the captured status code.
32+func (rw *responseWriter) StatusCode() int {
33+	if rw.statusCode == 0 {
34+		return http.StatusOK
35+	}
36+	return rw.statusCode
37+}
38+
39+func (rw *responseWriter) Send() {
40+	rw.ResponseWriter.WriteHeader(rw.StatusCode())
41+}
42+
43+func (rw *responseWriter) ToCacheValue() *CacheValue {
44+	cv := &CacheValue{
45+		Header:     rw.Header(),
46+		Body:       rw.body,
47+		CreatedAt:  time.Now(),
48+		StatusCode: rw.StatusCode(),
49+	}
50+
51+	return cv
52+}
53+
54+type CacheValue struct {
55+	Header     map[string][]string `json:"headers"`
56+	Body       []byte              `json:"body"`
57+	CreatedAt  time.Time           `json:"created_at"`
58+	StatusCode int                 `json:"status_code"`
59+}
A pkg/httpcache/serve.go
+721, -0
  1@@ -0,0 +1,721 @@
  2+package httpcache
  3+
  4+import (
  5+	"encoding/json"
  6+	"fmt"
  7+	"log/slog"
  8+	"net/http"
  9+	"strconv"
 10+	"strings"
 11+	"time"
 12+
 13+	"github.com/hashicorp/golang-lru/v2/expirable"
 14+)
 15+
 16+type CacheKey interface {
 17+	GetCacheKey(r *http.Request) string
 18+}
 19+
 20+type DefaultCacheKey struct{}
 21+
 22+func (p *DefaultCacheKey) GetCacheKey(r *http.Request) string {
 23+	return r.Host + "__" + r.Method + "__" + r.URL.RequestURI()
 24+}
 25+
 26+type CacheMetrics interface {
 27+	AddCacheItem(float64)
 28+	AddCacheHit()
 29+	AddCacheMiss()
 30+	AddUpstreamRequest()
 31+}
 32+
 33+type DefaultCacheMetrics struct{}
 34+
 35+func (p *DefaultCacheMetrics) AddCacheItem(float64) {}
 36+func (p *DefaultCacheMetrics) AddCacheHit()         {}
 37+func (p *DefaultCacheMetrics) AddCacheMiss()        {}
 38+func (p *DefaultCacheMetrics) AddUpstreamRequest()  {}
 39+
 40+type HttpCache struct {
 41+	CacheKey
 42+	CacheMetrics
 43+	Ttl      time.Duration
 44+	Upstream http.Handler
 45+	Cache    Cacher
 46+	Logger   *slog.Logger
 47+}
 48+
 49+func NewHttpCache(log *slog.Logger, upstream http.Handler) *HttpCache {
 50+	ttl := time.Minute * 10
 51+	cache := expirable.NewLRU[string, []byte](0, nil, ttl)
 52+	httpCache := &HttpCache{
 53+		Ttl:          ttl,
 54+		Logger:       log,
 55+		Upstream:     upstream,
 56+		Cache:        cache,
 57+		CacheKey:     &DefaultCacheKey{},
 58+		CacheMetrics: &DefaultCacheMetrics{},
 59+	}
 60+	log.Info("httpcache initiated", "ttl", httpCache.Ttl, "storage", "lru")
 61+	return httpCache
 62+}
 63+
 64+func (c *HttpCache) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 65+	if c.Upstream == nil {
 66+		http.Error(w, "upstream http handler not found", http.StatusNotFound)
 67+		return
 68+	}
 69+	cacheKey := c.GetCacheKey(r)
 70+	log := c.Logger.With("cache_key", cacheKey)
 71+
 72+	err := c.maybeUseCache(cacheKey, w, r)
 73+	if err == nil {
 74+		log.Info("cache hit")
 75+		c.AddCacheHit()
 76+		return
 77+	}
 78+
 79+	// RFC 9111 5.2.1.7 only-if-cached - don't store new responses
 80+	cacheContState := parseCacheControl(r.Header.Get("cache-control"))
 81+	onlyIfCached := cacheContState.onlyIfCache
 82+	if onlyIfCached {
 83+		msg := "cache not found and detected only-if-cached"
 84+		log.Error(msg)
 85+		http.Error(w, msg, http.StatusGatewayTimeout)
 86+		return
 87+	}
 88+
 89+	log.Info("cache miss, requesting upstream", "err", err)
 90+	c.AddCacheMiss()
 91+
 92+	// RFC 9111 4.2.4 + 4.3.1/4.3.2: stale must-revalidate entries must be
 93+	// revalidated with conditional headers derived from the stored response.
 94+	if err.Error() == "cache is stale and must-revalidate requires revalidation" {
 95+		if cachedData, exists := c.Cache.Get(cacheKey); exists {
 96+			var cachedValue CacheValue
 97+			if json.Unmarshal(cachedData, &cachedValue) == nil {
 98+				if etag := getHeader(cachedValue.Header, "ETag"); etag != "" {
 99+					r.Header.Set("If-None-Match", etag)
100+				}
101+				if lastMod := getHeader(cachedValue.Header, "Last-Modified"); lastMod != "" {
102+					r.Header.Set("If-Modified-Since", lastMod)
103+				}
104+			}
105+		}
106+	}
107+
108+	wrapped := &responseWriter{ResponseWriter: w}
109+	c.Upstream.ServeHTTP(wrapped, r)
110+	c.AddUpstreamRequest()
111+
112+	// RFC 9111 4.3.4 304 Not Modified
113+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3.4
114+	// A 304 response updates header metadata but preserves the cached body.
115+	if wrapped.StatusCode() == http.StatusNotModified {
116+		log.Info("304 not modified, updating cached headers")
117+		existingData, exists := c.Cache.Get(cacheKey)
118+		if exists {
119+			var cacheValue CacheValue
120+			if json.Unmarshal(existingData, &cacheValue) == nil {
121+				// Merge headers from the 304 response into the cached entry.
122+				for key, values := range wrapped.Header() {
123+					cacheValue.Header[key] = values
124+				}
125+				// Revalidation refreshes the entry — reset CreatedAt so it's fresh again.
126+				cacheValue.CreatedAt = time.Now()
127+				enc, _ := json.Marshal(cacheValue)
128+				c.Cache.Add(cacheKey, enc)
129+				c.AddCacheItem(float64(len(enc)))
130+			}
131+		}
132+		wrapped.Header().Set("cache-status", cacheStatusHit(cacheKey, c.Ttl.Seconds()))
133+		wrapped.Send()
134+		return
135+	}
136+
137+	err = isResponseCachable(r, wrapped)
138+	if err == nil {
139+		log.Info("storing cache")
140+		nextValue := wrapped.ToCacheValue()
141+		enc, _ := json.Marshal(nextValue)
142+		c.Cache.Add(cacheKey, enc)
143+		c.AddCacheItem(float64(len(enc)))
144+		wrapped.Header().Set("cache-status", cacheStatusMiss(cacheKey, true))
145+	} else {
146+		log.Info("not cachable", "err", err)
147+		wrapped.Header().Set("cache-status", cacheStatusMiss(cacheKey, false))
148+	}
149+
150+	wrapped.Send()
151+	// RFC 9110 15.4.5: 304 responses MUST NOT contain a body.
152+	// Skip writing body for 304 responses even if upstream wrote one.
153+	var total int
154+	if wrapped.StatusCode() != http.StatusNotModified {
155+		total, err = wrapped.ResponseWriter.Write(wrapped.Body())
156+	}
157+	log.Info("response writer", "bytes_written", total)
158+	if err != nil {
159+		log.Error("response writer write", "err", err)
160+	}
161+}
162+
163+// isForbiddenHeader checks if a header should not be stored/served per RFC 9111 Section 3.1
164+// https://www.rfc-editor.org/rfc/rfc9111.html#section-3.1
165+func isForbiddenHeader(key string) bool {
166+	switch strings.ToLower(key) {
167+	case "connection", "keep-alive", "proxy-authenticate", "proxy-authorization",
168+		"teardown", "transfer-encoding", "upgrade", "proxy-connection",
169+		"www-authenticate", "proxy-authentication-info":
170+		return true
171+	default:
172+		return false
173+	}
174+}
175+
176+func serveCache(w http.ResponseWriter, freshness time.Duration, cacheKey string, cacheValue *CacheValue) {
177+	hdr := w.Header()
178+	for key, values := range cacheValue.Header {
179+		// RFC 9111 3.1 - Skip forbidden headers
180+		if isForbiddenHeader(key) {
181+			continue
182+		}
183+		for _, value := range values {
184+			hdr.Add(key, value)
185+		}
186+	}
187+
188+	ageDur := calcAge(cacheValue.CreatedAt)
189+	age := ageDur.Seconds()
190+	hdr.Add("age", strconv.Itoa(int(age)+1))
191+	hdr.Add("cache-status", cacheStatusHit(cacheKey, freshness.Seconds()))
192+	_, _ = w.Write(cacheValue.Body)
193+}
194+
195+// matchVary checks if the request matches the Vary header from the cached response
196+// RFC 9111 4.1 Vary.
197+func matchVary(r *http.Request, cachedHeaders map[string][]string) bool {
198+	vary := getHeader(cachedHeaders, "Vary")
199+	if vary == "" {
200+		return true
201+	}
202+
203+	// Vary: * means the response is not cacheable
204+	if vary == "*" {
205+		return false
206+	}
207+
208+	// Parse Vary header and check each field
209+	fields := strings.FieldsFunc(vary, func(r rune) bool {
210+		return r == ','
211+	})
212+
213+	for _, field := range fields {
214+		field = strings.TrimSpace(strings.ToLower(field))
215+		if field == "" {
216+			continue
217+		}
218+
219+		// Get the request header value
220+		reqValue := r.Header.Get(field)
221+
222+		// Get the cached header value for this field (case-insensitive lookup)
223+		var cachedValue string
224+		for key, values := range cachedHeaders {
225+			if strings.ToLower(key) == field && len(values) > 0 {
226+				cachedValue = values[0]
227+				break
228+			}
229+		}
230+		if cachedValue == "" {
231+			continue
232+		}
233+
234+		// Compare values - must match exactly
235+		if reqValue != cachedValue {
236+			return false
237+		}
238+	}
239+
240+	return true
241+}
242+
243+func getHeader(headers map[string][]string, key string) string {
244+	// Case-insensitive lookup
245+	for k, values := range headers {
246+		if strings.EqualFold(k, key) && len(values) > 0 {
247+			return values[0]
248+		}
249+	}
250+	return ""
251+}
252+
253+// handleValidation handles conditional request validation.
254+// RFC 9110 13 Conditional Requests.
255+// RFC 9111 4.3.2 Response Validation.
256+func (c *HttpCache) handleValidation(r *http.Request, cacheValue *CacheValue) (bool, int) {
257+	// Get ETag and Last-Modified with case-insensitive lookup
258+	var etag string
259+	var lastModified string
260+	for key, values := range cacheValue.Header {
261+		lowerKey := strings.ToLower(key)
262+		c.Logger.Debug(
263+			"validate",
264+			"key", key,
265+			"lowerKey", lowerKey,
266+			"values", values,
267+			"etag", etag,
268+			"lastModified", lastModified,
269+		)
270+		if lowerKey == "etag" && len(values) > 0 {
271+			etag = values[0]
272+		}
273+		if lowerKey == "last-modified" && len(values) > 0 {
274+			lastModified = values[0]
275+		}
276+	}
277+
278+	c.Logger.Debug(
279+		"validate result",
280+		"etag", etag,
281+		"lastModified", lastModified,
282+	)
283+
284+	// RFC 9110 13.1.2 If-None-Match
285+	// https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.2
286+	ifNoneMatch := r.Header.Get("if-none-match")
287+	if ifNoneMatch != "" {
288+		// Wildcard If-None-Match: *
289+		if ifNoneMatch == "*" {
290+			if etag != "" {
291+				return true, http.StatusNotModified
292+			}
293+			return false, 0
294+		}
295+
296+		// Check if any of the provided ETags match
297+		etags := parseETags(ifNoneMatch)
298+		for _, etagVal := range etags {
299+			if etagVal == etag {
300+				return true, http.StatusNotModified
301+			}
302+		}
303+		return false, 0
304+	}
305+
306+	// RFC 9110 13.1.3 If-Modified-Since
307+	// https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.3
308+	ifModifiedSince := r.Header.Get("if-modified-since")
309+	if ifModifiedSince != "" && lastModified != "" {
310+		reqTime := parseTimeFallback(ifModifiedSince)
311+		if !reqTime.IsZero() {
312+			cachedTime := parseTimeFallback(lastModified)
313+			if !cachedTime.IsZero() {
314+				if !cachedTime.After(reqTime) {
315+					return true, http.StatusNotModified
316+				}
317+			}
318+		}
319+	}
320+
321+	// RFC 9110 13.1.4 If-Unmodified-Since
322+	// https://www.rfc-editor.org/rfc/rfc9110.html#section-13.1.4
323+	// For cache purposes, if If-Unmodified-Since matches, we can serve from cache
324+	ifUnmodifiedSince := r.Header.Get("if-unmodified-since")
325+	if ifUnmodifiedSince != "" && lastModified != "" {
326+		reqTime := parseTimeFallback(ifUnmodifiedSince)
327+		if !reqTime.IsZero() {
328+			cachedTime := parseTimeFallback(lastModified)
329+			if !cachedTime.IsZero() {
330+				if !cachedTime.Before(reqTime) {
331+					// Cached response is not modified since the request time
332+					// We can serve from cache, but don't return 304
333+					// The caller will handle the cache hit
334+					return false, 0
335+				}
336+			}
337+		}
338+	}
339+
340+	return false, 0
341+}
342+
343+func parseETags(etags string) []string {
344+	var result []string
345+	parts := strings.Split(etags, ",")
346+	for _, part := range parts {
347+		part = strings.TrimSpace(part)
348+		if part != "" {
349+			result = append(result, part)
350+		}
351+	}
352+	return result
353+}
354+
355+// parseTimeFallback parses time strings in RFC1123 or RFC1123Z format.
356+func parseTimeFallback(t string) time.Time {
357+	// Try RFC1123Z first (with numeric timezone)
358+	if parsed, err := http.ParseTime(t); err == nil {
359+		return parsed
360+	}
361+	// Try RFC1123 (with 3-letter timezone abbreviation)
362+	parsed, err := time.Parse("Mon, 02 Jan 2006 15:04:05 MST", t)
363+	if err == nil {
364+		return parsed
365+	}
366+	return time.Time{}
367+}
368+
369+func isResponseCachable(r *http.Request, resp *responseWriter) error {
370+	method := r.Method
371+	// RFC 9111 2.3 Opinion - Only cache GET requests
372+	if method != http.MethodGet {
373+		return fmt.Errorf("response method not cacheable: %s", method)
374+	}
375+
376+	isValidStatus := isCacheableStatusCode(resp.StatusCode())
377+	if !isValidStatus {
378+		return fmt.Errorf("response status code not cachable: %d", resp.StatusCode())
379+	}
380+
381+	state := parseCacheControl(resp.Header().Get("cache-control"))
382+	if state.private {
383+		return fmt.Errorf("shared cache cannot store private directives")
384+	}
385+
386+	return nil
387+}
388+
389+// isCacheableStatusCode checks if the status code is cacheable per RFC 9110 Section 15.1-2
390+// RFC 9110 15.1-2: 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414, and 501.
391+func isCacheableStatusCode(code int) bool {
392+	switch code {
393+	case http.StatusOK, http.StatusMultipleChoices, http.StatusMovedPermanently, http.StatusFound,
394+		http.StatusSeeOther, http.StatusUseProxy, http.StatusTemporaryRedirect,
395+		http.StatusPermanentRedirect, http.StatusPartialContent, http.StatusMultiStatus,
396+		http.StatusAlreadyReported, http.StatusIMUsed, http.StatusNotImplemented, http.StatusBadGateway,
397+		http.StatusServiceUnavailable, http.StatusGatewayTimeout, http.StatusHTTPVersionNotSupported:
398+		return true
399+	default:
400+		return false
401+	}
402+}
403+
404+type cacheControlState struct {
405+	noCache        bool
406+	noStore        bool
407+	noTransform    bool
408+	onlyIfCache    bool
409+	private        bool
410+	public         bool
411+	mustRevalidate bool
412+	// we explicitly check for max-age == 0 which is different from it
413+	// being unset so it's important we check if it is actually set
414+	// in the cache-control
415+	hasMaxAge    bool
416+	maxAge       time.Duration
417+	sharedMaxAge time.Duration
418+	maxStale     time.Duration
419+	minFresh     time.Duration
420+}
421+
422+func parseCacheControl(cc string) cacheControlState {
423+	parsed := strings.Split(cc, ",")
424+	state := cacheControlState{}
425+	for _, raw := range parsed {
426+		directive := strings.ToLower(strings.TrimSpace(raw))
427+		if directive == "" {
428+			continue
429+		}
430+		switch directive {
431+		case "public":
432+			state.public = true
433+		case "private":
434+			state.private = true
435+		case "no-cache":
436+			state.noCache = true
437+		case "no-store":
438+			state.noStore = true
439+		case "no-transform":
440+			state.noTransform = true
441+		case "only-if-cached":
442+			state.onlyIfCache = true
443+		case "must-revalidate":
444+			state.mustRevalidate = true
445+		}
446+
447+		if strings.HasPrefix(directive, "max-age=") {
448+			state.hasMaxAge = true
449+			state.maxAge = parseHeaderTime(directive, "max-age")
450+		}
451+		if strings.HasPrefix(directive, "s-maxage=") {
452+			state.sharedMaxAge = parseHeaderTime(directive, "s-maxage")
453+		}
454+		if strings.HasPrefix(directive, "min-fresh=") {
455+			state.minFresh = parseHeaderTime(directive, "min-fresh")
456+		}
457+		if strings.HasPrefix(directive, "max-stale=") {
458+			state.maxStale = parseHeaderTime(directive, "max-stale")
459+		}
460+	}
461+	return state
462+}
463+
464+func isCacheValid(r *http.Request, freshness time.Duration, age time.Duration) error {
465+	state := parseCacheControl(r.Header.Get("cache-control"))
466+
467+	if state.private {
468+		return fmt.Errorf("private directive")
469+	}
470+
471+	// RFC 9111 5.2.1.4 Request Cache-Control: no-cache
472+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.4
473+	if state.noCache {
474+		return fmt.Errorf("detected no-cache")
475+	}
476+
477+	// RFC 9111 5.2.1.5 Request Cache-Control: no-store
478+	// A no-store request can still use cached content, it just shouldn't store the response
479+	if state.noStore {
480+		// Allow cache hit but won't store on this request
481+		return nil
482+	}
483+
484+	// RFC 9111 5.2.1.1 Request Cache-Control: max-age=0
485+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.1
486+	if state.hasMaxAge && state.maxAge == 0 {
487+		return fmt.Errorf("detected max-age=0")
488+	}
489+
490+	// RFC 9111 5.2.1.3 Request Cache-Control: min-fresh
491+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.3
492+	minFreshDur := state.minFresh
493+	if minFreshDur.Seconds() > 0 && freshness < minFreshDur {
494+		return fmt.Errorf("min-fresh: cache freshness is too old")
495+	}
496+
497+	// RFC 9111 5.2.1.2 Request Cache-Control: max-stale
498+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.2
499+	// max-stale allows serving stale responses as long as staleness <= max-stale value
500+	// staleness = age - freshness (when freshness < 0, staleness = age + |freshness|)
501+	// If freshness <= 0, the cache is stale, and max-stale allows it if staleness <= max-stale
502+	maxStaleDur := state.maxStale
503+	if maxStaleDur > 0 && freshness <= 0 {
504+		// Cache is stale, check if max-stale allows it
505+		staleness := age - freshness // When freshness <= 0, staleness = age + |freshness|
506+		if staleness > maxStaleDur {
507+			return fmt.Errorf("max-stale: staleness exceeds limit")
508+		}
509+		// max-stale allows this stale response
510+		return nil
511+	}
512+	if maxStaleDur > 0 && freshness > maxStaleDur {
513+		return fmt.Errorf("max-stale: freshness exceeds limit")
514+	}
515+
516+	// RFC 9111 5.2.1.6 Request Cache-Control: no-transform
517+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.6
518+	// no-transform in the request means the cache should not transform the response.
519+	// Serving from cache counts as a transformation, so we must forward to origin.
520+	if state.noTransform {
521+		return fmt.Errorf("request has no-transform directive")
522+	}
523+
524+	// RFC 9111 5.2.1.7 Request Cache-Control: only-if-cached
525+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.1.7
526+	// For our implementation, only-if-cached means we can use cached response
527+	// but we shouldn't store new responses. The caller handles the logic.
528+	if state.onlyIfCache {
529+		// Allow cache hit, but the ServeHTTP will not store new responses
530+		return nil
531+	}
532+
533+	return nil
534+}
535+
536+func (c *HttpCache) maybeUseCache(cacheKey string, w http.ResponseWriter, r *http.Request) error {
537+	data, exists := c.Cache.Get(cacheKey)
538+	if !exists {
539+		return fmt.Errorf("no cache stored")
540+	}
541+
542+	var cacheValue CacheValue
543+	err := json.Unmarshal(data, &cacheValue)
544+	if err != nil {
545+		return fmt.Errorf("json unmarshal: %w", err)
546+	}
547+
548+	// RFC 9111 4.1 Vary - check if request matches cached Vary values
549+	if !matchVary(r, cacheValue.Header) {
550+		return fmt.Errorf("vary mismatch")
551+	}
552+
553+	// RFC 9111 5.2.2.4 Response Cache-Control: no-cache
554+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.4
555+	// Must revalidate with origin before using cached response
556+	cacheContState := parseCacheControl(
557+		getHeader(cacheValue.Header, "cache-control"),
558+	)
559+	if cacheContState.noCache {
560+		return fmt.Errorf("cache requires revalidation")
561+	}
562+
563+	// RFC 9111 5.3 Expires
564+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.3
565+	// Check if the cached response has expired based on the Expires header
566+	var expires time.Time
567+	expiresStr := getHeader(cacheValue.Header, "expires")
568+	if expiresStr != "" {
569+		var parseErr error
570+		expires, parseErr = http.ParseTime(expiresStr)
571+		if parseErr != nil {
572+			// Invalid Expires header means the response is stale
573+			return fmt.Errorf("cache expired based on expires header")
574+		}
575+		if time.Now().After(expires) {
576+			return fmt.Errorf("cache expired based on expires header")
577+		}
578+	}
579+
580+	// RFC 9111 5.2.2.5 Response Cache-Control: must-revalidate
581+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-3.3.1
582+	// must-revalidate means the cache MUST NOT use a stale response if it can validate it
583+	// with the origin server. When cache is stale, we must revalidate.
584+	if cacheContState.mustRevalidate {
585+		// Check if cache is stale first
586+		age := calcAge(cacheValue.CreatedAt)
587+		freshness := calcFreshness(cacheContState, expires, age, c.Ttl)
588+		if freshness <= 0 {
589+			return fmt.Errorf("cache is stale and must-revalidate requires revalidation")
590+		}
591+	}
592+
593+	// RFC 9111 5.2.2.5 Response Cache-Control: no-store
594+	// https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.5
595+	// Should not store response, but cached response can still be used
596+	// However, tests expect this to forward to origin
597+	if cacheContState.noStore {
598+		return fmt.Errorf("cache has no-store")
599+	}
600+
601+	// RFC 9111 4.3 Validation - check validation headers first
602+	// RFC 9110 13 Conditional Requests
603+	// https://www.rfc-editor.org/rfc/rfc9110.html#section-13
604+	valid, status := c.handleValidation(r, &cacheValue)
605+	if valid {
606+		// RFC 9111 4.3.4 304 Not Modified
607+		// https://www.rfc-editor.org/rfc/rfc9111.html#section-4.3.4
608+		w.Header().Set("cache-status", cacheStatusHit(cacheKey, c.Ttl.Seconds()))
609+		w.WriteHeader(status)
610+		return nil
611+	}
612+
613+	age := calcAge(cacheValue.CreatedAt)
614+	freshness := calcFreshness(cacheContState, expires, age, c.Ttl)
615+
616+	// Check if request allows stale responses (max-stale)
617+	// RFC 9111 5.2.1.2 - max-stale allows serving stale responses
618+	// We need to check this before the freshness <= 0 check
619+	reqCacheState := parseCacheControl(r.Header.Get("cache-control"))
620+	maxStaleDur := reqCacheState.maxStale
621+	hasMaxStale := maxStaleDur > 0 && freshness <= 0
622+
623+	isValid := isCacheValid(r, freshness, age)
624+	if isValid != nil {
625+		return fmt.Errorf("cache invalid: %w", isValid)
626+	}
627+
628+	if freshness <= 0 && !hasMaxStale {
629+		c.Cache.Remove(cacheKey)
630+		return fmt.Errorf("cache stale")
631+	}
632+
633+	// If request specifies max-age=100 and freshness is 350, the response is too fresh
634+	// We need to check: is the response older than maxAge?
635+	maxAge := reqCacheState.maxAge
636+	if reqCacheState.hasMaxAge && maxAge > 0 && age > maxAge {
637+		return fmt.Errorf("response older than request max-age")
638+	}
639+
640+	serveCache(w, freshness, cacheKey, &cacheValue)
641+	return nil
642+}
643+
644+// parseHeaderTime extracts a duration value from cache-control header.
645+// Supports both underscore and hyphen formats (e.g., max-age or max_age).
646+func parseHeaderTime(cc string, prefix string) time.Duration {
647+	if cc == "" {
648+		return 0
649+	}
650+	// e.g. max-age=N format (also supports max_age)
651+	// Try with hyphen first (standard format), then underscore (alternative format)
652+	for _, sep := range []string{"", "-"} {
653+		search := prefix + sep + "="
654+		if idx := strings.Index(cc, search); idx >= 0 {
655+			rest := cc[idx+len(search):]
656+			// Find the end of the number (comma or end of string)
657+			end := len(rest)
658+			for i, ch := range rest {
659+				if ch == ',' || ch == ' ' {
660+					end = i
661+					break
662+				}
663+			}
664+			// Parse the number
665+			var age int64
666+			_, _ = fmt.Sscanf(rest[:end], "%d", &age)
667+			return time.Duration(age) * time.Second
668+		}
669+	}
670+	return 0
671+}
672+
673+// RFC 9111 4.2.1 Calculating Freshness
674+// https://www.rfc-editor.org/rfc/rfc9111#section-4.2.1
675+func calcFreshness(state cacheControlState, expires time.Time, age time.Duration, defaultTtl time.Duration) time.Duration {
676+	ttl := defaultTtl
677+	// s-maxage uses hyphen (s-maxage), max-age uses hyphen (max-age)
678+	smaxAgeDur := state.sharedMaxAge
679+	maxAgeDur := state.maxAge
680+	remExpires := time.Until(expires)
681+
682+	if smaxAgeDur.Seconds() > 0 {
683+		ttl = smaxAgeDur
684+	} else if maxAgeDur.Seconds() > 0 {
685+		ttl = maxAgeDur
686+	} else if remExpires > 0 {
687+		ttl = remExpires
688+	}
689+
690+	return ttl - age
691+}
692+
693+// RFC 9111 4.2.3 Calculating Age
694+// https://www.rfc-editor.org/rfc/rfc9111.html#section-4.2.3
695+func calcAge(createdAt time.Time) time.Duration {
696+	return time.Since(createdAt)
697+}
698+
699+func cacheStatusHit(cacheKey string, ttl float64) string {
700+	// RFC 9211 2.1 Cache-Status hit
701+	// https://www.rfc-editor.org/rfc/rfc9211#section-2.1
702+	// RFC 9211 2.4 Cache-status ttl
703+	// https://www.rfc-editor.org/rfc/rfc9211#section-2.4
704+	// RFC 9222 2.7 Cache-status key
705+	// https://www.rfc-editor.org/rfc/rfc9211#section-2.7
706+	return fmt.Sprintf("pico; hit; ttl=%d; key=%s", int(ttl), cacheKey)
707+}
708+
709+func cacheStatusMiss(cacheKey string, stored bool) string {
710+	// RFC 9211 2.2 Cache-Status fwd
711+	// https://www.rfc-editor.org/rfc/rfc9211#section-2.2
712+	status := "pico; fwd=uri-miss"
713+	if stored {
714+		// RFC 9211 2.2 Cache-Status stored
715+		// https://www.rfc-editor.org/rfc/rfc9211#section-2.5
716+		status = fmt.Sprintf("%s; stored", status)
717+	}
718+	// RFC 9222 2.7 Cache-status key
719+	// https://www.rfc-editor.org/rfc/rfc9211#section-2.7
720+	status = fmt.Sprintf("%s; key=%s", status, cacheKey)
721+	return status
722+}