Commit 588159a

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