From 91d392d9ae4519665948734629333e63ddea9526 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 16 Dec 2025 19:03:08 +0100 Subject: [PATCH 01/49] chore: update go-libp2p 0.46 (#11105) * fix: update go-libp2p to v0.46.0 - reduced WebRTC log noise (go-libp2p#3426) - fixed mDNS discovery on Windows/macOS (go-libp2p#3434) - includes quic-go v0.57.1 (v0.56.0 + v0.57.0) * fix(example): kubo-as-a-library test timeout - use custom ports (4010/4011) to avoid conflicts with default 4001 - add 2-minute context timeout to fail fast - get peer addresses dynamically instead of hardcoding wrong port - wait for peer connection synchronously instead of fire-and-forget - update comments to reference autoconf.FallbackBootstrapPeers * chore: update p2p-forge to v0.7.0 * fix(test): wait for DHT readiness in GetClosestPeers test the test was failing for `routing_type=auto` because it only waited for swarm connections but not for the DHT routing table to be populated. added a separate probe loop that waits for GetClosestPeers to succeed before running the actual test assertions. --- docs/changelogs/v0.40.md | 11 +- docs/examples/kubo-as-a-library/go.mod | 11 +- docs/examples/kubo-as-a-library/go.sum | 124 ++----------- docs/examples/kubo-as-a-library/main.go | 66 ++++--- go.mod | 11 +- go.sum | 115 ++---------- .../delegated_routing_v1_http_server_test.go | 24 ++- test/dependencies/go.mod | 9 +- test/dependencies/go.sum | 168 ++---------------- 9 files changed, 110 insertions(+), 429 deletions(-) diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index 29780937f..1b963611b 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -11,7 +11,8 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [Overview](#overview) - [๐Ÿ”ฆ Highlights](#-highlights) - [Routing V1 HTTP API now exposed by default](#routing-v1-http-api-now-exposed-by-default) - - [Track total size when adding pins](#track-total-size-when-adding-pins] + - [Track total size when adding pins](#track-total-size-when-adding-pins) + - [๐Ÿ“ฆ๏ธ Dependency updates](#-dependency-updates) - [๐Ÿ“ Changelog](#-changelog) - [๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Contributors](#-contributors) @@ -32,6 +33,14 @@ Example output: Fetched/Processed 336 nodes (83 MB) ``` +#### ๐Ÿ“ฆ๏ธ Dependency updates + +- update `go-libp2p` to [v0.46.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.46.0) + - Reduced WebRTC log noise by using debug level for pion errors ([go-libp2p#3426](https://github.com/libp2p/go-libp2p/pull/3426)). + - Fixed mDNS discovery on Windows and macOS by filtering addresses to reduce packet size ([go-libp2p#3434](https://github.com/libp2p/go-libp2p/pull/3434)). +- update `quic-go` to [v0.57.1](https://github.com/quic-go/quic-go/releases/tag/v0.57.1) (incl. [v0.56.0](https://github.com/quic-go/quic-go/releases/tag/v0.56.0) + [v0.57.0](https://github.com/quic-go/quic-go/releases/tag/v0.57.0)) +- update `p2p-forge` to [v0.7.0](https://github.com/ipshipyard/p2p-forge/releases/tag/v0.7.0) + ### ๐Ÿ“ Changelog ### ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Contributors diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 53fd29136..54c6efde7 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -9,7 +9,7 @@ replace github.com/ipfs/kubo => ./../../.. require ( github.com/ipfs/boxo v0.35.3-0.20251202220026-0842ad274a0c github.com/ipfs/kubo v0.0.0-00010101000000-000000000000 - github.com/libp2p/go-libp2p v0.45.0 + github.com/libp2p/go-libp2p v0.46.0 github.com/multiformats/go-multiaddr v0.16.1 ) @@ -49,8 +49,7 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/filecoin-project/go-clock v0.1.0 // indirect github.com/flynn/noise v1.1.0 // indirect - github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/gammazero/chanqueue v1.1.1 // indirect github.com/gammazero/deque v1.2.0 // indirect @@ -101,7 +100,7 @@ require ( github.com/ipld/go-car/v2 v2.16.0 // indirect github.com/ipld/go-codec-dagpb v1.7.0 // indirect github.com/ipld/go-ipld-prime v0.21.0 // indirect - github.com/ipshipyard/p2p-forge v0.6.1 // indirect + github.com/ipshipyard/p2p-forge v0.7.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/klauspost/compress v1.18.0 // indirect @@ -176,8 +175,8 @@ require ( github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.17.0 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.55.0 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.57.1 // indirect github.com/quic-go/webtransport-go v0.9.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 7de462937..46b5cb555 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -1,9 +1,7 @@ bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc h1:utDghgcjE8u+EBjHOgYT+dJPcnDF05KqWMBcjuJy510= bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -18,12 +16,7 @@ cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2k cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= @@ -45,16 +38,13 @@ github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vS github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 h1:iW0a5ljuFxkLGPNem5Ui+KBjFJzKg4Fv2fnxe4dvzpM= github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5/go.mod h1:Y2QMoi1vgtOIfc+6DhrMOGkLoGzqSV2rKp4Sm+opsyA= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= @@ -64,7 +54,6 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU= github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= @@ -107,7 +96,6 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf h1:dwGgBWn84wUS1pVikGiruW+x5XM4amhjaZO20vCjay4= @@ -148,19 +136,16 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9gd6MPfXbKVU= github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gammazero/chanqueue v1.1.1 h1:n9Y+zbBxw2f7uUE9wpgs0rOSkP/I/yhDLiNuhyVjojQ= @@ -171,10 +156,7 @@ github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9 h1:r5GgOLGbza2wVHRzK7aAj6lWZjfbAwiu/RDCVOKjRyM= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -188,7 +170,6 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= @@ -202,7 +183,6 @@ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -239,8 +219,6 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -253,8 +231,6 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -264,8 +240,6 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/guillaumemichel/reservedpool v0.3.0 h1:eqqO/QvTllLBrit7LVtVJBqw4cD0WdV9ajUe7WNTajw= @@ -368,8 +342,8 @@ github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20250821084354-a425e60cd714 h1:cqNk8PEwHnK0vqWln+U/YZhQc9h2NB3KjUjDPZo5Q2s= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20250821084354-a425e60cd714/go.mod h1:ZEUdra3CoqRVRYgAX/jAJO9aZGz6SKtKEG628fHHktY= -github.com/ipshipyard/p2p-forge v0.6.1 h1:987/hUC1YxI56CcMX6iTB+9BLjFV0d2SJnig9Z1pf8A= -github.com/ipshipyard/p2p-forge v0.6.1/go.mod h1:pj8Zcs+ex5OMq5a1bFLHqW0oL3qYO0v5eGLZmit0l7U= +github.com/ipshipyard/p2p-forge v0.7.0 h1:PQayexxZC1FR2Vx0XOSbmZ6wDPliidS48I+xXWuF+YU= +github.com/ipshipyard/p2p-forge v0.7.0/go.mod h1:i2wg0p7WmHGyo5vYaK9COZBp8BN5Drncfu3WoQNZlQY= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= @@ -377,11 +351,9 @@ github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABo github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= @@ -406,7 +378,6 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -424,8 +395,8 @@ github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZ github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784= github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo= -github.com/libp2p/go-libp2p v0.45.0 h1:Pdhr2HsFXaYjtfiNcBP4CcRUONvbMFdH3puM9vV4Tiw= -github.com/libp2p/go-libp2p v0.45.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc= +github.com/libp2p/go-libp2p v0.46.0 h1:0T2yvIKpZ3DVYCuPOFxPD1layhRU486pj9rSlGWYnDM= +github.com/libp2p/go-libp2p v0.46.0/go.mod h1:TbIDnpDjBLa7isdgYpbxozIVPBTmM/7qKOJP4SFySrQ= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= @@ -461,10 +432,8 @@ github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfY github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q= github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxfg= github.com/marcopolo/simnet v0.0.1/go.mod h1:WDaQkgLAjqDUEBAOXz22+1j6wXKfGlC5sD5XWt3ddOs= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= @@ -475,11 +444,9 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= @@ -500,8 +467,6 @@ github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dz github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -546,8 +511,6 @@ github.com/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOo github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -568,7 +531,6 @@ github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= @@ -632,23 +594,19 @@ github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4 github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/probe-lab/go-libdht v0.4.0 h1:LAqHuko/owRW6+0cs5wmJXbHzg09EUMJEh5DI37yXqo= github.com/probe-lab/go-libdht v0.4.0/go.mod h1:hamw22kI6YkPQFGy5P6BrWWDrgE9ety5Si8iWAyuDvc= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= -github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= +github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -658,30 +616,7 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/slok/go-http-metrics v0.13.0 h1:lQDyJJx9wKhmbliyUsZ2l6peGnXRHjsjoqPt5VYzcP8= github.com/slok/go-http-metrics v0.13.0/go.mod h1:HIr7t/HbN2sJaunvnt9wKP9xoBBVZFo1/KiHU3b0w+4= @@ -692,8 +627,6 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= @@ -727,15 +660,12 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb h1:Ywfo8sUltxogBpFuMOFRrrSifO788kAFxmvVw31PtQQ= github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE= github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s= github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y= @@ -769,7 +699,6 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= @@ -819,17 +748,13 @@ go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -856,7 +781,6 @@ golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -881,13 +805,10 @@ golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -916,13 +837,10 @@ golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -938,13 +856,11 @@ golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1006,15 +922,12 @@ golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1057,9 +970,6 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhS golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1069,17 +979,11 @@ google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1097,9 +1001,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1: google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1131,7 +1032,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -1145,8 +1045,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1159,5 +1057,3 @@ pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/docs/examples/kubo-as-a-library/main.go b/docs/examples/kubo-as-a-library/main.go index 765e83c6d..ffa86c7f0 100644 --- a/docs/examples/kubo-as-a-library/main.go +++ b/docs/examples/kubo-as-a-library/main.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strings" "sync" + "time" "github.com/ipfs/boxo/files" "github.com/ipfs/boxo/path" @@ -46,7 +47,7 @@ func setupPlugins(externalPluginsPath string) error { return nil } -func createTempRepo() (string, error) { +func createTempRepo(swarmPort int) (string, error) { repoPath, err := os.MkdirTemp("", "ipfs-shell") if err != nil { return "", fmt.Errorf("failed to get temp dir: %s", err) @@ -58,6 +59,15 @@ func createTempRepo() (string, error) { return "", err } + // Configure custom ports to avoid conflicts with other IPFS instances. + // This demonstrates how to customize the node's network addresses. + cfg.Addresses.Swarm = []string{ + fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", swarmPort), + fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic-v1", swarmPort), + fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic-v1/webtransport", swarmPort), + fmt.Sprintf("/ip4/0.0.0.0/udp/%d/webrtc-direct", swarmPort), + } + // When creating the repository, you can define custom settings on the repository, such as enabling experimental // features (See experimental-features.md) or customizing the gateway endpoint. // To do such things, you should modify the variable `cfg`. For example: @@ -108,7 +118,8 @@ func createNode(ctx context.Context, repoPath string) (*core.IpfsNode, error) { var loadPluginsOnce sync.Once // Spawns a node to be used just for this run (i.e. creates a tmp repo). -func spawnEphemeral(ctx context.Context) (icore.CoreAPI, *core.IpfsNode, error) { +// The swarmPort parameter specifies the port for libp2p swarm listeners. +func spawnEphemeral(ctx context.Context, swarmPort int) (icore.CoreAPI, *core.IpfsNode, error) { var onceErr error loadPluginsOnce.Do(func() { onceErr = setupPlugins("") @@ -118,7 +129,7 @@ func spawnEphemeral(ctx context.Context) (icore.CoreAPI, *core.IpfsNode, error) } // Create a Temporary Repo - repoPath, err := createTempRepo() + repoPath, err := createTempRepo(swarmPort) if err != nil { return nil, nil, fmt.Errorf("failed to create temp repo: %s", err) } @@ -192,11 +203,12 @@ func main() { fmt.Println("-- Getting an IPFS node running -- ") - ctx, cancel := context.WithCancel(context.Background()) + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) defer cancel() // Spawn a local peer using a temporary path, for testing purposes - ipfsA, nodeA, err := spawnEphemeral(ctx) + // Using port 4010 to avoid conflict with default IPFS port 4001 + ipfsA, nodeA, err := spawnEphemeral(ctx, 4010) if err != nil { panic(fmt.Errorf("failed to spawn peer node: %s", err)) } @@ -210,8 +222,9 @@ func main() { fmt.Printf("Added file to peer with CID %s\n", peerCidFile.String()) // Spawn a node using a temporary path, creating a temporary repo for the run + // Using port 4011 (different from nodeA's port 4010) fmt.Println("Spawning Kubo node on a temporary repo") - ipfsB, _, err := spawnEphemeral(ctx) + ipfsB, _, err := spawnEphemeral(ctx, 4011) if err != nil { panic(fmt.Errorf("failed to spawn ephemeral node: %s", err)) } @@ -288,37 +301,34 @@ func main() { fmt.Println("\n-- Going to connect to a few nodes in the Network as bootstrappers --") - peerMa := fmt.Sprintf("/ip4/127.0.0.1/udp/4010/p2p/%s", nodeA.Identity.String()) + // Get nodeA's address so we can fetch the file we added to it + peerAddrs, err := ipfsA.Swarm().LocalAddrs(ctx) + if err != nil { + panic(fmt.Errorf("could not get peer addresses: %s", err)) + } + peerMa := peerAddrs[0].String() + "/p2p/" + nodeA.Identity.String() bootstrapNodes := []string{ - // IPFS Bootstrapper nodes. + // In production, use autoconf.FallbackBootstrapPeers from boxo/autoconf + // which includes well-known IPFS bootstrap peers like: // "/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", // "/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", - // "/dnsaddr/bootstrap.libp2p.io/p2p/QmbLHAnMoJPWSCR5Zhtx6BHJX9KiKNN6tpvbUcqanj75Nb", - // "/dnsaddr/bootstrap.libp2p.io/p2p/QmcZf59bWwK5XFi76CZX8cbJ4BhTzzA3gU1ZjYZcYW3dwt", + // "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", - // IPFS Cluster Pinning nodes - // "/ip4/138.201.67.219/tcp/4001/p2p/QmUd6zHcbkbcs7SMxwLs48qZVX3vpcM8errYS7xEczwRMA", - // "/ip4/138.201.67.219/udp/4001/quic/p2p/QmUd6zHcbkbcs7SMxwLs48qZVX3vpcM8errYS7xEczwRMA", - // "/ip4/138.201.67.220/tcp/4001/p2p/QmNSYxZAiJHeLdkBg38roksAR9So7Y5eojks1yjEcUtZ7i", - // "/ip4/138.201.67.220/udp/4001/quic/p2p/QmNSYxZAiJHeLdkBg38roksAR9So7Y5eojks1yjEcUtZ7i", - // "/ip4/138.201.68.74/tcp/4001/p2p/QmdnXwLrC8p1ueiq2Qya8joNvk3TVVDAut7PrikmZwubtR", - // "/ip4/138.201.68.74/udp/4001/quic/p2p/QmdnXwLrC8p1ueiq2Qya8joNvk3TVVDAut7PrikmZwubtR", - // "/ip4/94.130.135.167/tcp/4001/p2p/QmUEMvxS2e7iDrereVYc5SWPauXPyNwxcy9BXZrC1QTcHE", - // "/ip4/94.130.135.167/udp/4001/quic/p2p/QmUEMvxS2e7iDrereVYc5SWPauXPyNwxcy9BXZrC1QTcHE", + // You can add custom peers here. For example, another IPFS node: + // "/ip4/192.0.2.1/tcp/4001/p2p/QmYourPeerID...", + // "/ip4/192.0.2.1/udp/4001/quic-v1/p2p/QmYourPeerID...", - // You can add more nodes here, for example, another IPFS node you might have running locally, mine was: - // "/ip4/127.0.0.1/tcp/4010/p2p/QmZp2fhDLxjYue2RiUvLwT9MWdnbDxam32qYFnGmxZDh5L", - // "/ip4/127.0.0.1/udp/4010/quic/p2p/QmZp2fhDLxjYue2RiUvLwT9MWdnbDxam32qYFnGmxZDh5L", + // nodeA's address (the peer we created above that has our test file) peerMa, } - go func() { - err := connectToPeers(ctx, ipfsB, bootstrapNodes) - if err != nil { - log.Printf("failed connect to peers: %s", err) - } - }() + fmt.Println("Connecting to peers...") + err = connectToPeers(ctx, ipfsB, bootstrapNodes) + if err != nil { + panic(fmt.Errorf("failed to connect to peers: %s", err)) + } + fmt.Println("Connected to peers") exampleCIDStr := peerCidFile.RootCid().String() diff --git a/go.mod b/go.mod index 8be0de334..190a08b62 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 github.com/filecoin-project/go-clock v0.1.0 - github.com/fsnotify/fsnotify v1.7.0 + github.com/fsnotify/fsnotify v1.9.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-version v1.7.0 github.com/ipfs-shipyard/nopfs v0.0.14 @@ -47,11 +47,11 @@ require ( github.com/ipld/go-car/v2 v2.16.0 github.com/ipld/go-codec-dagpb v1.7.0 github.com/ipld/go-ipld-prime v0.21.0 - github.com/ipshipyard/p2p-forge v0.6.1 + github.com/ipshipyard/p2p-forge v0.7.0 github.com/jbenet/go-temp-err-catcher v0.1.0 github.com/julienschmidt/httprouter v1.3.0 github.com/libp2p/go-doh-resolver v0.5.0 - github.com/libp2p/go-libp2p v0.45.0 + github.com/libp2p/go-libp2p v0.46.0 github.com/libp2p/go-libp2p-http v0.5.0 github.com/libp2p/go-libp2p-kad-dht v0.36.0 github.com/libp2p/go-libp2p-kbucket v0.8.0 @@ -127,7 +127,6 @@ require ( github.com/fatih/color v1.15.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/flynn/noise v1.1.0 // indirect - github.com/francoispqt/gojay v1.2.13 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/gammazero/chanqueue v1.1.1 // indirect github.com/gammazero/deque v1.2.0 // indirect @@ -220,8 +219,8 @@ require ( github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.17.0 // indirect github.com/prometheus/statsd_exporter v0.27.1 // indirect - github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.55.0 // indirect + github.com/quic-go/qpack v0.6.0 // indirect + github.com/quic-go/quic-go v0.57.1 // indirect github.com/quic-go/webtransport-go v0.9.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect diff --git a/go.sum b/go.sum index 1cb7a2d6c..115681360 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,7 @@ bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc h1:utDghgcjE8u+EBjHOgYT+dJPcnDF05KqWMBcjuJy510= bazil.org/fuse v0.0.0-20200117225306-7b5117fecadc/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= @@ -36,12 +34,7 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= @@ -81,7 +74,6 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/btcsuite/btcd v0.0.0-20190824003749-130ea5bddde3/go.mod h1:3J08xEfcugPacsc34/LKRU2yO7YmuT8yt28J8k2+rrI= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= @@ -91,7 +83,6 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/caddyserver/certmagic v0.23.0 h1:CfpZ/50jMfG4+1J/u2LV6piJq4HOfO6ppOnOf7DkFEU= github.com/caddyserver/certmagic v0.23.0/go.mod h1:9mEZIWqqWoI+Gf+4Trh04MOVPD0tGSxtqsxg87hAIH4= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= @@ -138,7 +129,6 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= @@ -189,16 +179,14 @@ github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjr github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gammazero/chanqueue v1.1.1 h1:n9Y+zbBxw2f7uUE9wpgs0rOSkP/I/yhDLiNuhyVjojQ= @@ -209,10 +197,7 @@ github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9 h1:r5GgOLGbza2wVHRzK7aAj6lWZjfbAwiu/RDCVOKjRyM= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -253,7 +238,6 @@ github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -298,8 +282,6 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= @@ -318,8 +300,6 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -329,8 +309,6 @@ github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/guillaumemichel/reservedpool v0.3.0 h1:eqqO/QvTllLBrit7LVtVJBqw4cD0WdV9ajUe7WNTajw= @@ -437,8 +415,8 @@ github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20250821084354-a425e60cd714 h1:cqNk8PEwHnK0vqWln+U/YZhQc9h2NB3KjUjDPZo5Q2s= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20250821084354-a425e60cd714/go.mod h1:ZEUdra3CoqRVRYgAX/jAJO9aZGz6SKtKEG628fHHktY= -github.com/ipshipyard/p2p-forge v0.6.1 h1:987/hUC1YxI56CcMX6iTB+9BLjFV0d2SJnig9Z1pf8A= -github.com/ipshipyard/p2p-forge v0.6.1/go.mod h1:pj8Zcs+ex5OMq5a1bFLHqW0oL3qYO0v5eGLZmit0l7U= +github.com/ipshipyard/p2p-forge v0.7.0 h1:PQayexxZC1FR2Vx0XOSbmZ6wDPliidS48I+xXWuF+YU= +github.com/ipshipyard/p2p-forge v0.7.0/go.mod h1:i2wg0p7WmHGyo5vYaK9COZBp8BN5Drncfu3WoQNZlQY= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA= @@ -446,7 +424,6 @@ github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABo github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= github.com/jbenet/goprocess v0.0.0-20160826012719-b497e2f366b8/go.mod h1:Ly/wlsjFq/qrU3Rar62tu1gASgGw6chQbSh/XgIIXCY= github.com/jbenet/goprocess v0.1.3/go.mod h1:5yspPrukOVuOLORacaBi858NqyClJPQxYZlqdZVfqY4= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -484,7 +461,6 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -504,8 +480,8 @@ github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZ github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784= github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo= -github.com/libp2p/go-libp2p v0.45.0 h1:Pdhr2HsFXaYjtfiNcBP4CcRUONvbMFdH3puM9vV4Tiw= -github.com/libp2p/go-libp2p v0.45.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc= +github.com/libp2p/go-libp2p v0.46.0 h1:0T2yvIKpZ3DVYCuPOFxPD1layhRU486pj9rSlGWYnDM= +github.com/libp2p/go-libp2p v0.46.0/go.mod h1:TbIDnpDjBLa7isdgYpbxozIVPBTmM/7qKOJP4SFySrQ= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= @@ -547,10 +523,8 @@ github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfY github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv53Q= github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxfg= github.com/marcopolo/simnet v0.0.1/go.mod h1:WDaQkgLAjqDUEBAOXz22+1j6wXKfGlC5sD5XWt3ddOs= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= @@ -575,7 +549,6 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= @@ -647,8 +620,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -669,7 +640,6 @@ github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= @@ -734,7 +704,6 @@ github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4 github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/probe-lab/go-libdht v0.4.0 h1:LAqHuko/owRW6+0cs5wmJXbHzg09EUMJEh5DI37yXqo= github.com/probe-lab/go-libdht v0.4.0/go.mod h1:hamw22kI6YkPQFGy5P6BrWWDrgE9ety5Si8iWAyuDvc= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= @@ -750,7 +719,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1: github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= @@ -759,7 +727,6 @@ github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJ github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= @@ -771,10 +738,10 @@ github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUO github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= github.com/prometheus/statsd_exporter v0.27.1 h1:tcRJOmwlA83HPfWzosAgr2+zEN5XDFv+M2mn/uYkn5Y= github.com/prometheus/statsd_exporter v0.27.1/go.mod h1:vA6ryDfsN7py/3JApEst6nLTJboq66XsNcJGNmC88NQ= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= -github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= +github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -789,30 +756,7 @@ github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -825,8 +769,6 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= @@ -862,7 +804,6 @@ github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxm github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U= github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -881,8 +822,6 @@ github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb h1:Ywfo8sUltxogBpF github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE= github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s= github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y= @@ -920,7 +859,6 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= @@ -984,17 +922,13 @@ go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1023,7 +957,6 @@ golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1049,14 +982,11 @@ golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1100,8 +1030,6 @@ golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1110,7 +1038,6 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1130,14 +1057,12 @@ golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1221,16 +1146,13 @@ golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1289,9 +1211,6 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhS golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1309,18 +1228,12 @@ google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1353,9 +1266,6 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1: google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1395,7 +1305,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8= gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= @@ -1410,8 +1319,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1426,5 +1333,3 @@ pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/test/cli/delegated_routing_v1_http_server_test.go b/test/cli/delegated_routing_v1_http_server_test.go index b3fdcba05..7883fa793 100644 --- a/test/cli/delegated_routing_v1_http_server_test.go +++ b/test/cli/delegated_routing_v1_http_server_test.go @@ -242,6 +242,13 @@ func TestRoutingV1Server(t *testing.T) { }) node.StartDaemon() + // Create client before waiting so we can probe DHT readiness + c, err := client.New(node.GatewayURL()) + require.NoError(t, err) + + // Query for closest peers to our own peer ID + key := peer.ToCid(node.PeerID()) + // Wait for node to connect to bootstrap peers and populate WAN DHT routing table minPeers := len(autoconf.FallbackBootstrapPeers) require.EventuallyWithT(t, func(t *assert.CollectT) { @@ -253,13 +260,18 @@ func TestRoutingV1Server(t *testing.T) { // Wait until we have at least minPeers connected assert.GreaterOrEqual(t, peerCount, minPeers, "waiting for at least %d bootstrap peers, currently have %d", minPeers, peerCount) - }, 30*time.Second, time.Second) + }, 60*time.Second, time.Second) - c, err := client.New(node.GatewayURL()) - require.NoError(t, err) - - // Query for closest peers to our own peer ID - key := peer.ToCid(node.PeerID()) + // Wait for DHT to be ready by probing GetClosestPeers until it succeeds + require.EventuallyWithT(t, func(t *assert.CollectT) { + probeCtx, probeCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer probeCancel() + probeIter, probeErr := c.GetClosestPeers(probeCtx, key) + if probeErr == nil { + probeIter.Close() + } + assert.NoError(t, probeErr, "DHT should be ready to handle GetClosestPeers") + }, 2*time.Minute, 5*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) defer cancel() diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index f9121746e..6cebaf1f7 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -87,8 +87,7 @@ require ( github.com/filecoin-project/go-clock v0.1.0 // indirect github.com/firefart/nonamedreturns v1.0.5 // indirect github.com/flynn/noise v1.1.0 // indirect - github.com/francoispqt/gojay v1.2.13 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/gammazero/chanqueue v1.1.1 // indirect @@ -153,7 +152,7 @@ require ( github.com/ipld/go-car/v2 v2.16.0 // indirect github.com/ipld/go-codec-dagpb v1.7.0 // indirect github.com/ipld/go-ipld-prime v0.21.0 // indirect - github.com/ipshipyard/p2p-forge v0.6.1 // indirect + github.com/ipshipyard/p2p-forge v0.7.0 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect github.com/jgautheron/goconst v1.7.1 // indirect @@ -182,7 +181,7 @@ require ( github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-doh-resolver v0.5.0 // indirect github.com/libp2p/go-flow-metrics v0.3.0 // indirect - github.com/libp2p/go-libp2p v0.45.0 // indirect + github.com/libp2p/go-libp2p v0.46.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect github.com/libp2p/go-libp2p-kad-dht v0.36.0 // indirect github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect @@ -258,7 +257,7 @@ require ( github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect - github.com/quic-go/quic-go v0.55.0 // indirect + github.com/quic-go/quic-go v0.57.1 // indirect github.com/raeperd/recvcheck v0.2.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index c5fba6ef6..46f9b2409 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -2,15 +2,6 @@ 4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY= 4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU= 4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E= github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI= github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE= @@ -63,14 +54,12 @@ github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQ github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= github.com/alingse/nilnesserr v0.1.2 h1:Yf8Iwm3z2hUUrP4muWfW83DF4nE3r1xZ26fGWUKCZlo= github.com/alingse/nilnesserr v0.1.2/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU= github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bitfield/gotestdox v0.2.2 h1:x6RcPAbBbErKLnapz1QeAlf3ospg8efBsedU93CDsnE= @@ -81,12 +70,10 @@ github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= github.com/bombsimon/wsl/v4 v4.5.0 h1:iZRsEvDdyhd2La0FVi5k6tYehpOR/R7qIUjmKk7N74A= github.com/bombsimon/wsl/v4 v4.5.0/go.mod h1:NOQ3aLF4nD7N5YPXMruR6ZXDOAqLoM0GEpLwTdvmOSc= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= github.com/breml/bidichk v0.3.2 h1:xV4flJ9V5xWTqxL+/PMFF6dtJPvZLPsyixAoPe8BGJs= github.com/breml/bidichk v0.3.2/go.mod h1:VzFLBxuYtT23z5+iVkamXO386OB+/sVwZOpIj6zXGos= github.com/breml/errchkjson v0.4.0 h1:gftf6uWZMtIa/Is3XJgibewBm2ksAQSY/kABDNFTAdk= github.com/breml/errchkjson v0.4.0/go.mod h1:AuBOSTHyLSaaAFlWsRSuRBIroCh3eh7ZHh5YeelDIk8= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= github.com/butuzov/ireturn v0.3.1 h1:mFgbEI6m+9W8oP/oDdfA34dLisRFCj2G6o/yiI1yZrY= github.com/butuzov/ireturn v0.3.1/go.mod h1:ZfRp+E7eJLC0NQmk1Nrm1LOrn/gQlOykv+cVPdiXH5M= github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc= @@ -107,7 +94,6 @@ github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= github.com/ckaznocha/intrange v0.3.0 h1:VqnxtK32pxgkhJgYQEeOArVidIPg+ahLP7WBOXZd5ZY= github.com/ckaznocha/intrange v0.3.0/go.mod h1:+I/o2d2A1FBHgGELbGxzIcyd3/9l9DuwjM8FsbSS3Lo= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/crlib v0.0.0-20241112164430-1264a2edc35b h1:SHlYZ/bMx7frnmeqCu+xm0TCxXLzX3jQIVuFbnFGtFU= github.com/cockroachdb/crlib v0.0.0-20241112164430-1264a2edc35b/go.mod h1:Gq51ZeKaFCXk6QwuGM0w1dnaOqc/F5zKT2zA9D6Xeac= github.com/cockroachdb/datadriven v1.0.3-0.20250407164829-2945557346d5 h1:UycK/E0TkisVrQbSoxvU827FwgBBcZ95nRRmpj/12QI= @@ -126,7 +112,6 @@ github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961 h1:Nua446ru3juLH github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= @@ -156,7 +141,6 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk= github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= @@ -173,16 +157,12 @@ github.com/filecoin-project/go-clock v0.1.0 h1:SFbYIM75M8NnFm1yMHhN9Ahy3W5bEZV9g github.com/filecoin-project/go-clock v0.1.0/go.mod h1:4uB/O4PvOjlx1VCMdZ9MyDZXRm//gkj1ELEbxfI1AZs= github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg= github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= -github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= @@ -195,13 +175,10 @@ github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9 h1:r5GgOLGbza2wVHRzK7aAj6lWZjfbAwiu/RDCVOKjRyM= github.com/ghemawat/stream v0.0.0-20171120220530-696b145b53b9/go.mod h1:106OIgooyS7OzLDOpUGgm9fA3bQENb/cFSyyBmMoJDs= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghostiam/protogetter v0.3.9 h1:j+zlLLWzqLay22Cz/aYwTHKQ88GE2DQ6GkWSYFOI4lQ= github.com/ghostiam/protogetter v0.3.9/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-critic/go-critic v0.12.0 h1:iLosHZuye812wnkEz1Xu3aBwn5ocCPfc9yqmFG9pa6w= github.com/go-critic/go-critic v0.12.0/go.mod h1:DpE0P6OVc6JzVYzmM5gq5jMU31zLr4am5mB/VfFK64w= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -241,15 +218,8 @@ github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e h1:4bw4WeyTYPp0smaXiJZCNnLrvVBqirQVreixayXezGc= github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 h1:WUvBfQL6EW/40l6OmeSBYQJNSif4O11+bmWEz+C7FYw= @@ -268,8 +238,6 @@ github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2 github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs= github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -277,20 +245,14 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a h1://KbezygeMJZCSHH+HgUZiTeSoiuFspbMg1ge+eFj18= github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -311,8 +273,6 @@ github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8= github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo= github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= @@ -390,21 +350,18 @@ github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20250821084354-a425e60cd714 h1:cqNk8PEwHnK0vqWln+U/YZhQc9h2NB3KjUjDPZo5Q2s= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20250821084354-a425e60cd714/go.mod h1:ZEUdra3CoqRVRYgAX/jAJO9aZGz6SKtKEG628fHHktY= -github.com/ipshipyard/p2p-forge v0.6.1 h1:987/hUC1YxI56CcMX6iTB+9BLjFV0d2SJnig9Z1pf8A= -github.com/ipshipyard/p2p-forge v0.6.1/go.mod h1:pj8Zcs+ex5OMq5a1bFLHqW0oL3qYO0v5eGLZmit0l7U= +github.com/ipshipyard/p2p-forge v0.7.0 h1:PQayexxZC1FR2Vx0XOSbmZ6wDPliidS48I+xXWuF+YU= +github.com/ipshipyard/p2p-forge v0.7.0/go.mod h1:i2wg0p7WmHGyo5vYaK9COZBp8BN5Drncfu3WoQNZlQY= github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jjti/go-spancheck v0.6.4 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc= github.com/jjti/go-spancheck v0.6.4/go.mod h1:yAEYdKJ2lRkDA8g7X+oKUHXOWVAXSBJRv04OhF+QUjk= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= @@ -423,12 +380,10 @@ github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzh github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/koron/go-ssdp v0.0.6 h1:Jb0h04599eq/CY7rB5YEqPS83HmRfHP2azkxMN2rFtU= github.com/koron/go-ssdp v0.0.6/go.mod h1:0R9LfRJGek1zWTjN3JUNlm5INCDYGpRDfAptnct63fI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -460,8 +415,8 @@ github.com/libp2p/go-doh-resolver v0.5.0 h1:4h7plVVW+XTS+oUBw2+8KfoM1jF6w8XmO7+s github.com/libp2p/go-doh-resolver v0.5.0/go.mod h1:aPDxfiD2hNURgd13+hfo29z9IC22fv30ee5iM31RzxU= github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784= github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo= -github.com/libp2p/go-libp2p v0.45.0 h1:Pdhr2HsFXaYjtfiNcBP4CcRUONvbMFdH3puM9vV4Tiw= -github.com/libp2p/go-libp2p v0.45.0/go.mod h1:NovCojezAt4dnDd4fH048K7PKEqH0UFYYqJRjIIu8zc= +github.com/libp2p/go-libp2p v0.46.0 h1:0T2yvIKpZ3DVYCuPOFxPD1layhRU486pj9rSlGWYnDM= +github.com/libp2p/go-libp2p v0.46.0/go.mod h1:TbIDnpDjBLa7isdgYpbxozIVPBTmM/7qKOJP4SFySrQ= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-kad-dht v0.36.0 h1:7QuXhV36+Vyj+L6A7mrYkn2sYLrbRcbjvsYDu/gXhn8= @@ -482,12 +437,10 @@ github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQsc github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg= github.com/libp2p/go-yamux/v5 v5.0.1/go.mod h1:en+3cdX51U0ZslwRdRLrvQsdayFt3TSUKvBGErzpWbU= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= @@ -509,12 +462,10 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY= github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -531,8 +482,6 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= @@ -564,8 +513,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= @@ -579,7 +526,6 @@ github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= @@ -638,7 +584,6 @@ github.com/pion/turn/v4 v4.0.2/go.mod h1:pMMKP/ieNAG/fN5cZiN4SDuyKsXtNTr0ccN7ITo github.com/pion/webrtc/v4 v4.1.2 h1:mpuUo/EJ1zMNKGE79fAdYNFZBX790KE7kQQpLMjjR54= github.com/pion/webrtc/v4 v4.1.2/go.mod h1:xsCXiNAmMEjIdFxAYU0MbB3RwRieJsegSB2JZsGN+8U= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -650,16 +595,12 @@ github.com/polyfloyd/go-errorlint v1.7.1 h1:RyLVXIbosq1gBdk/pChWA8zWYLsq9UEw7a1L github.com/polyfloyd/go-errorlint v1.7.1/go.mod h1:aXjNb1x2TNhoLsk26iv1yl7a+zTnXPhwEMtEXukiLR8= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo= @@ -672,10 +613,10 @@ github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs= github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= -github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= +github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI= @@ -686,7 +627,6 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -708,30 +648,9 @@ github.com/sashamelentyev/usestdlibvars v1.28.0 h1:jZnudE2zKCtYlGzLVreNp5pmCdOxX github.com/sashamelentyev/usestdlibvars v1.28.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= github.com/securego/gosec/v2 v2.22.2 h1:IXbuI7cJninj0nRpZSLCUlotsj8jGusohfONMrHoF6g= github.com/securego/gosec/v2 v2.22.2/go.mod h1:UEBGA+dSKb+VqM6TdehR7lnQtIIMorYJ4/9CW1KVQBE= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= @@ -744,12 +663,10 @@ github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hg github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM= github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= @@ -772,7 +689,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= @@ -788,7 +704,6 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8= github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= @@ -818,8 +733,6 @@ github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYR github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU= github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U= github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/warpfork/go-testmark v0.12.1 h1:rMgCpJfwy1sJ50x0M0NgyphxYYPMOODIJHhsXyEHU0s= github.com/warpfork/go-testmark v0.12.1/go.mod h1:kHwy7wfvGSPh1rQJYKayD4AbtNaeyZdcGi9tNJTaa5Y= github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= @@ -866,7 +779,6 @@ go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= go-simpler.org/sloglint v0.9.0 h1:/40NQtjRx9txvsB/RN022KsUJU+zaaSb/9q9BSefSrE= go-simpler.org/sloglint v0.9.0/go.mod h1:G/OrAF6uxj48sHahCzrbarVMptL2kjWTaUeC8+fOGww= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= @@ -899,11 +811,7 @@ go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -917,16 +825,12 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4= golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -942,15 +846,7 @@ golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -971,15 +867,6 @@ golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -991,11 +878,7 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1038,7 +921,6 @@ golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1052,15 +934,9 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= @@ -1096,31 +972,13 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhS golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -1131,10 +989,6 @@ gotest.tools/gotestsum v1.12.3 h1:jFwenGJ0RnPkuKh2VzAYl1mDOJgbhobBDeL2W1iEycs= gotest.tools/gotestsum v1.12.3/go.mod h1:Y1+e0Iig4xIRtdmYbEV7K7H6spnjc1fX4BOuUhWw2Wk= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= @@ -1143,5 +997,3 @@ mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U= mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= From 117d8d67e53ed8b96b111f6182e63bdd7c8f457a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 22:56:59 +0100 Subject: [PATCH 02/49] chore(deps): bump actions/cache from 4 to 5 (#11108) Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/interop.yml | 4 ++-- .github/workflows/sharness.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/interop.yml b/.github/workflows/interop.yml index ebc0dbe8f..43687dd63 100644 --- a/.github/workflows/interop.yml +++ b/.github/workflows/interop.yml @@ -59,7 +59,7 @@ jobs: - run: chmod +x cmd/ipfs/ipfs - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT id: npm-cache-dir - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ${{ steps.npm-cache-dir.outputs.dir }} key: ${{ runner.os }}-${{ github.job }}-helia-${{ hashFiles('**/package-lock.json') }} @@ -99,7 +99,7 @@ jobs: - run: | echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT id: npm-cache-dir - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: ${{ steps.npm-cache-dir.outputs.dir }} key: ${{ runner.os }}-${{ github.job }}-${{ hashFiles('**/package-lock.json') }} diff --git a/.github/workflows/sharness.yml b/.github/workflows/sharness.yml index 67ebd208e..94fdf8008 100644 --- a/.github/workflows/sharness.yml +++ b/.github/workflows/sharness.yml @@ -32,7 +32,7 @@ jobs: go-version-file: 'kubo/go.mod' - name: Install missing tools run: sudo apt update && sudo apt install -y socat net-tools fish libxml2-utils - - uses: actions/cache@v4 + - uses: actions/cache@v5 with: path: test/sharness/lib/dependencies key: ${{ runner.os }}-test-generate-junit-html-${{ hashFiles('test/sharness/lib/test-generate-junit-html.sh') }} From edc2cadc85a17ba30c5c1fd5b48c7813bf953e56 Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Fri, 19 Dec 2025 17:08:22 -0800 Subject: [PATCH 03/49] output stdout and stderr on example test failure (#11119) --- docs/examples/kubo-as-a-library/main_test.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/examples/kubo-as-a-library/main_test.go b/docs/examples/kubo-as-a-library/main_test.go index ec34d62b1..be601c6a9 100644 --- a/docs/examples/kubo-as-a-library/main_test.go +++ b/docs/examples/kubo-as-a-library/main_test.go @@ -9,7 +9,11 @@ import ( func TestExample(t *testing.T) { out, err := exec.Command("go", "run", "main.go").Output() if err != nil { - t.Fatalf("running example (%v)", err) + var stderr string + if xe, ok := err.(*exec.ExitError); ok { + stderr = string(xe.Stderr) + } + t.Fatalf("running example (%v): %s\n%s", err, string(out), stderr) } if !strings.Contains(string(out), "All done!") { t.Errorf("example did not run successfully") From 828526e5158b8ebdff361e374032890ae439113a Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Tue, 6 Jan 2026 01:38:03 -0800 Subject: [PATCH 04/49] datastore: upgrade go-ds-pebble to v0.5.8 (#11129) * datastore: upgrade go-ds-pebble to v0.5.8 - update 'go-ds-pebble` to [v0.5.8](https://github.com/ipfs/go-ds-pebble/releases/tag/v0.5.8) - updates github.com/cockroachdb/pebble to [v2.1.3](https://github.com/cockroachdb/pebble/releases/tag/v2.1.3) - enables Go 1.26 support --- docs/changelogs/v0.40.md | 2 ++ docs/examples/kubo-as-a-library/go.mod | 6 +++--- docs/examples/kubo-as-a-library/go.sum | 12 ++++++------ go.mod | 6 +++--- go.sum | 12 ++++++------ test/dependencies/go.mod | 4 ++-- test/dependencies/go.sum | 8 ++++---- 7 files changed, 26 insertions(+), 24 deletions(-) diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index 1b963611b..f8284d46c 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -40,6 +40,8 @@ Fetched/Processed 336 nodes (83 MB) - Fixed mDNS discovery on Windows and macOS by filtering addresses to reduce packet size ([go-libp2p#3434](https://github.com/libp2p/go-libp2p/pull/3434)). - update `quic-go` to [v0.57.1](https://github.com/quic-go/quic-go/releases/tag/v0.57.1) (incl. [v0.56.0](https://github.com/quic-go/quic-go/releases/tag/v0.56.0) + [v0.57.0](https://github.com/quic-go/quic-go/releases/tag/v0.57.0)) - update `p2p-forge` to [v0.7.0](https://github.com/ipshipyard/p2p-forge/releases/tag/v0.7.0) +- update `go-ds-pebble` to [v0.5.8](https://github.com/ipfs/go-ds-pebble/releases/tag/v0.5.8) + - updates `github.com/cockroachdb/pebble` to [v2.1.3](https://github.com/cockroachdb/pebble/releases/tag/v2.1.3) to enable Go 1.26 support ### ๐Ÿ“ Changelog diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 54c6efde7..a747ec1dc 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -34,9 +34,9 @@ require ( github.com/cockroachdb/crlib v0.0.0-20241112164430-1264a2edc35b // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble/v2 v2.1.2 // indirect + github.com/cockroachdb/pebble/v2 v2.1.3 // indirect github.com/cockroachdb/redact v1.1.5 // indirect - github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961 // indirect + github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf // indirect github.com/cskr/pubsub v1.0.2 // indirect @@ -81,7 +81,7 @@ require ( github.com/ipfs/go-ds-flatfs v0.5.5 // indirect github.com/ipfs/go-ds-leveldb v0.5.2 // indirect github.com/ipfs/go-ds-measure v0.2.2 // indirect - github.com/ipfs/go-ds-pebble v0.5.7 // indirect + github.com/ipfs/go-ds-pebble v0.5.8 // indirect github.com/ipfs/go-dsqueue v0.1.1 // indirect github.com/ipfs/go-fs-lock v0.1.1 // indirect github.com/ipfs/go-ipfs-cmds v0.15.0 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 46b5cb555..e198bdb79 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -84,12 +84,12 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895 h1:XANOgPYtvELQ/h4IrmPAohXqe2pWA8Bwhejr3VQoZsA= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895/go.mod h1:aPd7gM9ov9M8v32Yy5NJrDyOcD8z642dqs+F0CeNXfA= -github.com/cockroachdb/pebble/v2 v2.1.2 h1:IwYt+Y2Cdw6egblwk1kWzdmJvD2680t5VK/3i0BJ6IA= -github.com/cockroachdb/pebble/v2 v2.1.2/go.mod h1:Aza05DCCc05ghIJZkB4Q/axv/JK9wx5cFwWcnhG0eGw= +github.com/cockroachdb/pebble/v2 v2.1.3 h1:irU503OnjRoJBrkZQIJvwv9c4WvpUeOJxhRApojB8D8= +github.com/cockroachdb/pebble/v2 v2.1.3/go.mod h1:B1UgWsyR+L+UvZXNgpxw+WqsUKA8VQ/bb//FXOHghB8= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961 h1:Nua446ru3juLHLZd4AwKNzClZgL1co3pUPGv3o8FlcA= -github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg= +github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b h1:VXvSNzmr8hMj8XTuY0PT9Ane9qZGul/p67vGYwl9BFI= +github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -295,8 +295,8 @@ github.com/ipfs/go-ds-leveldb v0.5.2 h1:6nmxlQ2zbp4LCNdJVsmHfs9GP0eylfBNxpmY1csp github.com/ipfs/go-ds-leveldb v0.5.2/go.mod h1:2fAwmcvD3WoRT72PzEekHBkQmBDhc39DJGoREiuGmYo= github.com/ipfs/go-ds-measure v0.2.2 h1:4kwvBGbbSXNYe4ANlg7qTIYoZU6mNlqzQHdVqICkqGI= github.com/ipfs/go-ds-measure v0.2.2/go.mod h1:b/87ak0jMgH9Ylt7oH0+XGy4P8jHx9KG09Qz+pOeTIs= -github.com/ipfs/go-ds-pebble v0.5.7 h1:4PQI46y3fjjxUTgHwYqcOVyoxiU6v1sqN6ONeRXGQTM= -github.com/ipfs/go-ds-pebble v0.5.7/go.mod h1:rsIgXE2qN+VfHKBin2cOOGFTZ/Agor6i8wBWA6ihbr0= +github.com/ipfs/go-ds-pebble v0.5.8 h1:NbAfKQo+m39Nka6gt8PARAyH+VoHtRInB6CFCmT+wqo= +github.com/ipfs/go-ds-pebble v0.5.8/go.mod h1:AJjJTHgads/Fn5+tuWmaDGjGEbks7Wgx82NQ/pwmEhc= github.com/ipfs/go-dsqueue v0.1.1 h1:6PQlHDyf9PSTN69NmwUir5+0is3tU0vRJj8zLlgK8Mc= github.com/ipfs/go-dsqueue v0.1.1/go.mod h1:Xxg353WSwwzYn3FGSzZ+taSQII3pIZ+EJC8/oWRDM10= github.com/ipfs/go-fs-lock v0.1.1 h1:TecsP/Uc7WqYYatasreZQiP9EGRy4ZnKoG4yXxR33nw= diff --git a/go.mod b/go.mod index 190a08b62..4ef10f87b 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 github.com/ceramicnetwork/go-dag-jose v0.1.1 github.com/cheggaaa/pb v1.0.29 - github.com/cockroachdb/pebble/v2 v2.1.2 + github.com/cockroachdb/pebble/v2 v2.1.3 github.com/coreos/go-systemd/v22 v22.5.0 github.com/dustin/go-humanize v1.0.1 github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 @@ -32,7 +32,7 @@ require ( github.com/ipfs/go-ds-flatfs v0.5.5 github.com/ipfs/go-ds-leveldb v0.5.2 github.com/ipfs/go-ds-measure v0.2.2 - github.com/ipfs/go-ds-pebble v0.5.7 + github.com/ipfs/go-ds-pebble v0.5.8 github.com/ipfs/go-fs-lock v0.1.1 github.com/ipfs/go-ipfs-cmds v0.15.0 github.com/ipfs/go-ipld-cbor v0.2.1 @@ -114,7 +114,7 @@ require ( github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect - github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961 // indirect + github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf // indirect github.com/cskr/pubsub v1.0.2 // indirect diff --git a/go.sum b/go.sum index 115681360..f5f690e1b 100644 --- a/go.sum +++ b/go.sum @@ -117,12 +117,12 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895 h1:XANOgPYtvELQ/h4IrmPAohXqe2pWA8Bwhejr3VQoZsA= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895/go.mod h1:aPd7gM9ov9M8v32Yy5NJrDyOcD8z642dqs+F0CeNXfA= -github.com/cockroachdb/pebble/v2 v2.1.2 h1:IwYt+Y2Cdw6egblwk1kWzdmJvD2680t5VK/3i0BJ6IA= -github.com/cockroachdb/pebble/v2 v2.1.2/go.mod h1:Aza05DCCc05ghIJZkB4Q/axv/JK9wx5cFwWcnhG0eGw= +github.com/cockroachdb/pebble/v2 v2.1.3 h1:irU503OnjRoJBrkZQIJvwv9c4WvpUeOJxhRApojB8D8= +github.com/cockroachdb/pebble/v2 v2.1.3/go.mod h1:B1UgWsyR+L+UvZXNgpxw+WqsUKA8VQ/bb//FXOHghB8= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961 h1:Nua446ru3juLHLZd4AwKNzClZgL1co3pUPGv3o8FlcA= -github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg= +github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b h1:VXvSNzmr8hMj8XTuY0PT9Ane9qZGul/p67vGYwl9BFI= +github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -366,8 +366,8 @@ github.com/ipfs/go-ds-leveldb v0.5.2 h1:6nmxlQ2zbp4LCNdJVsmHfs9GP0eylfBNxpmY1csp github.com/ipfs/go-ds-leveldb v0.5.2/go.mod h1:2fAwmcvD3WoRT72PzEekHBkQmBDhc39DJGoREiuGmYo= github.com/ipfs/go-ds-measure v0.2.2 h1:4kwvBGbbSXNYe4ANlg7qTIYoZU6mNlqzQHdVqICkqGI= github.com/ipfs/go-ds-measure v0.2.2/go.mod h1:b/87ak0jMgH9Ylt7oH0+XGy4P8jHx9KG09Qz+pOeTIs= -github.com/ipfs/go-ds-pebble v0.5.7 h1:4PQI46y3fjjxUTgHwYqcOVyoxiU6v1sqN6ONeRXGQTM= -github.com/ipfs/go-ds-pebble v0.5.7/go.mod h1:rsIgXE2qN+VfHKBin2cOOGFTZ/Agor6i8wBWA6ihbr0= +github.com/ipfs/go-ds-pebble v0.5.8 h1:NbAfKQo+m39Nka6gt8PARAyH+VoHtRInB6CFCmT+wqo= +github.com/ipfs/go-ds-pebble v0.5.8/go.mod h1:AJjJTHgads/Fn5+tuWmaDGjGEbks7Wgx82NQ/pwmEhc= github.com/ipfs/go-dsqueue v0.1.1 h1:6PQlHDyf9PSTN69NmwUir5+0is3tU0vRJj8zLlgK8Mc= github.com/ipfs/go-dsqueue v0.1.1/go.mod h1:Xxg353WSwwzYn3FGSzZ+taSQII3pIZ+EJC8/oWRDM10= github.com/ipfs/go-fs-lock v0.1.1 h1:TecsP/Uc7WqYYatasreZQiP9EGRy4ZnKoG4yXxR33nw= diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index 6cebaf1f7..826fd60eb 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -65,9 +65,9 @@ require ( github.com/cockroachdb/crlib v0.0.0-20241112164430-1264a2edc35b // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble/v2 v2.1.2 // indirect + github.com/cockroachdb/pebble/v2 v2.1.3 // indirect github.com/cockroachdb/redact v1.1.5 // indirect - github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961 // indirect + github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index 46f9b2409..829479591 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -104,12 +104,12 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895 h1:XANOgPYtvELQ/h4IrmPAohXqe2pWA8Bwhejr3VQoZsA= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895/go.mod h1:aPd7gM9ov9M8v32Yy5NJrDyOcD8z642dqs+F0CeNXfA= -github.com/cockroachdb/pebble/v2 v2.1.2 h1:IwYt+Y2Cdw6egblwk1kWzdmJvD2680t5VK/3i0BJ6IA= -github.com/cockroachdb/pebble/v2 v2.1.2/go.mod h1:Aza05DCCc05ghIJZkB4Q/axv/JK9wx5cFwWcnhG0eGw= +github.com/cockroachdb/pebble/v2 v2.1.3 h1:irU503OnjRoJBrkZQIJvwv9c4WvpUeOJxhRApojB8D8= +github.com/cockroachdb/pebble/v2 v2.1.3/go.mod h1:B1UgWsyR+L+UvZXNgpxw+WqsUKA8VQ/bb//FXOHghB8= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= -github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961 h1:Nua446ru3juLHLZd4AwKNzClZgL1co3pUPGv3o8FlcA= -github.com/cockroachdb/swiss v0.0.0-20250624142022-d6e517c1d961/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg= +github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b h1:VXvSNzmr8hMj8XTuY0PT9Ane9qZGul/p67vGYwl9BFI= +github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b/go.mod h1:yBRu/cnL4ks9bgy4vAASdjIW+/xMlFwuHKqtmh3GZQg= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= From 3d0e7c846579a75d7c7d41ccbb231f859a75ea14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 22:50:47 -0800 Subject: [PATCH 05/49] chore(deps): bump actions/download-artifact from 6 to 7 (#11107) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/interop.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/interop.yml b/.github/workflows/interop.yml index 43687dd63..d8b4eaad1 100644 --- a/.github/workflows/interop.yml +++ b/.github/workflows/interop.yml @@ -52,7 +52,7 @@ jobs: - uses: actions/setup-node@v6 with: node-version: lts/* - - uses: actions/download-artifact@v6 + - uses: actions/download-artifact@v7 with: name: kubo path: cmd/ipfs @@ -87,7 +87,7 @@ jobs: - uses: actions/setup-node@v6 with: node-version: 20.x - - uses: actions/download-artifact@v6 + - uses: actions/download-artifact@v7 with: name: kubo path: cmd/ipfs From f4d6253ae201a032d6d992adfcd3bba7ea1d6658 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 7 Jan 2026 17:09:13 +0100 Subject: [PATCH 06/49] docs: add developer guide for local development workflow (#11128) addresses https://discuss.ipfs.tech/t/19933 - add docs/developer-guide.md with prerequisites, build, test, and troubleshooting - link from README.md, docs/README.md, and CONTRIBUTING.md - document test suite differences (unit vs e2e, test/cli vs test/sharness) - include tips for running specific tests during development --- CONTRIBUTING.md | 8 +- README.md | 41 +----- docs/README.md | 6 +- docs/developer-guide.md | 316 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 326 insertions(+), 45 deletions(-) create mode 100644 docs/developer-guide.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1db5ca246..ed9001df2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,10 @@ -IPFS as a project, including go-ipfs and all of its modules, follows the [standard IPFS Community contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md). +# Contributing to Kubo -We also adhere to the [GO IPFS Community contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING_GO.md) which provide additional information of how to collaborate and contribute in the Go implementation of IPFS. +**For development setup, building, and testing, see the [Developer Guide](docs/developer-guide.md).** + +IPFS as a project, including Kubo and all of its modules, follows the [standard IPFS Community contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md). + +We also adhere to the [Go IPFS Community contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING_GO.md) which provide additional information on how to collaborate and contribute to the Go implementation of IPFS. We appreciate your time and attention for going over these. Please open an issue on ipfs/community if you have any questions. diff --git a/README.md b/README.md index bd1cf9967..fa8285253 100644 --- a/README.md +++ b/README.md @@ -98,11 +98,6 @@ Before opening an issue, consider using one of the following locations to ensure - [Troubleshooting](#troubleshooting-1) - [Packages](#packages) - [Development](#development) - - [Map of Implemented Subsystems](#map-of-implemented-subsystems) - - [CLI, HTTP-API, Architecture Diagram](#cli-http-api-architecture-diagram) - - [Testing](#testing) - - [Development Dependencies](#development-dependencies) - - [Developer Notes](#developer-notes) - [Maintainer Info](#maintainer-info) - [Contributing](#contributing) - [License](#license) @@ -450,41 +445,7 @@ See [IPFS in GO](https://docs.ipfs.tech/reference/go/api/) documentation. ## Development -Some places to get you started on the codebase: - -- Main file: [./cmd/ipfs/main.go](https://github.com/ipfs/kubo/blob/master/cmd/ipfs/main.go) -- CLI Commands: [./core/commands/](https://github.com/ipfs/kubo/tree/master/core/commands) -- Bitswap (the data trading engine): [go-bitswap](https://github.com/ipfs/go-bitswap) -- libp2p - - libp2p: https://github.com/libp2p/go-libp2p - - DHT: https://github.com/libp2p/go-libp2p-kad-dht -- [IPFS : The `Add` command demystified](https://github.com/ipfs/kubo/tree/master/docs/add-code-flow.md) - -### Map of Implemented Subsystems -**WIP**: This is a high-level architecture diagram of the various sub-systems of this specific implementation. To be updated with how they interact. Anyone who has suggestions is welcome to comment [here](https://docs.google.com/drawings/d/1OVpBT2q-NtSJqlPX3buvjYhOnWfdzb85YEsM_njesME/edit) on how we can improve this! - - -### CLI, HTTP-API, Architecture Diagram - -![](./docs/cli-http-api-core-diagram.png) - -> [Origin](https://github.com/ipfs/pm/pull/678#discussion_r210410924) - -Description: Dotted means "likely going away". The "Legacy" parts are thin wrappers around some commands to translate between the new system and the old system. The grayed-out parts on the "daemon" diagram are there to show that the code is all the same, it's just that we turn some pieces on and some pieces off depending on whether we're running on the client or the server. - -### Testing - -``` -make test -``` - -### Development Dependencies - -If you make changes to the protocol buffers, you will need to install the [protoc compiler](https://github.com/google/protobuf). - -### Developer Notes - -Find more documentation for developers on [docs](./docs) +See the [Developer Guide](docs/developer-guide.md) for build instructions, testing, architecture, and contribution workflow. ## Maintainer Info diff --git a/docs/README.md b/docs/README.md index ab7ac9cc3..244aa4846 100644 --- a/docs/README.md +++ b/docs/README.md @@ -14,9 +14,9 @@ Otherwise, check out the following guides to using and developing IPFS: ## Developing `kubo` -- First, please read the Contributing Guidelines [for IPFS projects](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) and then the Contributing Guidelines for [Go code specifically](https://github.com/ipfs/community/blob/master/CONTRIBUTING_GO.md) -- Building onโ€ฆ - - [Windows](windows.md) +- **[Developer Guide](developer-guide.md)** - prerequisites, build, test, and contribute +- Contributing Guidelines [for IPFS projects](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) and for [Go code specifically](https://github.com/ipfs/community/blob/master/CONTRIBUTING_GO.md) +- Building on [Windows](windows.md) - [Performance Debugging Guidelines](debug-guide.md) - [Release Checklist](releases.md) diff --git a/docs/developer-guide.md b/docs/developer-guide.md new file mode 100644 index 000000000..5799b48ca --- /dev/null +++ b/docs/developer-guide.md @@ -0,0 +1,316 @@ +# Developer Guide + +By the end of this guide, you will be able to: + +- Build Kubo from source +- Run the test suites +- Make and verify code changes + +This guide covers the local development workflow. For user documentation, see [docs.ipfs.tech](https://docs.ipfs.tech/). + +## Table of Contents + +- [Prerequisites](#prerequisites) +- [Quick Start](#quick-start) +- [Building](#building) +- [Running Tests](#running-tests) +- [Running the Linter](#running-the-linter) +- [Common Development Tasks](#common-development-tasks) +- [Code Organization](#code-organization) +- [Architecture](#architecture) +- [Troubleshooting](#troubleshooting) +- [Development Dependencies](#development-dependencies) +- [Further Reading](#further-reading) + +## Prerequisites + +Before you begin, ensure you have: + +- **Go** - see `go.mod` for the minimum required version +- **Git** +- **GNU Make** +- **GCC** (optional) - required for CGO (Go's C interop); without it, build with `CGO_ENABLED=0` + +## Quick Start + +```bash +git clone https://github.com/ipfs/kubo.git +cd kubo +make build +./cmd/ipfs/ipfs version +``` + +You should see output like: + +``` +ipfs version 0.34.0-dev +``` + +The binary is built to `cmd/ipfs/ipfs`. To install it system-wide: + +```bash +make install +``` + +This installs the binary to `$GOPATH/bin`. + +## Building + +| Command | Description | +|---------|-------------| +| `make build` | build the `ipfs` binary to `cmd/ipfs/ipfs` | +| `make install` | install to `$GOPATH/bin` | +| `make nofuse` | build without FUSE support | +| `make build CGO_ENABLED=0` | build without CGO (no C compiler needed) | + +For Windows-specific instructions, see [windows.md](windows.md). + +## Running Tests + +Kubo has two types of tests: + +- **Unit tests** - test individual packages in isolation. Fast and don't require a running daemon. +- **End-to-end tests** - spawn real `ipfs` nodes, run actual CLI commands, and test the full system. Slower but catch integration issues. + +Note that `go test ./...` runs both unit and end-to-end tests. Use `make test` to run all tests. CI runs unit and end-to-end tests in separate jobs for faster feedback. + + + +For end-to-end tests, Kubo has two suites: + +- **`test/cli`** - modern Go-based test harness that spawns real `ipfs` nodes and runs actual CLI commands. All new tests should be added here. +- **`test/sharness`** - legacy bash-based tests. We are slowly migrating these to `test/cli`. + +When modifying tests: cosmetic changes to `test/sharness` are fine, but if significant rewrites are needed, remove the outdated sharness test and add a modern one to `test/cli` instead. + +### Before Running Tests + +**Environment requirements**: some legacy tests expect default ports (8080, 5001, 4001) to be free and no mDNS (local network discovery) Kubo service on the LAN. Tests may fail if you have a local Kubo instance running. Before running the full test suite, stop any running `ipfs daemon`. + +Two critical setup steps: + +1. **Rebuild after code changes**: if you modify any `.go` files outside of `test/`, you must run `make build` before running integration tests. + +2. **Set environment variables**: integration tests use the `ipfs` binary from `PATH` and need an isolated `IPFS_PATH`. Run these commands from the repository root: + +```bash +export PATH="$PWD/cmd/ipfs:$PATH" +export IPFS_PATH="$(mktemp -d)" +``` + +### Unit Tests + +```bash +go test ./... +``` + +### CLI Integration Tests (`test/cli`) + +These are Go-based integration tests that invoke the `ipfs` CLI. + +Instead of running the entire test suite, you can run a specific test to get faster feedback during development. + +Run a specific test (recommended during development): + +```bash +go test ./test/cli/... -run TestAdd -v +``` + +Run all CLI tests: + +```bash +go test ./test/cli/... +``` + +Run a specific test: + +```bash +go test ./test/cli/... -run TestAdd +``` + +Run with verbose output: + +```bash +go test ./test/cli/... -v +``` + +**Common error**: "version (16) is lower than repos (17)" means your `PATH` points to an old binary. Check `which ipfs` and rebuild with `make build`. + +### Sharness Tests (`test/sharness`) + +Shell-based integration tests using [sharness](https://github.com/chriscool/sharness) (a portable shell testing framework). + +```bash +cd test/sharness +``` + +Run a specific test: + +```bash +timeout 60s ./t0080-repo.sh +``` + +Run with verbose output (this disables automatic cleanup): + +```bash +./t0080-repo.sh -v +``` + +**Cleanup**: the `-v` flag disables automatic cleanup. Before re-running tests, kill any dangling `ipfs daemon` processes: + +```bash +pkill -f "ipfs daemon" +``` + +### Full Test Suite + +```bash +make test # run all tests +make test_short # run shorter test suite +``` + +## Running the Linter + +Run the linter using the Makefile target (not `golangci-lint` directly): + +```bash +make -O test_go_lint +``` + +## Common Development Tasks + +### Modifying CLI Commands + +After editing help text in `core/commands/`, verify the output width: + +```bash +go test ./test/cli/... -run TestCommandDocsWidth +``` + +### Updating Dependencies + +Use the Makefile target (not `go mod tidy` directly): + +```bash +make mod_tidy +``` + +### Editing the Changelog + +When modifying `docs/changelogs/`: + +- update the Table of Contents when adding sections +- add user-facing changes to the Highlights section (the Changelog section is auto-generated) + +### Running the Daemon + +Always run the daemon with a timeout or shut it down promptly. + +With timeout: + +```bash +timeout 60s ipfs daemon +``` + +Or shut down via API: + +```bash +ipfs shutdown +``` + +For multi-step experiments, store `IPFS_PATH` in a file to ensure consistency. + +## Code Organization + +| Directory | Description | +|-----------|-------------| +| `cmd/ipfs/` | CLI entry point and binary | +| `core/` | core IPFS node implementation | +| `core/commands/` | CLI command definitions | +| `core/coreapi/` | Go API implementation | +| `client/rpc/` | HTTP RPC client | +| `plugin/` | plugin system | +| `repo/` | repository management | +| `test/cli/` | Go-based CLI integration tests | +| `test/sharness/` | legacy shell-based integration tests | +| `docs/` | documentation | + +Key external dependencies: + +- [go-libp2p](https://github.com/libp2p/go-libp2p) - networking stack +- [go-libp2p-kad-dht](https://github.com/libp2p/go-libp2p-kad-dht) - distributed hash table +- [boxo](https://github.com/ipfs/boxo) - IPFS SDK (including Bitswap, the data exchange engine) + +For a deep dive into how code flows through Kubo, see [The `Add` command demystified](add-code-flow.md). + +## Architecture + +**Map of Implemented Subsystems** ([editable source](https://docs.google.com/drawings/d/1OVpBT2q-NtSJqlPX3buvjYhOnWfdzb85YEsM_njesME/edit)): + + + +**CLI, HTTP-API, Core Diagram**: + +![](./cli-http-api-core-diagram.png) + +## Troubleshooting + +### "version (N) is lower than repos (M)" Error + +This means the `ipfs` binary in your `PATH` is older than expected. + +Check which binary is being used: + +```bash +which ipfs +``` + +Rebuild and verify PATH: + +```bash +make build +export PATH="$PWD/cmd/ipfs:$PATH" +./cmd/ipfs/ipfs version +``` + +### FUSE Issues + +If you don't need FUSE support, build without it: + +```bash +make nofuse +``` + +Or set the `TEST_FUSE=0` environment variable when running tests. + +### Build Fails with "No such file: stdlib.h" + +You're missing a C compiler. Either install GCC or build without CGO: + +```bash +make build CGO_ENABLED=0 +``` + +## Development Dependencies + +If you make changes to the protocol buffers, you will need to install the [protoc compiler](https://github.com/google/protobuf). + +## Further Reading + +- [The `Add` command demystified](add-code-flow.md) - deep dive into code flow +- [Configuration reference](config.md) +- [Performance debugging](debug-guide.md) +- [Experimental features](experimental-features.md) +- [Release process](releases.md) +- [Contributing guidelines](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) + +## Source Code + +The complete source code is at [github.com/ipfs/kubo](https://github.com/ipfs/kubo). From 566f8ba63f185a64ec676f1b1af5e3a61b6e174c Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Wed, 7 Jan 2026 09:56:11 -0800 Subject: [PATCH 07/49] keys: skip bad keys when listing (#11115) Change the `ipfs key list` behavior to log an error and continue listing keys when a key cannot be read from the keystore or decoded. Closes: #11102 --- core/commands/keystore.go | 4 ++-- core/coreapi/key.go | 17 ++++++++++------- docs/changelogs/v0.40.md | 5 +++++ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/core/commands/keystore.go b/core/commands/keystore.go index 0ffd14189..6ce1b5a0d 100644 --- a/core/commands/keystore.go +++ b/core/commands/keystore.go @@ -458,7 +458,7 @@ var keyListCmd = &cmds.Command{ Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { keyEnc, err := ke.KeyEncoderFromString(req.Options[ke.OptionIPNSBase.Name()].(string)) if err != nil { - return err + return fmt.Errorf("cannot get key encoder: %w", err) } api, err := cmdenv.GetApi(env, req) @@ -468,7 +468,7 @@ var keyListCmd = &cmds.Command{ keys, err := api.Key().List(req.Context) if err != nil { - return err + return fmt.Errorf("listing keys failed: %w", err) } list := make([]KeyOutput, 0, len(keys)) diff --git a/core/coreapi/key.go b/core/coreapi/key.go index 784045d26..e779c773f 100644 --- a/core/coreapi/key.go +++ b/core/coreapi/key.go @@ -29,7 +29,7 @@ type key struct { func newKey(name string, pid peer.ID) (*key, error) { p, err := path.NewPath("/ipns/" + ipns.NameFromPeer(pid).String()) if err != nil { - return nil, err + return nil, fmt.Errorf("cannot create new key: %w", err) } return &key{ name: name, @@ -121,34 +121,37 @@ func (api *KeyAPI) List(ctx context.Context) ([]coreiface.Key, error) { keys, err := api.repo.Keystore().List() if err != nil { - return nil, err + return nil, fmt.Errorf("cannot list keys in keystore: %w", err) } sort.Strings(keys) - out := make([]coreiface.Key, len(keys)+1) + out := make([]coreiface.Key, 1, len(keys)+1) out[0], err = newKey("self", api.identity) if err != nil { return nil, err } - for n, k := range keys { + for _, k := range keys { privKey, err := api.repo.Keystore().Get(k) if err != nil { - return nil, err + log.Errorf("cannot get key from keystore: %s", err) + continue } pubKey := privKey.GetPublic() pid, err := peer.IDFromPublicKey(pubKey) if err != nil { - return nil, err + log.Errorf("cannot decode public key: %s", err) + continue } - out[n+1], err = newKey(k, pid) + k, err := newKey(k, pid) if err != nil { return nil, err } + out = append(out, k) } return out, nil } diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index f8284d46c..19f785de8 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -12,6 +12,7 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [๐Ÿ”ฆ Highlights](#-highlights) - [Routing V1 HTTP API now exposed by default](#routing-v1-http-api-now-exposed-by-default) - [Track total size when adding pins](#track-total-size-when-adding-pins) + - [Skip bad keys when listing](#skip_bad_keys_when_listing) - [๐Ÿ“ฆ๏ธ Dependency updates](#-dependency-updates) - [๐Ÿ“ Changelog](#-changelog) - [๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Contributors](#-contributors) @@ -33,6 +34,10 @@ Example output: Fetched/Processed 336 nodes (83 MB) ``` +#### Skip bad keys when listing + +Change the `ipfs key list` behavior to log an error and continue listing keys when a key cannot be read from the keystore or decoded. + #### ๐Ÿ“ฆ๏ธ Dependency updates - update `go-libp2p` to [v0.46.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.46.0) From 9b99dc6f457940851c94968e88c0b3349f788111 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:24:08 -0800 Subject: [PATCH 08/49] chore(deps): bump codecov/codecov-action from 5.5.1 to 5.5.2 (#11106) Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5.5.1 to 5.5.2. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/5a1091511ad55cbe89839c7260b706298ca349f7...671740ac38dd9b0130fbe1cec585b89eea48d3de) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-version: 5.5.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gotest.yml | 2 +- .github/workflows/sharness.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gotest.yml b/.github/workflows/gotest.yml index 4e9e0b905..3f5cb0af2 100644 --- a/.github/workflows/gotest.yml +++ b/.github/workflows/gotest.yml @@ -44,7 +44,7 @@ jobs: make -j "$PARALLEL" test/unit/gotest.junit.xml && [[ ! $(jq -s -c 'map(select(.Action == "fail")) | .[]' test/unit/gotest.json) ]] - name: Upload coverage to Codecov - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 if: failure() || success() with: name: unittests diff --git a/.github/workflows/sharness.yml b/.github/workflows/sharness.yml index 94fdf8008..1941ce98b 100644 --- a/.github/workflows/sharness.yml +++ b/.github/workflows/sharness.yml @@ -55,7 +55,7 @@ jobs: # increasing parallelism beyond 10 doesn't speed up the tests much PARALLEL: ${{ github.repository == 'ipfs/kubo' && 10 || 3 }} - name: Upload coverage report - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 if: failure() || success() with: name: sharness From 663a4a78fa1e4866f122c945ad38c2e7bd3a314e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 11:25:01 -0800 Subject: [PATCH 09/49] chore(deps): bump actions/upload-artifact from 5 to 6 (#11109) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/gateway-conformance.yml | 8 ++++---- .github/workflows/gotest.yml | 4 ++-- .github/workflows/interop.yml | 2 +- .github/workflows/sharness.yml | 4 ++-- .github/workflows/test-migrations.yml | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/gateway-conformance.yml b/.github/workflows/gateway-conformance.yml index b62fd601f..f2cd854c5 100644 --- a/.github/workflows/gateway-conformance.yml +++ b/.github/workflows/gateway-conformance.yml @@ -109,13 +109,13 @@ jobs: run: cat output.md >> $GITHUB_STEP_SUMMARY - name: Upload HTML report if: failure() || success() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: gateway-conformance.html path: output.html - name: Upload JSON report if: failure() || success() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: gateway-conformance.json path: output.json @@ -214,13 +214,13 @@ jobs: run: cat output.md >> $GITHUB_STEP_SUMMARY - name: Upload HTML report if: failure() || success() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: gateway-conformance-libp2p.html path: output.html - name: Upload JSON report if: failure() || success() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: gateway-conformance-libp2p.json path: output.json diff --git a/.github/workflows/gotest.yml b/.github/workflows/gotest.yml index 3f5cb0af2..9f329cbfd 100644 --- a/.github/workflows/gotest.yml +++ b/.github/workflows/gotest.yml @@ -78,7 +78,7 @@ jobs: output: test/unit/gotest.junit.xml if: failure() || success() - name: Archive the JUnit XML report - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: unit path: test/unit/gotest.junit.xml @@ -91,7 +91,7 @@ jobs: output: test/unit/gotest.html if: failure() || success() - name: Archive the HTML report - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: html path: test/unit/gotest.html diff --git a/.github/workflows/interop.yml b/.github/workflows/interop.yml index d8b4eaad1..25bdba4f2 100644 --- a/.github/workflows/interop.yml +++ b/.github/workflows/interop.yml @@ -37,7 +37,7 @@ jobs: with: go-version-file: 'go.mod' - run: make build - - uses: actions/upload-artifact@v5 + - uses: actions/upload-artifact@v6 with: name: kubo path: cmd/ipfs/ipfs diff --git a/.github/workflows/sharness.yml b/.github/workflows/sharness.yml index 1941ce98b..7275be8ef 100644 --- a/.github/workflows/sharness.yml +++ b/.github/workflows/sharness.yml @@ -88,7 +88,7 @@ jobs: destination: sharness.html - name: Upload one-page HTML report if: github.repository != 'ipfs/kubo' && (failure() || success()) - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: sharness.html path: kubo/test/sharness/test-results/sharness.html @@ -108,7 +108,7 @@ jobs: destination: sharness-html/ - name: Upload full HTML report if: github.repository != 'ipfs/kubo' && (failure() || success()) - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: sharness-html path: kubo/test/sharness/test-results/sharness-html diff --git a/.github/workflows/test-migrations.yml b/.github/workflows/test-migrations.yml index 0d30bd357..35fcbe729 100644 --- a/.github/workflows/test-migrations.yml +++ b/.github/workflows/test-migrations.yml @@ -77,7 +77,7 @@ jobs: - name: Upload test results if: always() - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: ${{ matrix.os }}-test-results path: | From 584025bb6952d837829c0d89934fef87c2428115 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 7 Jan 2026 20:33:16 +0100 Subject: [PATCH 10/49] docs: improve README for first-time users (#11133) * docs: improve README for first-time users - add Quick Taste section with real CIDv1 example near top - rewrite "What is Kubo?" with technical concepts (CIDs, DAGs, UnixFS, Bitswap) - reorder features to follow user journey (CLI before advanced HTTP features) - streamline install section with links to docs.ipfs.tech - organize package managers in tables with Repology version badges - add supply chain security warning for third-party packages - surface important docs (metrics, debug guide, customizing) - update maintainer info with Shipyard branding Closes #11125 Closes #7298 Closes #5471 Closes #5087 * docs(readme): add changelogs link and fix docs directory URL * docs(readme): add mDNS/DHT links and clarify build steps - link LAN discovery to mDNS spec and WAN to Amino DHT glossary - show both make build and make install with output paths --- README.md | 491 +++++++++++++----------------------------------------- 1 file changed, 120 insertions(+), 371 deletions(-) diff --git a/README.md b/README.md index fa8285253..c1eaf9748 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
Kubo logo
- Kubo: IPFS Implementation in GO + Kubo: IPFS Implementation in Go
@@ -11,106 +11,61 @@

Official Part of IPFS Project Discourse Forum - Matrix + Matrix GitHub release


+

+What is Kubo? | Quick Taste | Install | Documentation | Development | Getting Help +

+ ## What is Kubo? -Kubo was the first IPFS implementation and is the most widely used one today. Implementing the *Interplanetary Filesystem* - the standard for content-addressing on the Web, interoperable with HTTP. Thus powered by future-proof data models and the libp2p for network communication. Kubo is written in Go. +Kubo was the first [IPFS](https://docs.ipfs.tech/concepts/what-is-ipfs/) implementation and is the [most widely used one today](https://probelab.io/ipfs/topology/#chart-agent-types-avg). It takes an opinionated approach to content-addressing ([CIDs](https://docs.ipfs.tech/concepts/glossary/#cid), [DAGs](https://docs.ipfs.tech/concepts/glossary/#dag)) that maximizes interoperability: [UnixFS](https://docs.ipfs.tech/concepts/glossary/#unixfs) for files and directories, [HTTP Gateways](https://docs.ipfs.tech/concepts/glossary/#gateway) for web browsers, [Bitswap](https://docs.ipfs.tech/concepts/glossary/#bitswap) and [HTTP](https://specs.ipfs.tech/http-gateways/trustless-gateway/) for verifiable data transfer. -Featureset -- Runs an IPFS-Node as a network service that is part of LAN and WAN DHT -- Native support for UnixFS (most popular way to represent files and directories on IPFS) -- [HTTP Gateway](https://specs.ipfs.tech/http-gateways/) (`/ipfs` and `/ipns`) functionality for trusted and [trustless](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval) content retrieval -- [HTTP Routing V1](https://specs.ipfs.tech/routing/http-routing-v1/) (`/routing/v1`) client and server implementation for [delegated routing](./docs/delegated-routing.md) lookups -- [HTTP Kubo RPC API](https://docs.ipfs.tech/reference/kubo/rpc/) (`/api/v0`) to access and control the daemon -- [Command Line Interface](https://docs.ipfs.tech/reference/kubo/cli/) based on (`/api/v0`) RPC API -- [WebUI](https://github.com/ipfs/ipfs-webui/#readme) to manage the Kubo node -- [Content blocking](/docs/content-blocking.md) support for operators of public nodes +**Features:** -### Other implementations +- Runs an IPFS node as a network service (LAN [mDNS](https://github.com/libp2p/specs/blob/master/discovery/mdns.md) and WAN [Amino DHT](https://docs.ipfs.tech/concepts/glossary/#dht)) +- [Command-line interface](https://docs.ipfs.tech/reference/kubo/cli/) (`ipfs --help`) +- [WebUI](https://github.com/ipfs/ipfs-webui/#readme) for node management +- [HTTP Gateway](https://specs.ipfs.tech/http-gateways/) for trusted and [trustless](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval) content retrieval +- [HTTP RPC API](https://docs.ipfs.tech/reference/kubo/rpc/) to control the daemon +- [HTTP Routing V1](https://specs.ipfs.tech/routing/http-routing-v1/) client and server for [delegated routing](./docs/delegated-routing.md) +- [Content blocking](./docs/content-blocking.md) for public node operators -See [List](https://docs.ipfs.tech/basics/ipfs-implementations/) +**Other IPFS implementations:** [Helia](https://github.com/ipfs/helia) (JavaScript), [more...](https://docs.ipfs.tech/concepts/ipfs-implementations/) -## What is IPFS? +## Quick Taste -IPFS is a global, versioned, peer-to-peer filesystem. It combines good ideas from previous systems such as Git, BitTorrent, Kademlia, SFS, and the Web. It is like a single BitTorrent swarm, exchanging git objects. IPFS provides an interface as simple as the HTTP web, but with permanence built-in. You can also mount the world at /ipfs. +After [installing Kubo](#install), verify it works: -For more info see: https://docs.ipfs.tech/concepts/what-is-ipfs/ +```console +$ ipfs init +generating ED25519 keypair...done +peer identity: 12D3KooWGcSLQdLDBi2BvoP8WnpdHvhWPbxpGcqkf93rL2XMZK7R -Before opening an issue, consider using one of the following locations to ensure you are opening your thread in the right place: - - kubo (previously named go-ipfs) _implementation_ bugs in [this repo](https://github.com/ipfs/kubo/issues). - - Documentation issues in [ipfs/docs issues](https://github.com/ipfs/ipfs-docs/issues). - - IPFS _design_ in [ipfs/specs issues](https://github.com/ipfs/specs/issues). - - Exploration of new ideas in [ipfs/notes issues](https://github.com/ipfs/notes/issues). - - Ask questions and meet the rest of the community at the [IPFS Forum](https://discuss.ipfs.tech). - - Or [chat with us](https://docs.ipfs.tech/community/chat/). +$ ipfs daemon & +Daemon is ready -[![YouTube Channel Subscribers](https://img.shields.io/youtube/channel/subscribers/UCdjsUXJ3QawK4O5L1kqqsew?label=Subscribe%20IPFS&style=social&cacheSeconds=3600)](https://www.youtube.com/channel/UCdjsUXJ3QawK4O5L1kqqsew) [![Follow @IPFS on Twitter](https://img.shields.io/twitter/follow/IPFS?style=social&cacheSeconds=3600)](https://twitter.com/IPFS) +$ echo "hello IPFS" | ipfs add -q --cid-version 1 +bafkreicouv3sksjuzxb3rbb6rziy6duakk2aikegsmtqtz5rsuppjorxsa -## Next milestones +$ ipfs cat bafkreicouv3sksjuzxb3rbb6rziy6duakk2aikegsmtqtz5rsuppjorxsa +hello IPFS +``` -[Milestones on GitHub](https://github.com/ipfs/kubo/milestones) +Verify this CID is provided by your node to the IPFS network: - -## Table of Contents - -- [What is Kubo?](#what-is-kubo) -- [What is IPFS?](#what-is-ipfs) -- [Next milestones](#next-milestones) -- [Table of Contents](#table-of-contents) -- [Security Issues](#security-issues) -- [Install](#install) - - [Minimal System Requirements](#minimal-system-requirements) - - [Docker](#docker) - - [Official prebuilt binaries](#official-prebuilt-binaries) - - [Updating](#updating) - - [Downloading builds using IPFS](#downloading-builds-using-ipfs) - - [Unofficial Linux packages](#unofficial-linux-packages) - - [ArchLinux](#arch-linux) - - [Gentoo Linux](#gentoo-linux) - - [Nix](#nix) - - [Solus](#solus) - - [openSUSE](#opensuse) - - [Guix](#guix) - - [Snap](#snap) - - [Ubuntu PPA](#ubuntu-ppa) - - [Fedora](#fedora-copr) - - [Unofficial Windows packages](#unofficial-windows-packages) - - [Chocolatey](#chocolatey) - - [Scoop](#scoop) - - [Unofficial MacOS packages](#unofficial-macos-packages) - - [MacPorts](#macports) - - [Nix](#nix-macos) - - [Homebrew](#homebrew) - - [Build from Source](#build-from-source) - - [Install Go](#install-go) - - [Download and Compile IPFS](#download-and-compile-ipfs) - - [Cross Compiling](#cross-compiling) - - [Troubleshooting](#troubleshooting) -- [Getting Started](#getting-started) - - [Usage](#usage) - - [Some things to try](#some-things-to-try) - - [Troubleshooting](#troubleshooting-1) -- [Packages](#packages) -- [Development](#development) -- [Maintainer Info](#maintainer-info) -- [Contributing](#contributing) -- [License](#license) - -## Security Issues - -Please follow [`SECURITY.md`](SECURITY.md). +See `ipfs add --help` for all import options. Ready for more? Follow the [command-line quick start](https://docs.ipfs.tech/how-to/command-line-quick-start/). ## Install -The canonical download instructions for IPFS are over at: https://docs.ipfs.tech/install/. It is **highly recommended** you follow those instructions if you are not interested in working on IPFS development. +Follow the [official installation guide](https://docs.ipfs.tech/install/command-line/), or choose: [prebuilt binary](#official-prebuilt-binaries) | [Docker](#docker) | [package manager](#package-managers) | [from source](#build-from-source). -For production use, Release Docker images (below) are recommended. +Prefer a GUI? Try [IPFS Desktop](https://docs.ipfs.tech/install/ipfs-desktop/) and/or [IPFS Companion](https://docs.ipfs.tech/install/ipfs-companion/). ### Minimal System Requirements @@ -122,354 +77,148 @@ Kubo runs on most Linux, macOS, and Windows systems. For optimal performance, we > [!CAUTION] > Systems with less than the recommended memory may experience instability, frequent OOM errors or restarts, and missing data announcement (reprovider window), which can make data fully or partially inaccessible to other peers. Running Kubo on underprovisioned hardware is at your own risk. +### Official Prebuilt Binaries + +Download from https://dist.ipfs.tech#kubo or [GitHub Releases](https://github.com/ipfs/kubo/releases/latest). + ### Docker Official images are published at https://hub.docker.com/r/ipfs/kubo/: [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/ipfs/kubo?color=blue&label=kubo%20docker%20image&logo=docker&sort=semver&style=flat-square&cacheSeconds=3600)](https://hub.docker.com/r/ipfs/kubo/) #### ๐ŸŸข Release Images - - These are production grade images. Use them. - - `latest` and [`release`](https://hub.docker.com/r/ipfs/kubo/tags?name=release) tags always point at [the latest stable release](https://github.com/ipfs/kubo/releases/latest). If you use this, remember to `docker pull` periodically to update. - - [`vN.N.N`](https://hub.docker.com/r/ipfs/kubo/tags?name=v) points at a specific [release tag](https://github.com/ipfs/kubo/releases) -#### ๐ŸŸ  Developer Preview Images - - These tags are used by developers for internal testing, not intended for end users or production use. - - [`master-latest`](https://hub.docker.com/r/ipfs/kubo/tags?name=master-latest) always points at the `HEAD` of the [`master`](https://github.com/ipfs/kubo/commits/master/) branch - - [`master-YYYY-DD-MM-GITSHA`](https://hub.docker.com/r/ipfs/kubo/tags?name=master-2) points at a specific commit from the `master` branch +Use these for production deployments. -#### ๐Ÿ”ด Internal Staging Images - - We use `staging` for testing arbitrary commits and experimental patches. - - To build image for current HEAD, force push to `staging` via `git push origin HEAD:staging --force`) - - [`staging-latest`](https://hub.docker.com/r/ipfs/kubo/tags?name=staging-latest) always points at the `HEAD` of the [`staging`](https://github.com/ipfs/kubo/commits/staging/) branch - - [`staging-YYYY-DD-MM-GITSHA`](https://hub.docker.com/r/ipfs/kubo/tags?name=staging-2) points at a specific commit from the `staging` branch +- `latest` and [`release`](https://hub.docker.com/r/ipfs/kubo/tags?name=release) always point at [the latest stable release](https://github.com/ipfs/kubo/releases/latest) +- [`vN.N.N`](https://hub.docker.com/r/ipfs/kubo/tags?name=v) points at a specific [release tag](https://github.com/ipfs/kubo/releases) ```console $ docker pull ipfs/kubo:latest $ docker run --rm -it --net=host ipfs/kubo:latest ``` -To [customize your node](https://docs.ipfs.tech/install/run-ipfs-inside-docker/#customizing-your-node), -pass necessary config via `-e` or by mounting scripts in the `/container-init.d`. +To [customize your node](https://docs.ipfs.tech/install/run-ipfs-inside-docker/#customizing-your-node), pass config via `-e` or mount scripts in `/container-init.d`. -Learn more at https://docs.ipfs.tech/install/run-ipfs-inside-docker/ +#### ๐ŸŸ  Developer Preview Images -### Official prebuilt binaries +For internal testing, not intended for production. -The official binaries are published at https://dist.ipfs.tech#kubo: +- [`master-latest`](https://hub.docker.com/r/ipfs/kubo/tags?name=master-latest) points at `HEAD` of [`master`](https://github.com/ipfs/kubo/commits/master/) +- [`master-YYYY-DD-MM-GITSHA`](https://hub.docker.com/r/ipfs/kubo/tags?name=master-2) points at a specific commit -[![dist.ipfs.tech Downloads](https://img.shields.io/github/v/release/ipfs/kubo?label=dist.ipfs.tech&logo=ipfs&style=flat-square&cacheSeconds=3600)](https://dist.ipfs.tech#kubo) +#### ๐Ÿ”ด Internal Staging Images -From there: -- Click the blue "Download Kubo" on the right side of the page. -- Open/extract the archive. -- Move kubo (`ipfs`) to your path (`install.sh` can do it for you). +For testing arbitrary commits and experimental patches (force push to `staging` branch). -If you are unable to access [dist.ipfs.tech](https://dist.ipfs.tech#kubo), you can also download kubo from: -- this project's GitHub [releases](https://github.com/ipfs/kubo/releases/latest) page -- `/ipns/dist.ipfs.tech` at [dweb.link](https://dweb.link/ipns/dist.ipfs.tech#kubo) gateway - -#### Updating - -##### Downloading builds using IPFS - -List the available versions of Kubo implementation: - -```console -$ ipfs cat /ipns/dist.ipfs.tech/kubo/versions -``` - -Then, to view available builds for a version from the previous command (`$VERSION`): - -```console -$ ipfs ls /ipns/dist.ipfs.tech/kubo/$VERSION -``` - -To download a given build of a version: - -```console -$ ipfs get /ipns/dist.ipfs.tech/kubo/$VERSION/kubo_$VERSION_darwin-amd64.tar.gz # darwin amd64 build -$ ipfs get /ipns/dist.ipfs.tech/kubo/$VERSION/kubo_$VERSION_darwin-arm64.tar.gz # darwin arm64 build -$ ipfs get /ipns/dist.ipfs.tech/kubo/$VERSION/kubo_$VERSION_freebsd-amd64.tar.gz # freebsd amd64 build -$ ipfs get /ipns/dist.ipfs.tech/kubo/$VERSION/kubo_$VERSION_linux-amd64.tar.gz # linux amd64 build -$ ipfs get /ipns/dist.ipfs.tech/kubo/$VERSION/kubo_$VERSION_linux-riscv64.tar.gz # linux riscv64 build -$ ipfs get /ipns/dist.ipfs.tech/kubo/$VERSION/kubo_$VERSION_linux-arm64.tar.gz # linux arm64 build -$ ipfs get /ipns/dist.ipfs.tech/kubo/$VERSION/kubo_$VERSION_windows-amd64.zip # windows amd64 build -``` - -### Unofficial Linux packages - - - Packaging status - - -- [ArchLinux](#arch-linux) -- [Gentoo Linux](#gentoo-linux) -- [Nix](#nix-linux) -- [Solus](#solus) -- [openSUSE](#opensuse) -- [Guix](#guix) -- [Snap](#snap) -- [Ubuntu PPA](#ubuntu-ppa) -- [Fedora](#fedora-copr) - -#### Arch Linux - -[![kubo via Community Repo](https://img.shields.io/archlinux/v/community/x86_64/kubo?color=1793d1&label=kubo&logo=arch-linux&style=flat-square&cacheSeconds=3600)](https://wiki.archlinux.org/title/IPFS) - -```bash -# pacman -S kubo -``` - -[![kubo-git via AUR](https://img.shields.io/static/v1?label=kubo-git&message=latest%40master&color=1793d1&logo=arch-linux&style=flat-square&cacheSeconds=3600)](https://archlinux.org/packages/kubo/) - -#### Gentoo Linux - -https://wiki.gentoo.org/wiki/Kubo - -```bash -# emerge -a net-p2p/kubo -``` - -https://packages.gentoo.org/packages/net-p2p/kubo - -#### Nix - -With the purely functional package manager [Nix](https://nixos.org/nix/) you can install kubo like this: - -``` -$ nix-env -i kubo -``` - -You can also install the Package by using its attribute name, which is also `kubo`. - -#### Solus - -[Package for Solus](https://dev.getsol.us/source/kubo/repository/master/) - -``` -$ sudo eopkg install kubo -``` - -You can also install it through the Solus software center. - -#### openSUSE - -[Community Package for kubo](https://software.opensuse.org/package/kubo) - -#### Guix - -[Community Package for kubo](https://packages.guix.gnu.org/search/?query=kubo) is available. - -#### Snap - -No longer supported, see rationale in [kubo#8688](https://github.com/ipfs/kubo/issues/8688). - -#### Ubuntu PPA - -[PPA homepage](https://launchpad.net/~twdragon/+archive/ubuntu/ipfs) on Launchpad. - -##### Latest Ubuntu (>= 20.04 LTS) -```sh -sudo add-apt-repository ppa:twdragon/ipfs -sudo apt update -sudo apt install ipfs-kubo -``` - -### Fedora COPR - -[`taw00/ipfs-rpm`](https://github.com/taw00/ipfs-rpm) - -##### Any Ubuntu version - -```sh -sudo su -echo 'deb https://ppa.launchpadcontent.net/twdragon/ipfs/ubuntu <> main' >> /etc/apt/sources.list.d/ipfs -echo 'deb-src https://ppa.launchpadcontent.net/twdragon/ipfs/ubuntu <> main' >> /etc/apt/sources.list.d/ipfs -exit -sudo apt update -sudo apt install ipfs-kubo -``` -where `<>` is the codename of your Ubuntu distribution (for example, `jammy` for 22.04 LTS). During the first installation the package maintenance script may automatically ask you about which networking profile, CPU accounting model, and/or existing node configuration file you want to use. - -**NOTE**: this method also may work with any compatible Debian-based distro which has `libc6` inside, and APT as a package manager. - -### Unofficial Windows packages - -- [Chocolatey](#chocolatey) -- [Scoop](#scoop) - -#### Chocolatey - -No longer supported, see rationale in [kubo#9341](https://github.com/ipfs/kubo/issues/9341). - -#### Scoop - -Scoop provides kubo as `kubo` in its 'extras' bucket. - -```Powershell -PS> scoop bucket add extras -PS> scoop install kubo -``` - -### Unofficial macOS packages - -- [MacPorts](#macports) -- [Nix](#nix-macos) -- [Homebrew](#homebrew) - -#### MacPorts - -The package [ipfs](https://ports.macports.org/port/ipfs) currently points to kubo and is being maintained. - -``` -$ sudo port install ipfs -``` - -#### Nix - -In macOS you can use the purely functional package manager [Nix](https://nixos.org/nix/): - -``` -$ nix-env -i kubo -``` - -You can also install the Package by using its attribute name, which is also `kubo`. - -#### Homebrew - -A Homebrew formula [ipfs](https://formulae.brew.sh/formula/ipfs) is maintained too. - -``` -$ brew install --formula ipfs -``` +- [`staging-latest`](https://hub.docker.com/r/ipfs/kubo/tags?name=staging-latest) points at `HEAD` of [`staging`](https://github.com/ipfs/kubo/commits/staging/) +- [`staging-YYYY-DD-MM-GITSHA`](https://hub.docker.com/r/ipfs/kubo/tags?name=staging-2) points at a specific commit ### Build from Source ![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/ipfs/kubo?label=Requires%20Go&logo=go&style=flat-square&cacheSeconds=3600) -kubo's build system requires Go and some standard POSIX build tools: - -* GNU make -* Git -* GCC (or some other go compatible C Compiler) (optional) - -To build without GCC, build with `CGO_ENABLED=0` (e.g., `make build CGO_ENABLED=0`). - -#### Install Go - -![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/ipfs/kubo?label=Requires%20Go&logo=go&style=flat-square&cacheSeconds=3600) - -If you need to update: [Download latest version of Go](https://golang.org/dl/). - -You'll need to add Go's bin directories to your `$PATH` environment variable e.g., by adding these lines to your `/etc/profile` (for a system-wide installation) or `$HOME/.profile`: - -``` -export PATH=$PATH:/usr/local/go/bin -export PATH=$PATH:$GOPATH/bin +```bash +git clone https://github.com/ipfs/kubo.git +cd kubo +make build # creates cmd/ipfs/ipfs +make install # installs to $GOPATH/bin/ipfs ``` -(If you run into trouble, see the [Go install instructions](https://golang.org/doc/install)). +See the [Developer Guide](docs/developer-guide.md) for details, Windows instructions, and troubleshooting. -#### Download and Compile IPFS +### Package Managers -``` -$ git clone https://github.com/ipfs/kubo.git +Kubo is available in community-maintained packages across many operating systems, Linux distributions, and package managers. See [Repology](https://repology.org/project/kubo/versions) for the full list: [![Packaging status](https://repology.org/badge/tiny-repos/kubo.svg)](https://repology.org/project/kubo/versions) -$ cd kubo -$ make install -``` +> [!WARNING] +> These packages are maintained by third-party volunteers. The IPFS Project and Kubo maintainers are not responsible for their contents or supply chain security. For increased security, [build from source](#build-from-source). -Alternatively, you can run `make build` to build the kubo binary (storing it in `cmd/ipfs/ipfs`) without installing it. +#### Linux -**NOTE:** If you get an error along the lines of "fatal error: stdlib.h: No such file or directory", you're missing a C compiler. Either re-run `make` with `CGO_ENABLED=0` or install GCC. +| Distribution | Install | Version | +|--------------|---------|---------| +| Ubuntu | [PPA](https://launchpad.net/~twdragon/+archive/ubuntu/ipfs): `sudo apt install ipfs-kubo` | [![PPA: twdragon](https://img.shields.io/badge/PPA-twdragon-E95420?logo=ubuntu)](https://launchpad.net/~twdragon/+archive/ubuntu/ipfs) | +| Arch | `pacman -S kubo` | [![Arch package](https://repology.org/badge/version-for-repo/arch/kubo.svg)](https://archlinux.org/packages/extra/x86_64/kubo/) | +| Fedora | [COPR](https://copr.fedorainfracloud.org/coprs/taw/ipfs/): `dnf install kubo` | [![COPR: taw](https://img.shields.io/badge/COPR-taw-51A2DA?logo=fedora)](https://copr.fedorainfracloud.org/coprs/taw/ipfs/) | +| Nix | `nix-env -i kubo` | [![nixpkgs unstable](https://repology.org/badge/version-for-repo/nix_unstable/kubo.svg)](https://search.nixos.org/packages?query=kubo) | +| Gentoo | `emerge -a net-p2p/kubo` | [![Gentoo package](https://repology.org/badge/version-for-repo/gentoo/kubo.svg)](https://packages.gentoo.org/packages/net-p2p/kubo) | +| openSUSE | `zypper install kubo` | [![openSUSE Tumbleweed](https://repology.org/badge/version-for-repo/opensuse_tumbleweed/kubo.svg)](https://software.opensuse.org/package/kubo) | +| Solus | `sudo eopkg install kubo` | [![Solus package](https://repology.org/badge/version-for-repo/solus/kubo.svg)](https://packages.getsol.us/shannon/k/kubo/) | +| Guix | `guix install kubo` | [![Guix package](https://repology.org/badge/version-for-repo/gnuguix/kubo.svg)](https://packages.guix.gnu.org/packages/kubo/) | +| _other_ | [See Repology for the full list](https://repology.org/project/kubo/versions) | | -##### Cross Compiling +~~Snap~~ no longer supported ([#8688](https://github.com/ipfs/kubo/issues/8688)) -Compiling for a different platform is as simple as running: +#### macOS -``` -make build GOOS=myTargetOS GOARCH=myTargetArchitecture -``` +| Manager | Install | Version | +|---------|---------|---------| +| Homebrew | `brew install ipfs` | [![Homebrew](https://repology.org/badge/version-for-repo/homebrew/kubo.svg)](https://formulae.brew.sh/formula/ipfs) | +| MacPorts | `sudo port install ipfs` | [![MacPorts](https://repology.org/badge/version-for-repo/macports/kubo.svg)](https://ports.macports.org/port/ipfs/) | +| Nix | `nix-env -i kubo` | [![nixpkgs unstable](https://repology.org/badge/version-for-repo/nix_unstable/kubo.svg)](https://search.nixos.org/packages?query=kubo) | +| _other_ | [See Repology for the full list](https://repology.org/project/kubo/versions) | | -#### Troubleshooting +#### Windows -- Separate [instructions are available for building on Windows](docs/windows.md). -- `git` is required in order for `go get` to fetch all dependencies. -- Package managers often contain out-of-date `golang` packages. - Ensure that `go version` reports the minimum version required (see go.mod). See above for how to install go. -- If you are interested in development, please install the development -dependencies as well. -- Shell command completions can be generated with one of the `ipfs commands completion` subcommands. Read [docs/command-completion.md](docs/command-completion.md) to learn more. -- See the [misc folder](https://github.com/ipfs/kubo/tree/master/misc) for how to connect IPFS to systemd or whatever init system your distro uses. +| Manager | Install | Version | +|---------|---------|---------| +| Scoop | `scoop install kubo` | [![Scoop](https://repology.org/badge/version-for-repo/scoop/kubo.svg)](https://scoop.sh/#/apps?q=kubo) | +| _other_ | [See Repology for the full list](https://repology.org/project/kubo/versions) | | -## Getting Started +~~Chocolatey~~ no longer supported ([#9341](https://github.com/ipfs/kubo/issues/9341)) -### Usage +## Documentation -[![docs: Command-line quick start](https://img.shields.io/static/v1?label=docs&message=Command-line%20quick%20start&color=blue&style=flat-square&cacheSeconds=3600)](https://docs.ipfs.tech/how-to/command-line-quick-start/) -[![docs: Command-line reference](https://img.shields.io/static/v1?label=docs&message=Command-line%20reference&color=blue&style=flat-square&cacheSeconds=3600)](https://docs.ipfs.tech/reference/kubo/cli/) - -To start using IPFS, you must first initialize IPFS's config files on your -system, this is done with `ipfs init`. See `ipfs init --help` for information on -the optional arguments it takes. After initialization is complete, you can use -`ipfs mount`, `ipfs add` and any of the other commands to explore! - -For detailed configuration options, see [docs/config.md](https://github.com/ipfs/kubo/blob/master/docs/config.md). - -### Some things to try - -Basic proof of 'ipfs working' locally: - - echo "hello world" > hello - ipfs add hello - # This should output a hash string that looks something like: - # QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o - ipfs cat - -### HTTP/RPC clients - -For programmatic interaction with Kubo, see our [list of HTTP/RPC clients](docs/http-rpc-clients.md). - -### Troubleshooting - -If you have previously installed IPFS before and you are running into problems getting a newer version to work, try deleting (or backing up somewhere else) your IPFS config directory (~/.ipfs by default) and rerunning `ipfs init`. This will reinitialize the config file to its defaults and clear out the local datastore of any bad entries. - -For more information about configuration options, see [docs/config.md](https://github.com/ipfs/kubo/blob/master/docs/config.md). - -Please direct general questions and help requests to our [forums](https://discuss.ipfs.tech). - -If you believe you've found a bug, check the [issues list](https://github.com/ipfs/kubo/issues) and, if you don't see your problem there, either come talk to us on [Matrix chat](https://docs.ipfs.tech/community/chat/), or file an issue of your own! - -## Packages - -See [IPFS in GO](https://docs.ipfs.tech/reference/go/api/) documentation. +| Topic | Description | +|-------|-------------| +| [Configuration](docs/config.md) | All config options reference | +| [Environment variables](docs/environment-variables.md) | Runtime settings via env vars | +| [Experimental features](docs/experimental-features.md) | Opt-in features in development | +| [HTTP Gateway](docs/gateway.md) | Path, subdomain, and trustless gateway setup | +| [HTTP RPC clients](docs/http-rpc-clients.md) | Client libraries for Go, JS | +| [Delegated routing](docs/delegated-routing.md) | Multi-router and HTTP routing | +| [Metrics & monitoring](docs/metrics.md) | Prometheus metrics | +| [Content blocking](docs/content-blocking.md) | Denylist for public nodes | +| [Customizing](docs/customizing.md) | Unsure if use Plugins, Boxo, or fork? | +| [Debug guide](docs/debug-guide.md) | CPU profiles, memory analysis, tracing | +| [Changelogs](docs/changelogs/) | Release notes for each version | +| [All documentation](https://github.com/ipfs/kubo/tree/master/docs) | Full list of docs | ## Development -See the [Developer Guide](docs/developer-guide.md) for build instructions, testing, architecture, and contribution workflow. +See the [Developer Guide](docs/developer-guide.md) for build instructions, testing, and contribution workflow. -## Maintainer Info +## Getting Help -Kubo is maintained by [Shipyard](https://ipshipyard.com/). +- [IPFS Forum](https://discuss.ipfs.tech) - community support, questions, and discussion +- [Community](https://docs.ipfs.tech/community/) - chat, events, and working groups +- [GitHub Issues](https://github.com/ipfs/kubo/issues) - bug reports for Kubo specifically +- [IPFS Docs Issues](https://github.com/ipfs/ipfs-docs/issues) - documentation issues -* This repository is part of [Shipyard's GO Triage triage](https://ipshipyard.notion.site/IPFS-Go-Triage-Boxo-Kubo-Rainbow-0ddee6b7f28d412da7dabe4f9107c29a). -* [Release Process](https://ipshipyard.notion.site/Kubo-Release-Process-6dba4f5755c9458ab5685eeb28173778) +## Security Issues +See [`SECURITY.md`](SECURITY.md). ## Contributing [![](https://cdn.rawgit.com/jbenet/contribute-ipfs-gif/master/img/contribute.gif)](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) -We โค๏ธ all [our contributors](docs/AUTHORS); this project wouldnโ€™t be what it is without you! If you want to help out, please see [CONTRIBUTING.md](CONTRIBUTING.md). +We welcome contributions. See [CONTRIBUTING.md](CONTRIBUTING.md) and the [Developer Guide](docs/developer-guide.md). -This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). +This repository follows the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md). -Members of IPFS community provide Kubo support on [discussion forum category here](https://discuss.ipfs.tech/c/help/help-kubo/23). +## Maintainer Info -Need help with IPFS itself? Learn where to get help and support at https://ipfs.tech/help. + + +> [!NOTE] +> Kubo is maintained by the [Shipyard](https://ipshipyard.com/) team. +> +> [Release Process](https://ipshipyard.notion.site/Kubo-Release-Process-6dba4f5755c9458ab5685eeb28173778) ## License -This project is dual-licensed under Apache 2.0 and MIT terms: +Dual-licensed under Apache 2.0 and MIT: -- Apache License, Version 2.0, ([LICENSE-APACHE](https://github.com/ipfs/kubo/blob/master/LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) -- MIT license ([LICENSE-MIT](https://github.com/ipfs/kubo/blob/master/LICENSE-MIT) or http://opensource.org/licenses/MIT) +- [LICENSE-APACHE](LICENSE-APACHE) +- [LICENSE-MIT](LICENSE-MIT) From 55b94751cc068939092be360446cf52f95c44d93 Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Wed, 7 Jan 2026 13:30:15 -0800 Subject: [PATCH 11/49] test: replace `go-clock` with `testing/synctest` (#11131) Use testing/synctest instead of go-clock for artificial time control. --- core/node/libp2p/rcmgr.go | 2 - core/node/libp2p/rcmgr_logging.go | 4 +- core/node/libp2p/rcmgr_logging_test.go | 89 +++++++++++++------------- go.mod | 2 +- 4 files changed, 47 insertions(+), 50 deletions(-) diff --git a/core/node/libp2p/rcmgr.go b/core/node/libp2p/rcmgr.go index 91a19bc2e..6844757f9 100644 --- a/core/node/libp2p/rcmgr.go +++ b/core/node/libp2p/rcmgr.go @@ -12,7 +12,6 @@ import ( "github.com/ipfs/kubo/core/node/helpers" "github.com/ipfs/kubo/repo" - "github.com/filecoin-project/go-clock" logging "github.com/ipfs/go-log/v2" "github.com/libp2p/go-libp2p" "github.com/libp2p/go-libp2p/core/network" @@ -112,7 +111,6 @@ filled in with autocomputed defaults.`) return nil, opts, fmt.Errorf("creating libp2p resource manager: %w", err) } lrm := &loggingResourceManager{ - clock: clock.New(), logger: &logging.Logger("resourcemanager").SugaredLogger, delegate: manager, } diff --git a/core/node/libp2p/rcmgr_logging.go b/core/node/libp2p/rcmgr_logging.go index 28188b0fc..72ee07668 100644 --- a/core/node/libp2p/rcmgr_logging.go +++ b/core/node/libp2p/rcmgr_logging.go @@ -7,7 +7,6 @@ import ( "sync" "time" - "github.com/filecoin-project/go-clock" "github.com/libp2p/go-libp2p/core/network" "github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/protocol" @@ -17,7 +16,6 @@ import ( ) type loggingResourceManager struct { - clock clock.Clock logger *zap.SugaredLogger delegate network.ResourceManager logInterval time.Duration @@ -42,7 +40,7 @@ func (n *loggingResourceManager) start(ctx context.Context) { if logInterval == 0 { logInterval = 10 * time.Second } - ticker := n.clock.Ticker(logInterval) + ticker := time.NewTicker(logInterval) go func() { defer ticker.Stop() for { diff --git a/core/node/libp2p/rcmgr_logging_test.go b/core/node/libp2p/rcmgr_logging_test.go index a49891829..1cc83eb34 100644 --- a/core/node/libp2p/rcmgr_logging_test.go +++ b/core/node/libp2p/rcmgr_logging_test.go @@ -2,9 +2,9 @@ package libp2p import ( "testing" + "testing/synctest" "time" - "github.com/filecoin-project/go-clock" "github.com/libp2p/go-libp2p/core/network" rcmgr "github.com/libp2p/go-libp2p/p2p/host/resource-manager" ma "github.com/multiformats/go-multiaddr" @@ -14,48 +14,49 @@ import ( ) func TestLoggingResourceManager(t *testing.T) { - clock := clock.NewMock() - orig := rcmgr.DefaultLimits.AutoScale() - limits := orig.ToPartialLimitConfig() - limits.System.Conns = 1 - limits.System.ConnsInbound = 1 - limits.System.ConnsOutbound = 1 - limiter := rcmgr.NewFixedLimiter(limits.Build(orig)) - rm, err := rcmgr.NewResourceManager(limiter) - if err != nil { - t.Fatal(err) - } - - oCore, oLogs := observer.New(zap.WarnLevel) - oLogger := zap.New(oCore) - lrm := &loggingResourceManager{ - clock: clock, - logger: oLogger.Sugar(), - delegate: rm, - logInterval: 1 * time.Second, - } - - // 2 of these should result in resource limit exceeded errors and subsequent log messages - for i := 0; i < 3; i++ { - _, _ = lrm.OpenConnection(network.DirInbound, false, ma.StringCast("/ip4/127.0.0.1/tcp/1234")) - } - - // run the logger which will write an entry for those errors - ctx := t.Context() - lrm.start(ctx) - clock.Add(3 * time.Second) - - timer := time.NewTimer(1 * time.Second) - for { - select { - case <-timer.C: - t.Fatalf("expected logs never arrived") - default: - if oLogs.Len() == 0 { - continue - } - require.Equal(t, "Protected from exceeding resource limits 2 times. libp2p message: \"system: cannot reserve inbound connection: resource limit exceeded\".", oLogs.All()[0].Message) - return + synctest.Test(t, func(t *testing.T) { + orig := rcmgr.DefaultLimits.AutoScale() + limits := orig.ToPartialLimitConfig() + limits.System.Conns = 1 + limits.System.ConnsInbound = 1 + limits.System.ConnsOutbound = 1 + limiter := rcmgr.NewFixedLimiter(limits.Build(orig)) + rm, err := rcmgr.NewResourceManager(limiter) + if err != nil { + t.Fatal(err) } - } + defer rm.Close() + + oCore, oLogs := observer.New(zap.WarnLevel) + oLogger := zap.New(oCore) + lrm := &loggingResourceManager{ + logger: oLogger.Sugar(), + delegate: rm, + logInterval: 1 * time.Second, + } + + // 2 of these should result in resource limit exceeded errors and subsequent log messages + for i := 0; i < 3; i++ { + _, _ = lrm.OpenConnection(network.DirInbound, false, ma.StringCast("/ip4/127.0.0.1/tcp/1234")) + } + + // run the logger which will write an entry for those errors + ctx := t.Context() + lrm.start(ctx) + time.Sleep(3 * time.Second) + + timer := time.NewTimer(1 * time.Second) + for { + select { + case <-timer.C: + t.Fatalf("expected logs never arrived") + default: + if oLogs.Len() == 0 { + continue + } + require.Equal(t, "Protected from exceeding resource limits 2 times. libp2p message: \"system: cannot reserve inbound connection: resource limit exceeded\".", oLogs.All()[0].Message) + return + } + } + }) } diff --git a/go.mod b/go.mod index 4ef10f87b..5e653e9de 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 - github.com/filecoin-project/go-clock v0.1.0 github.com/fsnotify/fsnotify v1.9.0 github.com/google/uuid v1.6.0 github.com/hashicorp/go-version v1.7.0 @@ -126,6 +125,7 @@ require ( github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/fatih/color v1.15.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/filecoin-project/go-clock v0.1.0 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/gammazero/chanqueue v1.1.1 // indirect From 1301710a911e146c32456f75fc509b2663ec753e Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Thu, 8 Jan 2026 05:07:08 +0100 Subject: [PATCH 12/49] fix(ci): parallelize gotest, cleanup output, flakiness (#11113) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ci: parallelize gotest by separating test/cli into own job split the Go Test workflow into two parallel jobs: - `unit-tests`: runs unit tests (excluding test/cli) - `cli-tests`: runs test/cli end-to-end tests test/cli takes ~3 minutes (~50% of total gotest time), so running it in parallel should reduce wall-clock CI time by ~1.5-2.5 minutes. both jobs produce JUnit XML and HTML reports for consistent debugging. * ci(gotest): reduce noise on test timeout panics add GOTRACEBACK=single to show only one goroutine stack instead of all when a test timeout panic occurs. this makes CI output much cleaner when tests hang. * fix(ci): prevent stderr from corrupting test JSON output - remove 2>&1 which mixed "go: downloading" stderr messages into JSON - add JSON validation before parsing - print failed test names for easier debugging * ci(gotest): use gotestsum for human-readable test output - replace per-package coverage loop with single gotestsum invocation - both unit-tests and cli-tests now show human-readable output - simplified coverage collection (single -coverprofile, no gocovmerge) - clarified step names to indicate they run tests * ci: fix codecov uploads by adding token - add CODECOV_TOKEN to gotest.yml and sharness.yml - update codecov-action to v5.5.2 - add fail_ci_if_error: false for robustness codecov stopped receiving coverage data ~1 year ago when they started requiring tokens for public repos * refactor(make): add test_unit and test_cli targets - add `make test_unit` for unit tests with coverage (used by CI) - add `make test_cli` for CLI integration tests (used by CI) - only disable colors when CI env var is set (local dev gets colors) - remove legacy targets: test_go_test, test_go_short, test_go_race, test_go_expensive - update gotest.yml to use make targets instead of inline commands - add test artifacts to .gitignore * fix(ci): move client/rpc tests to cli-tests job client/rpc tests use test/cli/harness which requires the ipfs binary. Move them from test_unit to test_cli where the binary is built. also: - update gotestsum to v1.13.0 - simplify workflow step names * fix(ci): use build tags when listing test packages go list needs build tags to properly exclude packages like fuse/mfs when running with TEST_FUSE=0 (nofuse tag). * fix(ci): move test/integration to cli-tests job test/integration tests need the ipfs binary, move them from test_unit to test_cli. * fix(test): fix flaky kubo-as-a-library and GetClosestPeers tests kubo-as-a-library: use `Bootstrap()` instead of raw `Swarm().Connect()` to fix race condition between swarm connection and bitswap peer discovery. `Bootstrap()` properly integrates peers into the routing system, ensuring bitswap learns about connected peers synchronously. GetClosestPeers: simplify retry logic using `EventuallyWithT` with 10-minute timeout. tests all 4 routing types (`auto`, `autoclient`, `dht`, `dhtclient`) against real bootstrap peers with patient polling. * fix(example): use bidirectional Swarm().Connect() for reliable bitswap - connect nodes bidirectionally (Aโ†’B and Bโ†’A) to simulate mutual peering - mutual peering protects connection from resource manager culling - use port 0 for random available ports (avoids CI conflicts) - enable LoopbackAddressesOnLanDHT for local testing - move retry logic to test file using require.Eventually * fix(ci): add test_examples target and parallel example-tests job - add `make test_examples` target to mk/golang.mk for consistency with test_unit/test_cli - move example tests to separate parallel CI job (example-tests) - example: use Bootstrap() with autoconf.FallbackBootstrapPeers for reliable bitswap - example: increase context timeout to 10 minutes - test: add 60s per-request timeout to GetClosestPeers (server has 30s routing timeout) - test: reduce EventuallyWithT to 3 minutes (locally passes in under 1 minute) * fix(ci): improve test targets, exclusion patterns, and artifact naming - define COVERPKG_EXCLUDE and UNIT_EXCLUDE as documented variables - use grep -vE with single regex instead of multiple grep -v calls - add mkdir -p before rm to ensure directories exist - add DEPS_GO dependency to test_cli target - make CLI test timeout configurable via TEST_CLI_TIMEOUT (default 10m) - fix test_examples cleanup on failure using subshell - reduce GetClosestPeers test wait time from 3m to 2m - rename artifacts to match job names: unit-tests-{junit,html}, cli-tests-{junit,html} - update cli-tests upload-artifact from v5 to v6 * fix(ci): fix unit test exclusion and speed up example test - fix UNIT_EXCLUDE regex to match client/rpc at end of path - remove public bootstrap peers from example (only connect to nodeA) - example test now runs in ~3s instead of timing out * fix(test): fix flaky TestAddMultipleGCLive race condition added time.Sleep after spawning GC goroutines to ensure they reach GCLock() before the test proceeds. without this, the adder's maybePauseForGC() might check GCRequested() before GC has even requested the lock, causing the lock to not be released and GC to block indefinitely. this matches the existing pattern in TestAddGCLive which already had this sleep. also replaced context.Background() with t.Context() in both TestAddMultipleGCLive and TestAddGCLive for proper test lifecycle management. * fix(example): use test harness settings for reliable CI the kubo-as-a-library example was flaky on CI. applied test-harness-like settings that match what transports_test.go uses: - TCP-only on 127.0.0.1 with random port (no QUIC/UDP) - explicitly disable non-TCP transports (QUIC, Relay, WebTransport, etc) - use NilRouterOption (no routing) since we connect peers directly - bitswap works with directly connected peers without DHT lookups - 2-minute context timeout - streaming output in test for debugging --- .github/workflows/gotest.yml | 124 +++++++++++++----- .github/workflows/sharness.yml | 2 + .gitignore | 5 + Rules.mk | 15 +-- core/coreunix/add_test.go | 29 ++-- coverage/Rules.mk | 27 +--- docs/examples/kubo-as-a-library/main.go | 73 ++++++----- docs/examples/kubo-as-a-library/main_test.go | 34 +++-- mk/golang.mk | 63 +++++---- .../delegated_routing_v1_http_server_test.go | 63 ++++----- test/dependencies/go.mod | 2 +- test/dependencies/go.sum | 4 +- test/unit/Rules.mk | 3 +- 13 files changed, 262 insertions(+), 182 deletions(-) diff --git a/.github/workflows/gotest.yml b/.github/workflows/gotest.yml index 9f329cbfd..8165eb12a 100644 --- a/.github/workflows/gotest.yml +++ b/.github/workflows/gotest.yml @@ -14,11 +14,13 @@ concurrency: cancel-in-progress: true jobs: - go-test: + # Unit tests with coverage collection (uploaded to Codecov) + unit-tests: if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "2xlarge"]' || '"ubuntu-latest"') }} - timeout-minutes: 20 + timeout-minutes: 15 env: + GOTRACEBACK: single # reduce noise on test timeout panics TEST_DOCKER: 0 TEST_FUSE: 0 TEST_VERBOSE: 1 @@ -36,12 +38,9 @@ jobs: go-version-file: 'go.mod' - name: Install missing tools run: sudo apt update && sudo apt install -y zsh - - name: ๐Ÿ‘‰๏ธ If this step failed, go to ยซSummaryยป (top left) โ†’ inspect the ยซFailures/Errorsยป table - env: - # increasing parallelism beyond 2 doesn't speed up the tests much - PARALLEL: 2 + - name: Run unit tests run: | - make -j "$PARALLEL" test/unit/gotest.junit.xml && + make test_unit && [[ ! $(jq -s -c 'map(select(.Action == "fail")) | .[]' test/unit/gotest.json) ]] - name: Upload coverage to Codecov uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 @@ -49,28 +48,8 @@ jobs: with: name: unittests files: coverage/unit_tests.coverprofile - - name: Test kubo-as-a-library example - run: | - # we want to first test with the kubo version in the go.mod file - go test -v ./... - - # we also want to test the examples against the current version of kubo - # however, that version might be in a fork so we need to replace the dependency - - # backup the go.mod and go.sum files to restore them after we run the tests - cp go.mod go.mod.bak - cp go.sum go.sum.bak - - # make sure the examples run against the current version of kubo - go mod edit -replace github.com/ipfs/kubo=./../../.. - go mod tidy - - go test -v ./... - - # restore the go.mod and go.sum files to their original state - mv go.mod.bak go.mod - mv go.sum.bak go.sum - working-directory: docs/examples/kubo-as-a-library + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false - name: Create a proper JUnit XML report uses: ipdxco/gotest-json-to-junit-xml@v1 with: @@ -80,7 +59,7 @@ jobs: - name: Archive the JUnit XML report uses: actions/upload-artifact@v6 with: - name: unit + name: unit-tests-junit path: test/unit/gotest.junit.xml if: failure() || success() - name: Create a HTML report @@ -93,7 +72,7 @@ jobs: - name: Archive the HTML report uses: actions/upload-artifact@v6 with: - name: html + name: unit-tests-html path: test/unit/gotest.html if: failure() || success() - name: Create a Markdown report @@ -106,3 +85,86 @@ jobs: - name: Set the summary run: cat test/unit/gotest.md >> $GITHUB_STEP_SUMMARY if: failure() || success() + + # End-to-end integration/regression tests from test/cli + # (Go-based replacement for legacy test/sharness shell scripts) + cli-tests: + if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' + runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "2xlarge"]' || '"ubuntu-latest"') }} + timeout-minutes: 15 + env: + GOTRACEBACK: single # reduce noise on test timeout panics + TEST_VERBOSE: 1 + GIT_PAGER: cat + IPFS_CHECK_RCMGR_DEFAULTS: 1 + defaults: + run: + shell: bash + steps: + - name: Check out Kubo + uses: actions/checkout@v6 + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version-file: 'go.mod' + - name: Install missing tools + run: sudo apt update && sudo apt install -y zsh + - name: Run CLI tests + env: + IPFS_PATH: ${{ runner.temp }}/ipfs-test + run: make test_cli + - name: Create JUnit XML report + uses: ipdxco/gotest-json-to-junit-xml@v1 + with: + input: test/cli/cli-tests.json + output: test/cli/cli-tests.junit.xml + if: failure() || success() + - name: Archive JUnit XML report + uses: actions/upload-artifact@v6 + with: + name: cli-tests-junit + path: test/cli/cli-tests.junit.xml + if: failure() || success() + - name: Create HTML report + uses: ipdxco/junit-xml-to-html@v1 + with: + mode: no-frames + input: test/cli/cli-tests.junit.xml + output: test/cli/cli-tests.html + if: failure() || success() + - name: Archive HTML report + uses: actions/upload-artifact@v6 + with: + name: cli-tests-html + path: test/cli/cli-tests.html + if: failure() || success() + - name: Create Markdown report + uses: ipdxco/junit-xml-to-html@v1 + with: + mode: summary + input: test/cli/cli-tests.junit.xml + output: test/cli/cli-tests.md + if: failure() || success() + - name: Set summary + run: cat test/cli/cli-tests.md >> $GITHUB_STEP_SUMMARY + if: failure() || success() + + # Example tests (kubo-as-a-library) + example-tests: + if: github.repository == 'ipfs/kubo' || github.event_name == 'workflow_dispatch' + runs-on: ${{ fromJSON(github.repository == 'ipfs/kubo' && '["self-hosted", "linux", "x64", "2xlarge"]' || '"ubuntu-latest"') }} + timeout-minutes: 5 + env: + GOTRACEBACK: single + defaults: + run: + shell: bash + steps: + - name: Check out Kubo + uses: actions/checkout@v6 + - name: Set up Go + uses: actions/setup-go@v6 + with: + go-version-file: 'go.mod' + - name: Run example tests + run: make test_examples diff --git a/.github/workflows/sharness.yml b/.github/workflows/sharness.yml index 7275be8ef..ac32bf3a4 100644 --- a/.github/workflows/sharness.yml +++ b/.github/workflows/sharness.yml @@ -60,6 +60,8 @@ jobs: with: name: sharness files: kubo/coverage/sharness_tests.coverprofile + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false - name: Aggregate results run: find kubo/test/sharness/test-results -name 't*-*.sh.*.counts' | kubo/test/sharness/lib/sharness/aggregate-results.sh > kubo/test/sharness/test-results/summary.txt - name: ๐Ÿ‘‰๏ธ If this step failed, go to ยซSummaryยป (top left) โ†’ ยซHTML Reportยป โ†’ inspect the ยซFailuresยป column diff --git a/.gitignore b/.gitignore index cb147456b..890870a6e 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,11 @@ go-ipfs-source.tar.gz docs/examples/go-ipfs-as-a-library/example-folder/Qm* /test/sharness/t0054-dag-car-import-export-data/*.car +# test artifacts from make test_unit / test_cli +/test/unit/gotest.json +/test/unit/gotest.junit.xml +/test/cli/cli-tests.json + # ignore build output from snapcraft /ipfs_*.snap /parts diff --git a/Rules.mk b/Rules.mk index d8f16ada8..b04e3d73e 100644 --- a/Rules.mk +++ b/Rules.mk @@ -134,15 +134,14 @@ help: @echo '' @echo 'TESTING TARGETS:' @echo '' - @echo ' test - Run all tests' - @echo ' test_short - Run short go tests and short sharness tests' - @echo ' test_go_short - Run short go tests' - @echo ' test_go_test - Run all go tests' + @echo ' test - Run all tests (test_go_fmt, test_unit, test_cli, test_sharness)' + @echo ' test_short - Run fast tests (test_go_fmt, test_unit)' + @echo ' test_unit - Run unit tests with coverage (excludes test/cli)' + @echo ' test_cli - Run CLI integration tests (requires built binary)' + @echo ' test_go_fmt - Check Go source formatting' @echo ' test_go_build - Build kubo for all platforms from .github/build-platforms.yml' - @echo ' test_go_expensive - Run all go tests and build all platforms' - @echo ' test_go_race - Run go tests with the race detector enabled' - @echo ' test_go_lint - Run the `golangci-lint` vetting tool' + @echo ' test_go_lint - Run golangci-lint' @echo ' test_sharness - Run sharness tests' - @echo ' coverage - Collects coverage info from unit tests and sharness' + @echo ' coverage - Collect coverage info from unit tests and sharness' @echo .PHONY: help diff --git a/core/coreunix/add_test.go b/core/coreunix/add_test.go index a11dd13e4..d5b06176a 100644 --- a/core/coreunix/add_test.go +++ b/core/coreunix/add_test.go @@ -30,6 +30,7 @@ import ( const testPeerID = "QmTFauExutTsy4XP6JbMFcw2Wa9645HJt2bTqL6qYDCKfe" func TestAddMultipleGCLive(t *testing.T) { + ctx := t.Context() r := &repo.Mock{ C: config.Config{ Identity: config.Identity{ @@ -38,13 +39,13 @@ func TestAddMultipleGCLive(t *testing.T) { }, D: syncds.MutexWrap(datastore.NewMapDatastore()), } - node, err := core.NewNode(context.Background(), &core.BuildCfg{Repo: r}) + node, err := core.NewNode(ctx, &core.BuildCfg{Repo: r}) if err != nil { t.Fatal(err) } out := make(chan interface{}, 10) - adder, err := NewAdder(context.Background(), node.Pinning, node.Blockstore, node.DAG) + adder, err := NewAdder(ctx, node.Pinning, node.Blockstore, node.DAG) if err != nil { t.Fatal(err) } @@ -67,7 +68,7 @@ func TestAddMultipleGCLive(t *testing.T) { go func() { defer close(out) - _, _ = adder.AddAllAndPin(context.Background(), slf) + _, _ = adder.AddAllAndPin(ctx, slf) // Ignore errors for clarity - the real bug would be gc'ing files while adding them, not this resultant error }() @@ -80,9 +81,12 @@ func TestAddMultipleGCLive(t *testing.T) { gc1started := make(chan struct{}) go func() { defer close(gc1started) - gc1out = gc.GC(context.Background(), node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) + gc1out = gc.GC(ctx, node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) }() + // Give GC goroutine time to reach GCLock (will block there waiting for adder) + time.Sleep(time.Millisecond * 100) + // GC shouldn't get the lock until after the file is completely added select { case <-gc1started: @@ -119,9 +123,12 @@ func TestAddMultipleGCLive(t *testing.T) { gc2started := make(chan struct{}) go func() { defer close(gc2started) - gc2out = gc.GC(context.Background(), node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) + gc2out = gc.GC(ctx, node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) }() + // Give GC goroutine time to reach GCLock + time.Sleep(time.Millisecond * 100) + select { case <-gc2started: t.Fatal("gc shouldn't have started yet") @@ -155,6 +162,7 @@ func TestAddMultipleGCLive(t *testing.T) { } func TestAddGCLive(t *testing.T) { + ctx := t.Context() r := &repo.Mock{ C: config.Config{ Identity: config.Identity{ @@ -163,13 +171,13 @@ func TestAddGCLive(t *testing.T) { }, D: syncds.MutexWrap(datastore.NewMapDatastore()), } - node, err := core.NewNode(context.Background(), &core.BuildCfg{Repo: r}) + node, err := core.NewNode(ctx, &core.BuildCfg{Repo: r}) if err != nil { t.Fatal(err) } out := make(chan interface{}) - adder, err := NewAdder(context.Background(), node.Pinning, node.Blockstore, node.DAG) + adder, err := NewAdder(ctx, node.Pinning, node.Blockstore, node.DAG) if err != nil { t.Fatal(err) } @@ -193,7 +201,7 @@ func TestAddGCLive(t *testing.T) { go func() { defer close(addDone) defer close(out) - _, err := adder.AddAllAndPin(context.Background(), slf) + _, err := adder.AddAllAndPin(ctx, slf) if err != nil { t.Error(err) } @@ -211,7 +219,7 @@ func TestAddGCLive(t *testing.T) { gcstarted := make(chan struct{}) go func() { defer close(gcstarted) - gcout = gc.GC(context.Background(), node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) + gcout = gc.GC(ctx, node.Blockstore, node.Repo.Datastore(), node.Pinning, nil) }() // gc shouldn't start until we let the add finish its current file. @@ -255,9 +263,6 @@ func TestAddGCLive(t *testing.T) { last = c } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - set := cid.NewSet() err = dag.Walk(ctx, dag.GetLinksWithDAG(node.DAG), last, set.Visit) if err != nil { diff --git a/coverage/Rules.mk b/coverage/Rules.mk index 48fce2856..84a4a1887 100644 --- a/coverage/Rules.mk +++ b/coverage/Rules.mk @@ -3,33 +3,14 @@ include mk/header.mk GOCC ?= go $(d)/coverage_deps: $$(DEPS_GO) cmd/ipfs/ipfs - rm -rf $(@D)/unitcover && mkdir $(@D)/unitcover rm -rf $(@D)/sharnesscover && mkdir $(@D)/sharnesscover -ifneq ($(IPFS_SKIP_COVER_BINS),1) -$(d)/coverage_deps: test/bin/gocovmerge -endif - .PHONY: $(d)/coverage_deps -# unit tests coverage -UTESTS_$(d) := $(shell $(GOCC) list -f '{{if (or (len .TestGoFiles) (len .XTestGoFiles))}}{{.ImportPath}}{{end}}' $(go-flags-with-tags) ./... | grep -v go-ipfs/vendor | grep -v go-ipfs/Godeps) +# unit tests coverage is now produced by test_unit target in mk/golang.mk +# (outputs coverage/unit_tests.coverprofile and test/unit/gotest.json) -UCOVER_$(d) := $(addsuffix .coverprofile,$(addprefix $(d)/unitcover/, $(subst /,_,$(UTESTS_$(d))))) - -$(UCOVER_$(d)): $(d)/coverage_deps ALWAYS - $(eval TMP_PKG := $(subst _,/,$(basename $(@F)))) - $(eval TMP_DEPS := $(shell $(GOCC) list -f '{{range .Deps}}{{.}} {{end}}' $(go-flags-with-tags) $(TMP_PKG) | sed 's/ /\n/g' | grep ipfs/go-ipfs) $(TMP_PKG)) - $(eval TMP_DEPS_LIST := $(call join-with,$(comma),$(TMP_DEPS))) - $(GOCC) test $(go-flags-with-tags) $(GOTFLAGS) -v -covermode=atomic -json -coverpkg=$(TMP_DEPS_LIST) -coverprofile=$@ $(TMP_PKG) | tee -a test/unit/gotest.json - - -$(d)/unit_tests.coverprofile: $(UCOVER_$(d)) - gocovmerge $^ > $@ - -TGTS_$(d) := $(d)/unit_tests.coverprofile - -.PHONY: $(d)/unit_tests.coverprofile +TGTS_$(d) := # sharness tests coverage $(d)/ipfs: GOTAGS += testrunmain @@ -46,7 +27,7 @@ endif export IPFS_COVER_DIR:= $(realpath $(d))/sharnesscover/ $(d)/sharness_tests.coverprofile: export TEST_PLUGIN=0 -$(d)/sharness_tests.coverprofile: $(d)/ipfs cmd/ipfs/ipfs-test-cover $(d)/coverage_deps test_sharness +$(d)/sharness_tests.coverprofile: $(d)/ipfs cmd/ipfs/ipfs-test-cover $(d)/coverage_deps test/bin/gocovmerge test_sharness (cd $(@D)/sharnesscover && find . -type f | gocovmerge -list -) > $@ diff --git a/docs/examples/kubo-as-a-library/main.go b/docs/examples/kubo-as-a-library/main.go index ffa86c7f0..8b2181ed7 100644 --- a/docs/examples/kubo-as-a-library/main.go +++ b/docs/examples/kubo-as-a-library/main.go @@ -47,7 +47,7 @@ func setupPlugins(externalPluginsPath string) error { return nil } -func createTempRepo(swarmPort int) (string, error) { +func createTempRepo() (string, error) { repoPath, err := os.MkdirTemp("", "ipfs-shell") if err != nil { return "", fmt.Errorf("failed to get temp dir: %s", err) @@ -59,15 +59,28 @@ func createTempRepo(swarmPort int) (string, error) { return "", err } - // Configure custom ports to avoid conflicts with other IPFS instances. - // This demonstrates how to customize the node's network addresses. + // Use TCP-only on loopback with random port for reliable local testing. + // This matches what kubo's test harness uses (test/cli/transports_test.go). + // QUIC/UDP transports are avoided because they may be throttled on CI. cfg.Addresses.Swarm = []string{ - fmt.Sprintf("/ip4/0.0.0.0/tcp/%d", swarmPort), - fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic-v1", swarmPort), - fmt.Sprintf("/ip4/0.0.0.0/udp/%d/quic-v1/webtransport", swarmPort), - fmt.Sprintf("/ip4/0.0.0.0/udp/%d/webrtc-direct", swarmPort), + "/ip4/127.0.0.1/tcp/0", } + // Explicitly disable non-TCP transports for reliability. + cfg.Swarm.Transports.Network.QUIC = config.False + cfg.Swarm.Transports.Network.Relay = config.False + cfg.Swarm.Transports.Network.WebTransport = config.False + cfg.Swarm.Transports.Network.WebRTCDirect = config.False + cfg.Swarm.Transports.Network.Websocket = config.False + cfg.AutoTLS.Enabled = config.False + + // Disable routing - we don't need DHT for direct peer connections. + // Bitswap works with directly connected peers without needing DHT lookups. + cfg.Routing.Type = config.NewOptionalString("none") + + // Disable bootstrap for this example - we manually connect only the peers we need. + cfg.Bootstrap = []string{} + // When creating the repository, you can define custom settings on the repository, such as enabling experimental // features (See experimental-features.md) or customizing the gateway endpoint. // To do such things, you should modify the variable `cfg`. For example: @@ -106,10 +119,14 @@ func createNode(ctx context.Context, repoPath string) (*core.IpfsNode, error) { // Construct the node nodeOptions := &core.BuildCfg{ - Online: true, - Routing: libp2p.DHTOption, // This option sets the node to be a full DHT node (both fetching and storing DHT Records) - // Routing: libp2p.DHTClientOption, // This option sets the node to be a client DHT node (only fetching records) - Repo: repo, + Online: true, + // For this example, we use NilRouterOption (no routing) since we connect peers directly. + // Bitswap works with directly connected peers without needing DHT lookups. + // In production, you would typically use: + // Routing: libp2p.DHTOption, // Full DHT node (stores and fetches records) + // Routing: libp2p.DHTClientOption, // DHT client (only fetches records) + Routing: libp2p.NilRouterOption, + Repo: repo, } return core.NewNode(ctx, nodeOptions) @@ -118,8 +135,7 @@ func createNode(ctx context.Context, repoPath string) (*core.IpfsNode, error) { var loadPluginsOnce sync.Once // Spawns a node to be used just for this run (i.e. creates a tmp repo). -// The swarmPort parameter specifies the port for libp2p swarm listeners. -func spawnEphemeral(ctx context.Context, swarmPort int) (icore.CoreAPI, *core.IpfsNode, error) { +func spawnEphemeral(ctx context.Context) (icore.CoreAPI, *core.IpfsNode, error) { var onceErr error loadPluginsOnce.Do(func() { onceErr = setupPlugins("") @@ -129,7 +145,7 @@ func spawnEphemeral(ctx context.Context, swarmPort int) (icore.CoreAPI, *core.Ip } // Create a Temporary Repo - repoPath, err := createTempRepo(swarmPort) + repoPath, err := createTempRepo() if err != nil { return nil, nil, fmt.Errorf("failed to create temp repo: %s", err) } @@ -207,8 +223,7 @@ func main() { defer cancel() // Spawn a local peer using a temporary path, for testing purposes - // Using port 4010 to avoid conflict with default IPFS port 4001 - ipfsA, nodeA, err := spawnEphemeral(ctx, 4010) + ipfsA, nodeA, err := spawnEphemeral(ctx) if err != nil { panic(fmt.Errorf("failed to spawn peer node: %s", err)) } @@ -222,9 +237,8 @@ func main() { fmt.Printf("Added file to peer with CID %s\n", peerCidFile.String()) // Spawn a node using a temporary path, creating a temporary repo for the run - // Using port 4011 (different from nodeA's port 4010) fmt.Println("Spawning Kubo node on a temporary repo") - ipfsB, _, err := spawnEphemeral(ctx, 4011) + ipfsB, _, err := spawnEphemeral(ctx) if err != nil { panic(fmt.Errorf("failed to spawn ephemeral node: %s", err)) } @@ -297,11 +311,12 @@ func main() { fmt.Printf("Got directory back from IPFS (IPFS path: %s) and wrote it to %s\n", cidDirectory.String(), outputPathDirectory) - /// --- Part IV: Getting a file from the IPFS Network + /// --- Part IV: Getting a file from another IPFS node - fmt.Println("\n-- Going to connect to a few nodes in the Network as bootstrappers --") + fmt.Println("\n-- Connecting to nodeA and fetching content via bitswap --") - // Get nodeA's address so we can fetch the file we added to it + // Get nodeA's actual listening address dynamically. + // We configured TCP-only on 127.0.0.1 with random port, so this will be a TCP address. peerAddrs, err := ipfsA.Swarm().LocalAddrs(ctx) if err != nil { panic(fmt.Errorf("could not get peer addresses: %s", err)) @@ -309,26 +324,18 @@ func main() { peerMa := peerAddrs[0].String() + "/p2p/" + nodeA.Identity.String() bootstrapNodes := []string{ - // In production, use autoconf.FallbackBootstrapPeers from boxo/autoconf - // which includes well-known IPFS bootstrap peers like: + // In production, use real bootstrap peers like: // "/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", - // "/dnsaddr/bootstrap.libp2p.io/p2p/QmQCU2EcMqAqQPR2i9bChDtGNJchTbq5TbXJJ16u19uLTa", - // "/ip4/104.131.131.82/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", - - // You can add custom peers here. For example, another IPFS node: - // "/ip4/192.0.2.1/tcp/4001/p2p/QmYourPeerID...", - // "/ip4/192.0.2.1/udp/4001/quic-v1/p2p/QmYourPeerID...", - - // nodeA's address (the peer we created above that has our test file) + // For this example, we only connect to nodeA which has our test content. peerMa, } - fmt.Println("Connecting to peers...") + fmt.Println("Connecting to peer...") err = connectToPeers(ctx, ipfsB, bootstrapNodes) if err != nil { panic(fmt.Errorf("failed to connect to peers: %s", err)) } - fmt.Println("Connected to peers") + fmt.Println("Connected to peer") exampleCIDStr := peerCidFile.RootCid().String() diff --git a/docs/examples/kubo-as-a-library/main_test.go b/docs/examples/kubo-as-a-library/main_test.go index be601c6a9..ecc2a592a 100644 --- a/docs/examples/kubo-as-a-library/main_test.go +++ b/docs/examples/kubo-as-a-library/main_test.go @@ -1,21 +1,39 @@ package main import ( + "bytes" + "io" + "os" "os/exec" "strings" "testing" + "time" ) func TestExample(t *testing.T) { - out, err := exec.Command("go", "run", "main.go").Output() + t.Log("Starting go run main.go...") + start := time.Now() + + cmd := exec.Command("go", "run", "main.go") + cmd.Env = append(os.Environ(), "GOLOG_LOG_LEVEL=error") // reduce libp2p noise + + // Stream output to both test log and capture buffer for verification + // This ensures we see progress even if the process is killed + var buf bytes.Buffer + cmd.Stdout = io.MultiWriter(os.Stdout, &buf) + cmd.Stderr = io.MultiWriter(os.Stderr, &buf) + + err := cmd.Run() + + elapsed := time.Since(start) + t.Logf("Command completed in %v", elapsed) + + out := buf.String() if err != nil { - var stderr string - if xe, ok := err.(*exec.ExitError); ok { - stderr = string(xe.Stderr) - } - t.Fatalf("running example (%v): %s\n%s", err, string(out), stderr) + t.Fatalf("running example (%v):\n%s", err, out) } - if !strings.Contains(string(out), "All done!") { - t.Errorf("example did not run successfully") + + if !strings.Contains(out, "All done!") { + t.Errorf("example did not complete successfully, output:\n%s", out) } } diff --git a/mk/golang.mk b/mk/golang.mk index b50179a0a..53bf5fca2 100644 --- a/mk/golang.mk +++ b/mk/golang.mk @@ -41,40 +41,57 @@ define go-build $(GOCC) build $(go-flags-with-tags) -o "$@" "$(1)" endef -test_go_test: $$(DEPS_GO) - $(GOCC) test $(go-flags-with-tags) $(GOTFLAGS) ./... -.PHONY: test_go_test +# Only disable colors when running in CI (non-interactive terminal) +GOTESTSUM_NOCOLOR := $(if $(CI),--no-color,) -# Build all platforms from .github/build-platforms.yml +# Packages excluded from coverage (test code and examples are not production code) +COVERPKG_EXCLUDE := /(test|docs/examples)/ + +# Packages excluded from unit tests: coverage exclusions + client/rpc (tested by test_cli) +UNIT_EXCLUDE := /(test|docs/examples)/|/client/rpc$$ + +# Unit tests with coverage +# Produces JSON for CI reporting and coverage profile for Codecov +test_unit: test/bin/gotestsum $$(DEPS_GO) + mkdir -p test/unit coverage + rm -f test/unit/gotest.json coverage/unit_tests.coverprofile + gotestsum $(GOTESTSUM_NOCOLOR) --jsonfile test/unit/gotest.json -- $(go-flags-with-tags) $(GOTFLAGS) -covermode=atomic -coverprofile=coverage/unit_tests.coverprofile -coverpkg=$$($(GOCC) list $(go-tags) ./... | grep -vE '$(COVERPKG_EXCLUDE)' | tr '\n' ',' | sed 's/,$$//') $$($(GOCC) list $(go-tags) ./... | grep -vE '$(UNIT_EXCLUDE)') +.PHONY: test_unit + +# CLI/integration tests (requires built binary in PATH) +# Includes test/cli, test/integration, and client/rpc +# Produces JSON for CI reporting +# Override TEST_CLI_TIMEOUT for local development: make test_cli TEST_CLI_TIMEOUT=5m +TEST_CLI_TIMEOUT ?= 10m +test_cli: cmd/ipfs/ipfs test/bin/gotestsum $$(DEPS_GO) + mkdir -p test/cli + rm -f test/cli/cli-tests.json + PATH="$(CURDIR)/cmd/ipfs:$(CURDIR)/test/bin:$$PATH" gotestsum $(GOTESTSUM_NOCOLOR) --jsonfile test/cli/cli-tests.json -- -v -timeout=$(TEST_CLI_TIMEOUT) ./test/cli/... ./test/integration/... ./client/rpc/... +.PHONY: test_cli + +# Example tests (docs/examples/kubo-as-a-library) +# Tests against both published and current kubo versions +# Uses timeout to ensure CI gets output before job-level timeout kills everything +TEST_EXAMPLES_TIMEOUT ?= 2m +test_examples: + cd docs/examples/kubo-as-a-library && go test -v -timeout=$(TEST_EXAMPLES_TIMEOUT) ./... && cp go.mod go.mod.bak && cp go.sum go.sum.bak && (go mod edit -replace github.com/ipfs/kubo=./../../.. && go mod tidy && go test -v -timeout=$(TEST_EXAMPLES_TIMEOUT) ./...; ret=$$?; mv go.mod.bak go.mod; mv go.sum.bak go.sum; exit $$ret) +.PHONY: test_examples + +# Build kubo for all platforms from .github/build-platforms.yml test_go_build: bin/test-go-build-platforms .PHONY: test_go_build -test_go_short: GOTFLAGS += -test.short -test_go_short: test_go_test -.PHONY: test_go_short - -test_go_race: GOTFLAGS += -race -test_go_race: test_go_test -.PHONY: test_go_race - -test_go_expensive: test_go_test test_go_build -.PHONY: test_go_expensive -TEST_GO += test_go_expensive - +# Check Go source formatting test_go_fmt: bin/test-go-fmt .PHONY: test_go_fmt -TEST_GO += test_go_fmt +# Run golangci-lint (used by CI) test_go_lint: test/bin/golangci-lint golangci-lint run --timeout=3m ./... .PHONY: test_go_lint -test_go: $(TEST_GO) - -# Version check is no longer needed - go.mod enforces minimum version -.PHONY: check_go_version - +TEST_GO := test_go_fmt test_unit test_cli test_examples TEST += $(TEST_GO) -TEST_SHORT += test_go_fmt test_go_short +TEST_SHORT += test_go_fmt test_unit diff --git a/test/cli/delegated_routing_v1_http_server_test.go b/test/cli/delegated_routing_v1_http_server_test.go index 7883fa793..ffcc571b7 100644 --- a/test/cli/delegated_routing_v1_http_server_test.go +++ b/test/cli/delegated_routing_v1_http_server_test.go @@ -2,7 +2,6 @@ package cli import ( "context" - "encoding/json" "strings" "testing" "time" @@ -21,11 +20,6 @@ import ( "github.com/stretchr/testify/require" ) -// swarmPeersOutput is used to parse the JSON output of 'ipfs swarm peers --enc=json' -type swarmPeersOutput struct { - Peers []struct{} `json:"Peers"` -} - func TestRoutingV1Server(t *testing.T) { t.Parallel() @@ -206,11 +200,14 @@ func TestRoutingV1Server(t *testing.T) { c, err := client.New(node.GatewayURL()) require.NoError(t, err) - // Try to get closest peers - should fail gracefully with an error + // Try to get closest peers - should fail gracefully with an error. + // Use 60-second timeout (server has 30s routing timeout). testCid, err := cid.Decode("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn") require.NoError(t, err) - _, err = c.GetClosestPeers(context.Background(), testCid) + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + _, err = c.GetClosestPeers(ctx, testCid) require.Error(t, err) // All these routing types should indicate DHT is not available // The exact error message may vary based on implementation details @@ -224,7 +221,7 @@ func TestRoutingV1Server(t *testing.T) { } }) - t.Run("GetClosestPeers returns peers for self", func(t *testing.T) { + t.Run("GetClosestPeers returns peers", func(t *testing.T) { t.Parallel() routingTypes := []string{"auto", "autoclient", "dht", "dhtclient"} @@ -242,47 +239,33 @@ func TestRoutingV1Server(t *testing.T) { }) node.StartDaemon() - // Create client before waiting so we can probe DHT readiness c, err := client.New(node.GatewayURL()) require.NoError(t, err) // Query for closest peers to our own peer ID key := peer.ToCid(node.PeerID()) - // Wait for node to connect to bootstrap peers and populate WAN DHT routing table - minPeers := len(autoconf.FallbackBootstrapPeers) - require.EventuallyWithT(t, func(t *assert.CollectT) { - res := node.RunIPFS("swarm", "peers", "--enc=json") - var output swarmPeersOutput - err := json.Unmarshal(res.Stdout.Bytes(), &output) - assert.NoError(t, err) - peerCount := len(output.Peers) - // Wait until we have at least minPeers connected - assert.GreaterOrEqual(t, peerCount, minPeers, - "waiting for at least %d bootstrap peers, currently have %d", minPeers, peerCount) - }, 60*time.Second, time.Second) - - // Wait for DHT to be ready by probing GetClosestPeers until it succeeds - require.EventuallyWithT(t, func(t *assert.CollectT) { - probeCtx, probeCancel := context.WithTimeout(context.Background(), 30*time.Second) - defer probeCancel() - probeIter, probeErr := c.GetClosestPeers(probeCtx, key) - if probeErr == nil { - probeIter.Close() + // Wait for WAN DHT routing table to be populated. + // The server has a 30-second routing timeout, so we use 60 seconds + // per request to allow for network latency while preventing hangs. + // Total wait time is 2 minutes (locally passes in under 1 minute). + var records []*types.PeerRecord + require.EventuallyWithT(t, func(ct *assert.CollectT) { + ctx, cancel := context.WithTimeout(t.Context(), 60*time.Second) + defer cancel() + resultsIter, err := c.GetClosestPeers(ctx, key) + if !assert.NoError(ct, err) { + return } - assert.NoError(t, probeErr, "DHT should be ready to handle GetClosestPeers") + records, err = iter.ReadAllResults(resultsIter) + assert.NoError(ct, err) }, 2*time.Minute, 5*time.Second) - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) - defer cancel() - resultsIter, err := c.GetClosestPeers(ctx, key) - require.NoError(t, err) - - records, err := iter.ReadAllResults(resultsIter) - require.NoError(t, err) - // Verify we got some peers back from WAN DHT - assert.NotEmpty(t, records, "should return some peers close to own peerid") + require.NotEmpty(t, records, "should return peers close to own peerid") + + // Per IPIP-0476, GetClosestPeers returns at most 20 peers + assert.LessOrEqual(t, len(records), 20, "IPIP-0476 limits GetClosestPeers to 20 peers") // Verify structure of returned records for _, record := range records { diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index 826fd60eb..75bbdf72c 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -15,7 +15,7 @@ require ( github.com/ipfs/iptb-plugins v0.5.1 github.com/multiformats/go-multiaddr v0.16.1 github.com/multiformats/go-multihash v0.2.3 - gotest.tools/gotestsum v1.12.3 + gotest.tools/gotestsum v1.13.0 ) require ( diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index 829479591..78d6acaef 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -985,8 +985,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/gotestsum v1.12.3 h1:jFwenGJ0RnPkuKh2VzAYl1mDOJgbhobBDeL2W1iEycs= -gotest.tools/gotestsum v1.12.3/go.mod h1:Y1+e0Iig4xIRtdmYbEV7K7H6spnjc1fX4BOuUhWw2Wk= +gotest.tools/gotestsum v1.13.0 h1:+Lh454O9mu9AMG1APV4o0y7oDYKyik/3kBOiCqiEpRo= +gotest.tools/gotestsum v1.13.0/go.mod h1:7f0NS5hFb0dWr4NtcsAsF0y1kzjEFfAil0HiBQJE03Q= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= diff --git a/test/unit/Rules.mk b/test/unit/Rules.mk index 69404637c..915d08f9a 100644 --- a/test/unit/Rules.mk +++ b/test/unit/Rules.mk @@ -2,7 +2,8 @@ include mk/header.mk CLEAN += $(d)/gotest.json $(d)/gotest.junit.xml -$(d)/gotest.junit.xml: test/bin/gotestsum coverage/unit_tests.coverprofile +# Convert gotest.json (produced by test_unit) to JUnit XML format +$(d)/gotest.junit.xml: test/bin/gotestsum $(d)/gotest.json gotestsum --no-color --junitfile $@ --raw-command cat $(@D)/gotest.json include mk/footer.mk From aa3c88dcdd84a16286354a3a4526ee5442daf651 Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Wed, 7 Jan 2026 20:51:19 -0800 Subject: [PATCH 13/49] shutdown daemon after test (#11135) --- test/cli/backup_bootstrap_test.go | 3 +++ test/cli/bitswap_config_test.go | 10 ++++++++++ test/cli/content_blocking_test.go | 1 + test/cli/dag_test.go | 3 +++ test/cli/delegated_routing_v1_http_proxy_test.go | 4 ++++ test/cli/delegated_routing_v1_http_server_test.go | 4 ++++ test/cli/dht_autoclient_test.go | 1 + test/cli/dht_opt_prov_test.go | 1 + test/cli/files_test.go | 14 ++++++++++++++ test/cli/gateway_limits_test.go | 2 ++ test/cli/gateway_range_test.go | 2 ++ test/cli/gateway_test.go | 10 ++++++++++ test/cli/http_gateway_over_libp2p_test.go | 2 ++ test/cli/http_retrieval_client_test.go | 1 + test/cli/init_test.go | 1 + test/cli/name_test.go | 6 ++++++ test/cli/peering_test.go | 4 ++++ test/cli/pin_ls_names_test.go | 6 +++--- test/cli/ping_test.go | 5 +++++ test/cli/pinning_remote_test.go | 15 +++++++++++++++ test/cli/pins_test.go | 4 ++++ test/cli/rcmgr_test.go | 11 +++++++++++ test/cli/routing_dht_test.go | 2 ++ test/cli/stats_test.go | 1 + test/cli/swarm_test.go | 5 +++++ test/cli/tracing_test.go | 1 + test/cli/transports_test.go | 6 ++++++ 27 files changed, 122 insertions(+), 3 deletions(-) diff --git a/test/cli/backup_bootstrap_test.go b/test/cli/backup_bootstrap_test.go index 017499f3d..eff00048a 100644 --- a/test/cli/backup_bootstrap_test.go +++ b/test/cli/backup_bootstrap_test.go @@ -39,7 +39,9 @@ func TestBackupBootstrapPeers(t *testing.T) { // Start 1 and 2. 2 does not know anyone yet. nodes[1].StartDaemon() + defer nodes[1].StopDaemon() nodes[2].StartDaemon() + defer nodes[2].StopDaemon() assert.Len(t, nodes[1].Peers(), 0) assert.Len(t, nodes[2].Peers(), 0) @@ -51,6 +53,7 @@ func TestBackupBootstrapPeers(t *testing.T) { // Start 0, wait a bit. Should connect to 1, and then discover 2 via the // backup bootstrap peers. nodes[0].StartDaemon() + defer nodes[0].StopDaemon() time.Sleep(time.Millisecond * 500) // Check if they're all connected. diff --git a/test/cli/bitswap_config_test.go b/test/cli/bitswap_config_test.go index 52e9ea541..5ee59ea56 100644 --- a/test/cli/bitswap_config_test.go +++ b/test/cli/bitswap_config_test.go @@ -22,7 +22,9 @@ func TestBitswapConfig(t *testing.T) { t.Parallel() h := harness.NewT(t) provider := h.NewNode().Init().StartDaemon() + defer provider.StopDaemon() requester := h.NewNode().Init().StartDaemon() + defer requester.StopDaemon() hash := provider.IPFSAddStr(string(testData)) requester.Connect(provider) @@ -38,8 +40,10 @@ func TestBitswapConfig(t *testing.T) { provider := h.NewNode().Init() provider.SetIPFSConfig("Bitswap.ServerEnabled", false) provider = provider.StartDaemon() + defer provider.StopDaemon() requester := h.NewNode().Init().StartDaemon() + defer requester.StopDaemon() hash := provider.IPFSAddStr(string(testData)) requester.Connect(provider) @@ -70,8 +74,10 @@ func TestBitswapConfig(t *testing.T) { requester := h.NewNode().Init() requester.SetIPFSConfig("Bitswap.ServerEnabled", false) requester.StartDaemon() + defer requester.StopDaemon() provider := h.NewNode().Init().StartDaemon() + defer provider.StopDaemon() hash := provider.IPFSAddStr(string(testData)) requester.Connect(provider) @@ -91,8 +97,10 @@ func TestBitswapConfig(t *testing.T) { cfg.HTTPRetrieval.Enabled = config.True }) requester.StartDaemon() + defer requester.StopDaemon() provider := h.NewNode().Init().StartDaemon() + defer provider.StopDaemon() hash := provider.IPFSAddStr(string(testData)) requester.Connect(provider) @@ -126,7 +134,9 @@ func TestBitswapConfig(t *testing.T) { cfg.HTTPRetrieval.Enabled = config.True }) provider = provider.StartDaemon() + defer provider.StopDaemon() requester := h.NewNode().Init().StartDaemon() + defer requester.StopDaemon() requester.Connect(provider) // read libp2p identify from remote peer, and print protocols diff --git a/test/cli/content_blocking_test.go b/test/cli/content_blocking_test.go index 8c50aee2b..513de5e59 100644 --- a/test/cli/content_blocking_test.go +++ b/test/cli/content_blocking_test.go @@ -76,6 +76,7 @@ func TestContentBlocking(t *testing.T) { // Start daemon, it should pick up denylist from $IPFS_PATH/denylists/test.deny node.StartDaemon() // we need online mode for GatewayOverLibp2p tests + t.Cleanup(func() { node.StopDaemon() }) client := node.GatewayClient() // First, confirm gateway works diff --git a/test/cli/dag_test.go b/test/cli/dag_test.go index f6758a710..38457318a 100644 --- a/test/cli/dag_test.go +++ b/test/cli/dag_test.go @@ -47,6 +47,8 @@ func TestDag(t *testing.T) { t.Run("ipfs dag stat --enc=json", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + // Import fixture r, err := os.Open(fixtureFile) assert.Nil(t, err) @@ -91,6 +93,7 @@ func TestDag(t *testing.T) { t.Run("ipfs dag stat", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() r, err := os.Open(fixtureFile) assert.NoError(t, err) defer r.Close() diff --git a/test/cli/delegated_routing_v1_http_proxy_test.go b/test/cli/delegated_routing_v1_http_proxy_test.go index 562532cb9..2b82a2714 100644 --- a/test/cli/delegated_routing_v1_http_proxy_test.go +++ b/test/cli/delegated_routing_v1_http_proxy_test.go @@ -60,6 +60,10 @@ func TestRoutingV1Proxy(t *testing.T) { }) nodes[2].StartDaemon() + t.Cleanup(func() { + nodes.StopDaemons() + }) + // Connect them. nodes.Connect() diff --git a/test/cli/delegated_routing_v1_http_server_test.go b/test/cli/delegated_routing_v1_http_server_test.go index ffcc571b7..503dba39b 100644 --- a/test/cli/delegated_routing_v1_http_server_test.go +++ b/test/cli/delegated_routing_v1_http_server_test.go @@ -32,6 +32,7 @@ func TestRoutingV1Server(t *testing.T) { }) }) nodes.StartDaemons().Connect() + t.Cleanup(func() { nodes.StopDaemons() }) return nodes } @@ -133,6 +134,7 @@ func TestRoutingV1Server(t *testing.T) { cfg.Routing.Type = config.NewOptionalString("dht") }) node.StartDaemon() + defer node.StopDaemon() // Put IPNS record in lonely node. It should be accepted as it is a valid record. c, err = client.New(node.GatewayURL()) @@ -196,6 +198,7 @@ func TestRoutingV1Server(t *testing.T) { } }) node.StartDaemon() + defer node.StopDaemon() c, err := client.New(node.GatewayURL()) require.NoError(t, err) @@ -238,6 +241,7 @@ func TestRoutingV1Server(t *testing.T) { cfg.Bootstrap = autoconf.FallbackBootstrapPeers }) node.StartDaemon() + defer node.StopDaemon() c, err := client.New(node.GatewayURL()) require.NoError(t, err) diff --git a/test/cli/dht_autoclient_test.go b/test/cli/dht_autoclient_test.go index adb200509..75e1cc241 100644 --- a/test/cli/dht_autoclient_test.go +++ b/test/cli/dht_autoclient_test.go @@ -16,6 +16,7 @@ func TestDHTAutoclient(t *testing.T) { node.IPFS("config", "Routing.Type", "autoclient") }) nodes.StartDaemons().Connect() + t.Cleanup(func() { nodes.StopDaemons() }) t.Run("file added on node in client mode is retrievable from node in client mode", func(t *testing.T) { t.Parallel() diff --git a/test/cli/dht_opt_prov_test.go b/test/cli/dht_opt_prov_test.go index 17b846dc7..291d48c54 100644 --- a/test/cli/dht_opt_prov_test.go +++ b/test/cli/dht_opt_prov_test.go @@ -22,6 +22,7 @@ func TestDHTOptimisticProvide(t *testing.T) { }) nodes.StartDaemons().Connect() + defer nodes.StopDaemons() hash := nodes[0].IPFSAddStr(string(random.Bytes(100))) nodes[0].IPFS("routing", "provide", hash) diff --git a/test/cli/files_test.go b/test/cli/files_test.go index ece87850e..4760c23aa 100644 --- a/test/cli/files_test.go +++ b/test/cli/files_test.go @@ -19,6 +19,7 @@ func TestFilesCp(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() // Create simple text file data := "testing files cp command" @@ -36,6 +37,7 @@ func TestFilesCp(t *testing.T) { t.Run("files cp with unsupported DAG node type fails", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() // MFS UnixFS is limited to dag-pb or raw, so we create a dag-cbor node to test this jsonData := `{"data": "not a UnixFS node"}` @@ -53,6 +55,7 @@ func TestFilesCp(t *testing.T) { t.Run("files cp with invalid UnixFS data structure fails", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() // Create an invalid proto file data := []byte{0xDE, 0xAD, 0xBE, 0xEF} // Invalid protobuf data @@ -75,6 +78,7 @@ func TestFilesCp(t *testing.T) { t.Run("files cp with raw node succeeds", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() // Create a raw node data := "raw data" @@ -98,6 +102,7 @@ func TestFilesCp(t *testing.T) { t.Run("files cp creates intermediate directories with -p", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() // Create a simple text file and add it to IPFS data := "hello parent directories" @@ -130,6 +135,7 @@ func TestFilesRm(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() // Create a file to remove node.IPFS("files", "mkdir", "/test-dir") @@ -149,6 +155,7 @@ func TestFilesRm(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() // Create a file to remove node.IPFS("files", "mkdir", "/test-dir") @@ -166,6 +173,7 @@ func TestFilesRm(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() // Create a file to remove node.IPFS("files", "mkdir", "/test-dir") @@ -186,6 +194,7 @@ func TestFilesNoFlushLimit(t *testing.T) { t.Run("reaches default limit of 256 operations", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() // Perform 256 operations with --flush=false (should succeed) for i := 0; i < 256; i++ { @@ -214,6 +223,7 @@ func TestFilesNoFlushLimit(t *testing.T) { }) node.StartDaemon() + defer node.StopDaemon() // Perform 5 operations (should succeed) for i := 0; i < 5; i++ { @@ -239,6 +249,7 @@ func TestFilesNoFlushLimit(t *testing.T) { }) node.StartDaemon() + defer node.StopDaemon() // Do 2 operations with --flush=false node.IPFS("files", "mkdir", "--flush=false", "/dir1") @@ -271,6 +282,7 @@ func TestFilesNoFlushLimit(t *testing.T) { }) node.StartDaemon() + defer node.StopDaemon() // Do 2 operations with --flush=false node.IPFS("files", "mkdir", "--flush=false", "/dir1") @@ -303,6 +315,7 @@ func TestFilesNoFlushLimit(t *testing.T) { }) node.StartDaemon() + defer node.StopDaemon() // Should be able to do many operations without error for i := 0; i < 300; i++ { @@ -322,6 +335,7 @@ func TestFilesNoFlushLimit(t *testing.T) { }) node.StartDaemon() + defer node.StopDaemon() // Mix of different MFS operations (5 operations to hit the limit) node.IPFS("files", "mkdir", "--flush=false", "/testdir") diff --git a/test/cli/gateway_limits_test.go b/test/cli/gateway_limits_test.go index 2c5554cf3..990eabb1a 100644 --- a/test/cli/gateway_limits_test.go +++ b/test/cli/gateway_limits_test.go @@ -28,6 +28,7 @@ func TestGatewayLimits(t *testing.T) { cfg.Gateway.RetrievalTimeout = config.NewOptionalDuration(1 * time.Second) }) node.StartDaemon() + defer node.StopDaemon() // Add content that can be retrieved quickly cid := node.IPFSAddStr("test content") @@ -69,6 +70,7 @@ func TestGatewayLimits(t *testing.T) { cfg.Gateway.RetrievalTimeout = config.NewOptionalDuration(2 * time.Second) }) node.StartDaemon() + defer node.StopDaemon() // Add some content - use a non-existent CID that will block during retrieval // to ensure we can control timing diff --git a/test/cli/gateway_range_test.go b/test/cli/gateway_range_test.go index 2d8ce1a3e..9efe08710 100644 --- a/test/cli/gateway_range_test.go +++ b/test/cli/gateway_range_test.go @@ -27,6 +27,7 @@ func TestGatewayHAMTDirectory(t *testing.T) { // Start node h := harness.NewT(t) node := h.NewNode().Init("--empty-repo", "--profile=test").StartDaemon("--offline") + defer node.StopDaemon() client := node.GatewayClient() // Import fixtures @@ -56,6 +57,7 @@ func TestGatewayHAMTRanges(t *testing.T) { // Start node h := harness.NewT(t) node := h.NewNode().Init("--empty-repo", "--profile=test").StartDaemon("--offline") + t.Cleanup(func() { node.StopDaemon() }) client := node.GatewayClient() // Import fixtures diff --git a/test/cli/gateway_test.go b/test/cli/gateway_test.go index 2d500c655..b80d2d700 100644 --- a/test/cli/gateway_test.go +++ b/test/cli/gateway_test.go @@ -28,6 +28,7 @@ func TestGateway(t *testing.T) { t.Parallel() h := harness.NewT(t) node := h.NewNode().Init().StartDaemon("--offline") + t.Cleanup(func() { node.StopDaemon() }) cid := node.IPFSAddStr("Hello Worlds!") peerID, err := peer.ToCid(node.PeerID()).StringOfBase(multibase.Base36) @@ -234,6 +235,7 @@ func TestGateway(t *testing.T) { cfg.API.HTTPHeaders = map[string][]string{header: values} }) node.StartDaemon() + defer node.StopDaemon() resp := node.APIClient().DisableRedirects().Get("/webui/") assert.Equal(t, resp.Headers.Values(header), values) @@ -257,6 +259,7 @@ func TestGateway(t *testing.T) { t.Run("pprof", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + t.Cleanup(func() { node.StopDaemon() }) apiClient := node.APIClient() t.Run("mutex", func(t *testing.T) { t.Parallel() @@ -300,6 +303,7 @@ func TestGateway(t *testing.T) { t.Parallel() h := harness.NewT(t) node := h.NewNode().Init().StartDaemon() + t.Cleanup(func() { node.StopDaemon() }) h.WriteFile("index/index.html", "

") cid := node.IPFS("add", "-Q", "-r", filepath.Join(h.Dir, "index")).Stderr.Trimmed() @@ -367,6 +371,7 @@ func TestGateway(t *testing.T) { cfg.Addresses.Gateway = config.Strings{"/ip4/127.0.0.1/tcp/32563"} }) node.StartDaemon() + defer node.StopDaemon() b, err := os.ReadFile(filepath.Join(node.Dir, "gateway")) require.NoError(t, err) @@ -388,6 +393,7 @@ func TestGateway(t *testing.T) { assert.NoError(t, err) nodes.StartDaemons().Connect() + t.Cleanup(func() { nodes.StopDaemons() }) t.Run("not present", func(t *testing.T) { cidFoo := node2.IPFSAddStr("foo") @@ -460,6 +466,7 @@ func TestGateway(t *testing.T) { } }) node.StartDaemon() + defer node.StopDaemon() cidFoo := node.IPFSAddStr("foo") client := node.GatewayClient() @@ -509,6 +516,7 @@ func TestGateway(t *testing.T) { node := harness.NewT(t).NewNode().Init() node.StartDaemon() + defer node.StopDaemon() client := node.GatewayClient() res := client.Get("/ipfs/invalid-thing", func(r *http.Request) { @@ -526,6 +534,7 @@ func TestGateway(t *testing.T) { cfg.Gateway.DisableHTMLErrors = config.True }) node.StartDaemon() + defer node.StopDaemon() client := node.GatewayClient() res := client.Get("/ipfs/invalid-thing", func(r *http.Request) { @@ -546,6 +555,7 @@ func TestLogs(t *testing.T) { t.Setenv("GOLOG_LOG_LEVEL", "info") node := h.NewNode().Init().StartDaemon("--offline") + defer node.StopDaemon() cid := node.IPFSAddStr("Hello Worlds!") peerID, err := peer.ToCid(node.PeerID()).StringOfBase(multibase.Base36) diff --git a/test/cli/http_gateway_over_libp2p_test.go b/test/cli/http_gateway_over_libp2p_test.go index f8cfe0071..58ab0217b 100644 --- a/test/cli/http_gateway_over_libp2p_test.go +++ b/test/cli/http_gateway_over_libp2p_test.go @@ -32,6 +32,7 @@ func TestGatewayOverLibp2p(t *testing.T) { p2pProxyNode := nodes[1] nodes.StartDaemons().Connect() + defer nodes.StopDaemons() // Add data to the gateway node cidDataOnGatewayNode := cid.MustParse(gwNode.IPFSAddStr("Hello Worlds2!")) @@ -65,6 +66,7 @@ func TestGatewayOverLibp2p(t *testing.T) { // Enable the experimental feature and reconnect the nodes gwNode.IPFS("config", "--json", "Experimental.GatewayOverLibp2p", "true") gwNode.StopDaemon().StartDaemon() + t.Cleanup(func() { gwNode.StopDaemon() }) nodes.Connect() // Note: the bare HTTP requests here assume that the gateway is mounted at `/` diff --git a/test/cli/http_retrieval_client_test.go b/test/cli/http_retrieval_client_test.go index e2934fc99..32628bfce 100644 --- a/test/cli/http_retrieval_client_test.go +++ b/test/cli/http_retrieval_client_test.go @@ -75,6 +75,7 @@ func TestHTTPRetrievalClient(t *testing.T) { // Start Kubo node.StartDaemon() + defer node.StopDaemon() if debug { fmt.Printf("delegatedRoutingServer.URL: %s\n", delegatedRoutingServer.URL) diff --git a/test/cli/init_test.go b/test/cli/init_test.go index 217ec64c3..dee844608 100644 --- a/test/cli/init_test.go +++ b/test/cli/init_test.go @@ -155,6 +155,7 @@ func TestInit(t *testing.T) { t.Run("ipfs init should not run while daemon is running", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() res := node.RunIPFS("init") assert.NotEqual(t, 0, res.ExitErr.ExitCode()) assert.Contains(t, res.Stderr.String(), "Error: ipfs daemon is running. please stop it to run this command") diff --git a/test/cli/name_test.go b/test/cli/name_test.go index a0931bfa0..cf5df2bb0 100644 --- a/test/cli/name_test.go +++ b/test/cli/name_test.go @@ -103,6 +103,7 @@ func TestName(t *testing.T) { }) node.StartDaemon() + defer node.StopDaemon() t.Run("Resolving self offline succeeds (daemon on)", func(t *testing.T) { res = node.IPFS("name", "resolve", "--offline", "/ipns/"+name.String()) @@ -147,6 +148,7 @@ func TestName(t *testing.T) { t.Run("Fails to publish in offline mode", func(t *testing.T) { t.Parallel() node := makeDaemon(t, nil).StartDaemon("--offline") + defer node.StopDaemon() res := node.RunIPFS("name", "publish", "/ipfs/"+fixtureCid) require.Error(t, res.Err) require.Equal(t, 1, res.ExitCode()) @@ -157,6 +159,7 @@ func TestName(t *testing.T) { t.Parallel() node := makeDaemon(t, nil).StartDaemon() + defer node.StopDaemon() ipnsName := ipns.NameFromPeer(node.PeerID()).String() ipnsPath := ipns.NamespacePrefix + ipnsName publishPath := "/ipfs/" + fixtureCid @@ -187,6 +190,7 @@ func TestName(t *testing.T) { t.Parallel() node := makeDaemon(t, nil).StartDaemon() + t.Cleanup(func() { node.StopDaemon() }) ipnsPath := ipns.NamespacePrefix + ipns.NameFromPeer(node.PeerID()).String() publishPath := "/ipfs/" + fixtureCid @@ -227,6 +231,7 @@ func TestName(t *testing.T) { t.Run("Inspect with verification using wrong RSA key errors", func(t *testing.T) { t.Parallel() node := makeDaemon(t, nil).StartDaemon() + defer node.StopDaemon() // Prepare RSA Key 1 res := node.IPFS("key", "gen", "--type=rsa", "--size=4096", "key1") @@ -299,6 +304,7 @@ func TestName(t *testing.T) { t.Parallel() node := makeDaemon(t, nil).StartDaemon() + defer node.StopDaemon() publishPath1 := "/ipfs/" + fixtureCid publishPath2 := "/ipfs/" + dagCid // Different content name := ipns.NameFromPeer(node.PeerID()) diff --git a/test/cli/peering_test.go b/test/cli/peering_test.go index 9c6ab975d..227e83f18 100644 --- a/test/cli/peering_test.go +++ b/test/cli/peering_test.go @@ -62,6 +62,7 @@ func TestPeering(t *testing.T) { h, nodes := harness.CreatePeerNodes(t, 3, peerings) nodes.StartDaemons() + defer nodes.StopDaemons() assertPeerings(h, nodes, peerings) nodes[0].Disconnect(nodes[1]) @@ -74,6 +75,7 @@ func TestPeering(t *testing.T) { h, nodes := harness.CreatePeerNodes(t, 3, peerings) nodes.StartDaemons() + defer nodes.StopDaemons() assertPeerings(h, nodes, peerings) nodes[2].Disconnect(nodes[1]) @@ -85,6 +87,7 @@ func TestPeering(t *testing.T) { peerings := []harness.Peering{{From: 0, To: 1}, {From: 1, To: 0}, {From: 1, To: 2}} h, nodes := harness.CreatePeerNodes(t, 3, peerings) + defer nodes.StopDaemons() nodes[0].StartDaemon() nodes[1].StartDaemon() assertPeerings(h, nodes, []harness.Peering{{From: 0, To: 1}, {From: 1, To: 0}}) @@ -99,6 +102,7 @@ func TestPeering(t *testing.T) { h, nodes := harness.CreatePeerNodes(t, 3, peerings) nodes.StartDaemons() + defer nodes.StopDaemons() assertPeerings(h, nodes, peerings) nodes[2].StopDaemon() diff --git a/test/cli/pin_ls_names_test.go b/test/cli/pin_ls_names_test.go index 54532b6b2..f8ae76885 100644 --- a/test/cli/pin_ls_names_test.go +++ b/test/cli/pin_ls_names_test.go @@ -28,6 +28,9 @@ func setupTestNode(t *testing.T) *harness.Node { t.Helper() node := harness.NewT(t).NewNode().Init() node.StartDaemon("--offline") + t.Cleanup(func() { + node.StopDaemon() + }) return node } @@ -498,7 +501,6 @@ func TestPinLsEdgeCases(t *testing.T) { t.Run("invalid pin type returns error", func(t *testing.T) { t.Parallel() node := setupTestNode(t) - defer node.StopDaemon() // Try to list pins with invalid type res := node.RunIPFS("pin", "ls", "--type=invalid") @@ -510,7 +512,6 @@ func TestPinLsEdgeCases(t *testing.T) { t.Run("non-existent path returns proper error", func(t *testing.T) { t.Parallel() node := setupTestNode(t) - defer node.StopDaemon() // Try to list a non-existent CID fakeCID := "QmNonExistent123456789" @@ -521,7 +522,6 @@ func TestPinLsEdgeCases(t *testing.T) { t.Run("unpinned CID returns not pinned error", func(t *testing.T) { t.Parallel() node := setupTestNode(t) - defer node.StopDaemon() // Add content but don't pin it explicitly (it's just in blockstore) unpinnedCID := node.IPFSAddStr("unpinned content", "--pin=false") diff --git a/test/cli/ping_test.go b/test/cli/ping_test.go index 9470e67d8..85de29cf9 100644 --- a/test/cli/ping_test.go +++ b/test/cli/ping_test.go @@ -15,6 +15,7 @@ func TestPing(t *testing.T) { t.Run("other", func(t *testing.T) { t.Parallel() nodes := harness.NewT(t).NewNodes(2).Init().StartDaemons().Connect() + defer nodes.StopDaemons() node1 := nodes[0] node2 := nodes[1] @@ -25,6 +26,7 @@ func TestPing(t *testing.T) { t.Run("ping unreachable peer", func(t *testing.T) { t.Parallel() nodes := harness.NewT(t).NewNodes(2).Init().StartDaemons().Connect() + defer nodes.StopDaemons() node1 := nodes[0] badPeer := "QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJx" @@ -37,6 +39,7 @@ func TestPing(t *testing.T) { t.Run("self", func(t *testing.T) { t.Parallel() nodes := harness.NewT(t).NewNodes(2).Init().StartDaemons() + defer nodes.StopDaemons() node1 := nodes[0] node2 := nodes[1] @@ -52,6 +55,7 @@ func TestPing(t *testing.T) { t.Run("0", func(t *testing.T) { t.Parallel() nodes := harness.NewT(t).NewNodes(2).Init().StartDaemons().Connect() + defer nodes.StopDaemons() node1 := nodes[0] node2 := nodes[1] @@ -63,6 +67,7 @@ func TestPing(t *testing.T) { t.Run("offline", func(t *testing.T) { t.Parallel() nodes := harness.NewT(t).NewNodes(2).Init().StartDaemons().Connect() + defer nodes.StopDaemons() node1 := nodes[0] node2 := nodes[1] diff --git a/test/cli/pinning_remote_test.go b/test/cli/pinning_remote_test.go index fd9ae8e94..6c802aaa0 100644 --- a/test/cli/pinning_remote_test.go +++ b/test/cli/pinning_remote_test.go @@ -51,6 +51,7 @@ func TestRemotePinning(t *testing.T) { node.IPFS("config", "--json", "Pinning.RemoteServices.svc.Policies.MFS.Enable", "true") node.StartDaemon() + t.Cleanup(func() { node.StopDaemon() }) node.IPFS("files", "cp", "/ipfs/bafkqaaa", "/mfs-pinning-test-"+uuid.NewString()) node.IPFS("files", "flush") @@ -133,6 +134,8 @@ func TestRemotePinning(t *testing.T) { t.Run("pin remote service ls --stat", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + _, svcURL := runPinningService(t, authToken) node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) @@ -155,6 +158,7 @@ func TestRemotePinning(t *testing.T) { t.Run("adding service with invalid URL fails", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() res := node.RunIPFS("pin", "remote", "service", "add", "svc", "invalid-service.example.com", "key") assert.Equal(t, 1, res.ExitCode()) @@ -168,6 +172,7 @@ func TestRemotePinning(t *testing.T) { t.Run("unauthorized pinning service calls fail", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() _, svcURL := runPinningService(t, authToken) node.IPFS("pin", "remote", "service", "add", "svc", svcURL, "othertoken") @@ -180,6 +185,7 @@ func TestRemotePinning(t *testing.T) { t.Run("pinning service calls fail when there is a wrong path", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() _, svcURL := runPinningService(t, authToken) node.IPFS("pin", "remote", "service", "add", "svc", svcURL+"/invalid-path", authToken) @@ -191,6 +197,7 @@ func TestRemotePinning(t *testing.T) { t.Run("pinning service calls fail when DNS resolution fails", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() node.IPFS("pin", "remote", "service", "add", "svc", "https://invalid-service.example.com", authToken) res := node.RunIPFS("pin", "remote", "ls", "--service=svc") @@ -201,6 +208,7 @@ func TestRemotePinning(t *testing.T) { t.Run("pin remote service rm", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() node.IPFS("pin", "remote", "service", "add", "svc", "https://example.com", authToken) node.IPFS("pin", "remote", "service", "rm", "svc") res := node.IPFS("pin", "remote", "service", "ls") @@ -225,6 +233,7 @@ func TestRemotePinning(t *testing.T) { t.Run("'ipfs pin remote add --background=true'", func(t *testing.T) { node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() svc, svcURL := runPinningService(t, authToken) node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) @@ -266,6 +275,7 @@ func TestRemotePinning(t *testing.T) { t.Run("'ipfs pin remote add --background=false'", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() svc, svcURL := runPinningService(t, authToken) node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) @@ -287,6 +297,7 @@ func TestRemotePinning(t *testing.T) { t.Run("'ipfs pin remote ls' with multiple statuses", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() svc, svcURL := runPinningService(t, authToken) node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) @@ -340,6 +351,7 @@ func TestRemotePinning(t *testing.T) { t.Run("'ipfs pin remote ls' by CID", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() svc, svcURL := runPinningService(t, authToken) node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) @@ -360,6 +372,7 @@ func TestRemotePinning(t *testing.T) { t.Run("'ipfs pin remote rm --name' without --force when multiple pins match", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() svc, svcURL := runPinningService(t, authToken) node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) @@ -388,6 +401,7 @@ func TestRemotePinning(t *testing.T) { t.Run("'ipfs pin remote rm --name --force' remove multiple pins", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() svc, svcURL := runPinningService(t, authToken) node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) @@ -408,6 +422,7 @@ func TestRemotePinning(t *testing.T) { t.Run("'ipfs pin remote rm --force' removes all pins", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() svc, svcURL := runPinningService(t, authToken) node.IPFS("pin", "remote", "service", "add", "svc", svcURL, authToken) diff --git a/test/cli/pins_test.go b/test/cli/pins_test.go index 1425a90b2..8e98aa7fe 100644 --- a/test/cli/pins_test.go +++ b/test/cli/pins_test.go @@ -26,6 +26,7 @@ func testPins(t *testing.T, args testPinsArgs) { node := harness.NewT(t).NewNode().Init() if args.runDaemon { node.StartDaemon("--offline") + defer node.StopDaemon() } strs := []string{"a", "b", "c", "d", "e", "f", "g"} @@ -127,6 +128,7 @@ func testPinsErrorReporting(t *testing.T, args testPinsArgs) { node := harness.NewT(t).NewNode().Init() if args.runDaemon { node.StartDaemon("--offline") + defer node.StopDaemon() } randomCID := "Qme8uX5n9hn15pw9p6WcVKoziyyC9LXv4LEgvsmKMULjnV" res := node.RunIPFS(StrCat("pin", "add", args.pinArg, randomCID)...) @@ -142,6 +144,7 @@ func testPinDAG(t *testing.T, args testPinsArgs) { node := h.NewNode().Init() if args.runDaemon { node.StartDaemon("--offline") + defer node.StopDaemon() } bytes := random.Bytes(1 << 20) // 1 MiB tmpFile := h.WriteToTemp(string(bytes)) @@ -168,6 +171,7 @@ func testPinProgress(t *testing.T, args testPinsArgs) { if args.runDaemon { node.StartDaemon("--offline") + defer node.StopDaemon() } bytes := random.Bytes(1 << 20) // 1 MiB diff --git a/test/cli/rcmgr_test.go b/test/cli/rcmgr_test.go index 50ea26979..66e6eb6ac 100644 --- a/test/cli/rcmgr_test.go +++ b/test/cli/rcmgr_test.go @@ -26,6 +26,7 @@ func TestRcmgr(t *testing.T) { }) node.StartDaemon() + defer node.StopDaemon() t.Run("swarm resources should fail", func(t *testing.T) { res := node.RunIPFS("swarm", "resources") @@ -41,6 +42,7 @@ func TestRcmgr(t *testing.T) { cfg.Swarm.ResourceMgr.Enabled = config.False }) node.StartDaemon() + defer node.StopDaemon() t.Run("swarm resources should fail", func(t *testing.T) { res := node.RunIPFS("swarm", "resources") @@ -56,6 +58,7 @@ func TestRcmgr(t *testing.T) { cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(1000) }) node.StartDaemon() + defer node.StopDaemon() res := node.RunIPFS("swarm", "resources", "--enc=json") require.Equal(t, 0, res.ExitCode()) @@ -73,7 +76,9 @@ func TestRcmgr(t *testing.T) { node.UpdateConfig(func(cfg *config.Config) { cfg.Swarm.ConnMgr.HighWater = config.NewOptionalInteger(1000) }) + node.StartDaemon() + t.Cleanup(func() { node.StopDaemon() }) t.Run("conns and streams are above 800 for default connmgr settings", func(t *testing.T) { t.Parallel() @@ -135,6 +140,7 @@ func TestRcmgr(t *testing.T) { overrides.System.ConnsInbound = rcmgr.Unlimited }) node.StartDaemon() + defer node.StopDaemon() res := node.RunIPFS("swarm", "resources", "--enc=json") limits := unmarshalLimits(t, res.Stdout.Bytes()) @@ -150,6 +156,7 @@ func TestRcmgr(t *testing.T) { overrides.Transient.Memory = 88888 }) node.StartDaemon() + defer node.StopDaemon() res := node.RunIPFS("swarm", "resources", "--enc=json") limits := unmarshalLimits(t, res.Stdout.Bytes()) @@ -163,6 +170,7 @@ func TestRcmgr(t *testing.T) { overrides.Service = map[string]rcmgr.ResourceLimits{"foo": {Memory: 77777}} }) node.StartDaemon() + defer node.StopDaemon() res := node.RunIPFS("swarm", "resources", "--enc=json") limits := unmarshalLimits(t, res.Stdout.Bytes()) @@ -176,6 +184,7 @@ func TestRcmgr(t *testing.T) { overrides.Protocol = map[protocol.ID]rcmgr.ResourceLimits{"foo": {Memory: 66666}} }) node.StartDaemon() + defer node.StopDaemon() res := node.RunIPFS("swarm", "resources", "--enc=json") limits := unmarshalLimits(t, res.Stdout.Bytes()) @@ -191,6 +200,7 @@ func TestRcmgr(t *testing.T) { overrides.Peer = map[peer.ID]rcmgr.ResourceLimits{validPeerID: {Memory: 55555}} }) node.StartDaemon() + defer node.StopDaemon() res := node.RunIPFS("swarm", "resources", "--enc=json") limits := unmarshalLimits(t, res.Stdout.Bytes()) @@ -218,6 +228,7 @@ func TestRcmgr(t *testing.T) { }) nodes.StartDaemons() + t.Cleanup(func() { nodes.StopDaemons() }) t.Run("node 0 should fail to connect to and ping node 1", func(t *testing.T) { t.Parallel() diff --git a/test/cli/routing_dht_test.go b/test/cli/routing_dht_test.go index 27ef2b19a..b1f3907b6 100644 --- a/test/cli/routing_dht_test.go +++ b/test/cli/routing_dht_test.go @@ -57,6 +57,7 @@ func testRoutingDHT(t *testing.T, enablePubsub bool) { } nodes.StartDaemons(daemonArgs...).Connect() + t.Cleanup(func() { nodes.StopDaemons() }) t.Run("ipfs routing findpeer", func(t *testing.T) { t.Parallel() @@ -157,6 +158,7 @@ func testSelfFindDHT(t *testing.T) { }) nodes.StartDaemons() + defer nodes.StopDaemons() res := nodes[0].RunIPFS("dht", "findpeer", nodes[0].PeerID().String()) assert.Equal(t, 1, res.ExitCode()) diff --git a/test/cli/stats_test.go b/test/cli/stats_test.go index 05c1702b4..f835381e0 100644 --- a/test/cli/stats_test.go +++ b/test/cli/stats_test.go @@ -14,6 +14,7 @@ func TestStats(t *testing.T) { t.Run("stats dht", func(t *testing.T) { t.Parallel() nodes := harness.NewT(t).NewNodes(2).Init().StartDaemons().Connect() + defer nodes.StopDaemons() node1 := nodes[0] res := node1.IPFS("stats", "dht") diff --git a/test/cli/swarm_test.go b/test/cli/swarm_test.go index 88f5f403b..56c484ae1 100644 --- a/test/cli/swarm_test.go +++ b/test/cli/swarm_test.go @@ -31,6 +31,7 @@ func TestSwarm(t *testing.T) { t.Run("ipfs swarm peers returns empty peers when a node is not connected to any peers", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() res := node.RunIPFS("swarm", "peers", "--enc=json", "--identify") var output expectedOutputType err := json.Unmarshal(res.Stdout.Bytes(), &output) @@ -40,7 +41,9 @@ func TestSwarm(t *testing.T) { t.Run("ipfs swarm peers with flag identify outputs expected identify information about connected peers", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() otherNode := harness.NewT(t).NewNode().Init().StartDaemon() + defer otherNode.StopDaemon() node.Connect(otherNode) res := node.RunIPFS("swarm", "peers", "--enc=json", "--identify") @@ -67,7 +70,9 @@ func TestSwarm(t *testing.T) { t.Run("ipfs swarm peers with flag identify outputs Identify field with data that matches calling ipfs id on a peer", func(t *testing.T) { t.Parallel() node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() otherNode := harness.NewT(t).NewNode().Init().StartDaemon() + defer otherNode.StopDaemon() node.Connect(otherNode) otherNodeIDResponse := otherNode.RunIPFS("id", "--enc=json") diff --git a/test/cli/tracing_test.go b/test/cli/tracing_test.go index 6f19759be..7be60fea0 100644 --- a/test/cli/tracing_test.go +++ b/test/cli/tracing_test.go @@ -76,6 +76,7 @@ func TestTracing(t *testing.T) { node.Runner.Env["OTEL_EXPORTER_OTLP_PROTOCOL"] = "grpc" node.Runner.Env["OTEL_EXPORTER_OTLP_ENDPOINT"] = "http://localhost:4317" node.StartDaemon() + defer node.StopDaemon() assert.Eventually(t, func() bool { diff --git a/test/cli/transports_test.go b/test/cli/transports_test.go index 43daa8ed4..e36d27287 100644 --- a/test/cli/transports_test.go +++ b/test/cli/transports_test.go @@ -74,6 +74,7 @@ func TestTransports(t *testing.T) { t.Parallel() nodes := tcpNodes(t).StartDaemons().Connect() runTests(nodes) + nodes.StopDaemons() }) t.Run("tcp with NOISE", func(t *testing.T) { @@ -86,6 +87,7 @@ func TestTransports(t *testing.T) { }) nodes.StartDaemons().Connect() runTests(nodes) + nodes.StopDaemons() }) t.Run("QUIC", func(t *testing.T) { @@ -104,6 +106,7 @@ func TestTransports(t *testing.T) { disableRouting(nodes) nodes.StartDaemons().Connect() runTests(nodes) + nodes.StopDaemons() }) t.Run("QUIC+Webtransport", func(t *testing.T) { @@ -122,6 +125,7 @@ func TestTransports(t *testing.T) { disableRouting(nodes) nodes.StartDaemons().Connect() runTests(nodes) + nodes.StopDaemons() }) t.Run("QUIC connects with non-dialable transports", func(t *testing.T) { @@ -144,6 +148,7 @@ func TestTransports(t *testing.T) { disableRouting(nodes) nodes.StartDaemons().Connect() runTests(nodes) + nodes.StopDaemons() }) t.Run("WebRTC Direct", func(t *testing.T) { @@ -162,5 +167,6 @@ func TestTransports(t *testing.T) { disableRouting(nodes) nodes.StartDaemons().Connect() runTests(nodes) + nodes.StopDaemons() }) } From 23ba660ef07a282e5fc3f4b0ad44dae107c74f3f Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 9 Jan 2026 18:23:31 +0100 Subject: [PATCH 14/49] fix(routing): use LegacyProvider for HTTP-only custom routing (#11112) * fix(routing): use LegacyProvider for HTTP-only custom routing when `Routing.Type=custom` with only HTTP routers and no DHT, fall back to LegacyProvider instead of SweepingProvider. SweepingProvider requires a DHT client which is unavailable in HTTP-only configurations, causing it to return NoopProvider and breaking provider record announcements to HTTP routers. fixes #11089 * test(routing): verify provide stat works with HTTP-only routing * docs(config): clarify SweepEnabled fallback for HTTP-only routing --------- Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com> --- core/node/provider.go | 52 +++++++++++++++++++++++++- docs/config.md | 3 ++ test/cli/provider_test.go | 79 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) diff --git a/core/node/provider.go b/core/node/provider.go index a780da3d7..1470112bb 100644 --- a/core/node/provider.go +++ b/core/node/provider.go @@ -692,6 +692,48 @@ See docs: https://github.com/ipfs/kubo/blob/master/docs/config.md#providedhtmaxw // ONLINE/OFFLINE +// hasDHTRouting checks if the routing configuration includes a DHT component. +// Returns false for HTTP-only custom routing configurations (e.g., Routing.Type="custom" +// with only HTTP routers). This is used to determine whether SweepingProviderOpt +// can be used, since it requires a DHT client. +func hasDHTRouting(cfg *config.Config) bool { + routingType := cfg.Routing.Type.WithDefault(config.DefaultRoutingType) + switch routingType { + case "auto", "autoclient", "dht", "dhtclient", "dhtserver": + return true + case "custom": + // Check if any router in custom config is DHT-based + for _, router := range cfg.Routing.Routers { + if routerIncludesDHT(router, cfg) { + return true + } + } + return false + default: // "none", "delegated" + return false + } +} + +// routerIncludesDHT recursively checks if a router configuration includes DHT. +// Handles parallel and sequential composite routers by checking their children. +func routerIncludesDHT(rp config.RouterParser, cfg *config.Config) bool { + switch rp.Type { + case config.RouterTypeDHT: + return true + case config.RouterTypeParallel, config.RouterTypeSequential: + if children, ok := rp.Parameters.(*config.ComposableRouterParams); ok { + for _, child := range children.Routers { + if childRouter, exists := cfg.Routing.Routers[child.RouterName]; exists { + if routerIncludesDHT(childRouter, cfg) { + return true + } + } + } + } + } + return false +} + // OnlineProviders groups units managing provide routing records online func OnlineProviders(provide bool, cfg *config.Config) fx.Option { if !provide { @@ -708,7 +750,15 @@ func OnlineProviders(provide bool, cfg *config.Config) fx.Option { opts := []fx.Option{ fx.Provide(setReproviderKeyProvider(providerStrategy)), } - if cfg.Provide.DHT.SweepEnabled.WithDefault(config.DefaultProvideDHTSweepEnabled) { + + sweepEnabled := cfg.Provide.DHT.SweepEnabled.WithDefault(config.DefaultProvideDHTSweepEnabled) + dhtAvailable := hasDHTRouting(cfg) + + // Use SweepingProvider only when both sweep is enabled AND DHT is available. + // For HTTP-only routing (e.g., Routing.Type="custom" with only HTTP routers), + // fall back to LegacyProvider which works with ProvideManyRouter. + // See https://github.com/ipfs/kubo/issues/11089 + if sweepEnabled && dhtAvailable { opts = append(opts, SweepingProviderOpt(cfg)) } else { reprovideInterval := cfg.Provide.DHT.Interval.WithDefault(config.DefaultProvideDHTInterval) diff --git a/docs/config.md b/docs/config.md index 23386f7e6..b4cb911f4 100644 --- a/docs/config.md +++ b/docs/config.md @@ -2195,6 +2195,9 @@ You can compare the effectiveness of sweep mode vs legacy mode by monitoring the > [!NOTE] > This is the default provider system as of Kubo v0.39. To use the legacy provider instead, set `Provide.DHT.SweepEnabled=false`. +> [!NOTE] +> When DHT routing is unavailable (e.g., `Routing.Type=custom` with only HTTP routers), the provider automatically falls back to the legacy provider regardless of this setting. + Default: `true` Type: `flag` diff --git a/test/cli/provider_test.go b/test/cli/provider_test.go index ccd164860..9d5e0d175 100644 --- a/test/cli/provider_test.go +++ b/test/cli/provider_test.go @@ -7,6 +7,7 @@ import ( "net/http" "net/http/httptest" "strings" + "sync/atomic" "testing" "time" @@ -764,3 +765,81 @@ func TestProvider(t *testing.T) { }) } } + +// TestHTTPOnlyProviderWithSweepEnabled tests that provider records are correctly +// sent to HTTP routers when Routing.Type="custom" with only HTTP routers configured, +// even when Provide.DHT.SweepEnabled=true (the default since v0.39). +// +// This is a regression test for https://github.com/ipfs/kubo/issues/11089 +func TestHTTPOnlyProviderWithSweepEnabled(t *testing.T) { + t.Parallel() + + // Track provide requests received by the mock HTTP router + var provideRequests atomic.Int32 + mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if (r.Method == http.MethodPut || r.Method == http.MethodPost) && + strings.HasPrefix(r.URL.Path, "/routing/v1/providers") { + provideRequests.Add(1) + w.WriteHeader(http.StatusOK) + } else if strings.HasPrefix(r.URL.Path, "/routing/v1/providers") && r.Method == http.MethodGet { + // Return empty providers for findprovs + w.Header().Set("Content-Type", "application/x-ndjson") + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(http.StatusNotFound) + } + })) + defer mockServer.Close() + + h := harness.NewT(t) + node := h.NewNode().Init() + + // Explicitly set SweepEnabled=true (the default since v0.39, but be explicit for test clarity) + node.SetIPFSConfig("Provide.DHT.SweepEnabled", true) + node.SetIPFSConfig("Provide.Enabled", true) + + // Configure HTTP-only custom routing (no DHT) with explicit Routing.Type=custom + routingConf := map[string]any{ + "Type": "custom", // Explicitly set Routing.Type=custom + "Methods": map[string]any{ + "provide": map[string]any{"RouterName": "HTTPRouter"}, + "get-ipns": map[string]any{"RouterName": "HTTPRouter"}, + "put-ipns": map[string]any{"RouterName": "HTTPRouter"}, + "find-peers": map[string]any{"RouterName": "HTTPRouter"}, + "find-providers": map[string]any{"RouterName": "HTTPRouter"}, + }, + "Routers": map[string]any{ + "HTTPRouter": map[string]any{ + "Type": "http", + "Parameters": map[string]any{ + "Endpoint": mockServer.URL, + }, + }, + }, + } + node.SetIPFSConfig("Routing", routingConf) + node.StartDaemon() + defer node.StopDaemon() + + // Add content and manually provide it + cid := node.IPFSAddStr(time.Now().String()) + + // Manual provide should succeed even without libp2p peers + res := node.RunIPFS("routing", "provide", cid) + // Check that the command succeeded (exit code 0) and no provide-related errors + assert.Equal(t, 0, res.ExitCode(), "routing provide should succeed with HTTP-only routing and SweepEnabled=true") + assert.NotContains(t, res.Stderr.String(), "cannot provide", "should not have provide errors") + + // Verify HTTP router received at least one provide request + assert.Greater(t, provideRequests.Load(), int32(0), + "HTTP router should have received provide requests") + + // Verify 'provide stat' works with HTTP-only routing (regression test for stats) + statRes := node.RunIPFS("provide", "stat") + assert.Equal(t, 0, statRes.ExitCode(), "provide stat should succeed with HTTP-only routing") + assert.NotContains(t, statRes.Stderr.String(), "stats not available", + "should not report stats unavailable") + // LegacyProvider outputs "TotalReprovides:" in its stats + assert.Contains(t, statRes.Stdout.String(), "TotalReprovides:", + "should show legacy provider stats") +} From 3e85793b5809dd56e6be5789ccbbaca23dc906ab Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 9 Jan 2026 18:26:32 +0100 Subject: [PATCH 15/49] docs: add production deployment guidance for gateway (#11117) - add "Running in Production" section to gateway.md - link to specs.ipfs.tech instead of github - update "Protocol Labs" to "IPFS Foundation" - add reverse proxy requirement to config.md PublicGateways section - reference reverse proxy, timeout, rate limiting, and CDN caveats --- docs/config.md | 20 +++++++++- docs/gateway.md | 99 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 110 insertions(+), 9 deletions(-) diff --git a/docs/config.md b/docs/config.md index b4cb911f4..d8284de2f 100644 --- a/docs/config.md +++ b/docs/config.md @@ -1073,7 +1073,11 @@ Toggle and configure experimental features of Kubo. Experimental features are li Options for the HTTP gateway. -**NOTE:** support for `/api/v0` under the gateway path is now deprecated. It will be removed in future versions: . +> [!IMPORTANT] +> By default, Kubo's gateway is configured for local use at `127.0.0.1` and `localhost`. +> To run a public gateway, configure your domain names in [`Gateway.PublicGateways`](#gatewaypublicgateways). +> For production deployment considerations (reverse proxy, timeouts, rate limiting, CDN), +> see [Running in Production](gateway.md#running-in-production). ### `Gateway.NoFetch` @@ -1268,6 +1272,11 @@ Examples: - `*.example.com` will match requests to `http://foo.example.com/ipfs/*` or `http://{cid}.ipfs.bar.example.com/*`. - `foo-*.example.com` will match requests to `http://foo-bar.example.com/ipfs/*` or `http://{cid}.ipfs.foo-xyz.example.com/*`. +> [!IMPORTANT] +> **Reverse Proxy:** If running behind nginx or another reverse proxy, ensure +> `Host` and `X-Forwarded-*` headers are forwarded correctly. +> See [Reverse Proxy Caveats](gateway.md#reverse-proxy) in gateway documentation. + #### `Gateway.PublicGateways: Paths` An array of paths that should be exposed on the hostname. @@ -1334,6 +1343,9 @@ Default: `false` Type: `bool` +> [!IMPORTANT] +> See [Reverse Proxy Caveats](gateway.md#reverse-proxy) if running behind nginx or another reverse proxy. + #### `Gateway.PublicGateways: NoDNSLink` A boolean to configure whether DNSLink for hostname present in `Host` @@ -1344,6 +1356,9 @@ Default: `false` (DNSLink lookup enabled by default for every defined hostname) Type: `bool` +> [!IMPORTANT] +> See [Reverse Proxy Caveats](gateway.md#reverse-proxy) if running behind nginx or another reverse proxy. + #### `Gateway.PublicGateways: InlineDNSLink` An optional flag to explicitly configure whether subdomain gateway's redirects @@ -1411,6 +1426,9 @@ ipfs config --json Gateway.PublicGateways '{"localhost": null }' Below is a list of the most common gateway setups. +> [!IMPORTANT] +> See [Reverse Proxy Caveats](gateway.md#reverse-proxy) if running behind nginx or another reverse proxy. + - Public [subdomain gateway](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#subdomain-gateway) at `http://{cid}.ipfs.dweb.link` (each content root gets its own Origin) ```console diff --git a/docs/gateway.md b/docs/gateway.md index 3a616a158..d51eab4cc 100644 --- a/docs/gateway.md +++ b/docs/gateway.md @@ -6,7 +6,7 @@ they were stored in a traditional web server. [More about Gateways](https://docs.ipfs.tech/concepts/ipfs-gateway/) and [addressing IPFS on the web](https://docs.ipfs.tech/how-to/address-ipfs-on-web/). -Kubo's Gateway implementation follows [ipfs/specs: Specification for HTTP Gateways](https://github.com/ipfs/specs/tree/main/http-gateways#readme). +Kubo's Gateway implementation follows [IPFS Gateway Specifications](https://specs.ipfs.tech/http-gateways/) and is tested with [Gateway Conformance Test Suite](https://github.com/ipfs/gateway-conformance). ### Local gateway @@ -14,14 +14,21 @@ By default, Kubo nodes run a [path gateway](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#path-gateway) at `http://127.0.0.1:8080/` and a [subdomain gateway](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#subdomain-gateway) at `http://localhost:8080/`. -The path one also implements [trustless gateway spec](https://specs.ipfs.tech/http-gateways/trustless-gateway/) -and supports [trustless responses](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval) as opt-in via `Accept` header. +> [!CAUTION] +> **For browsing websites, web apps, and dapps in a browser, use the subdomain +> gateway** (`localhost`). Each content root gets its own +> [web origin](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy), +> isolating localStorage, cookies, and session data between sites. +> +> **For file retrieval, use the path gateway** (`127.0.0.1`). Path gateways are +> suited for downloading files or fetching [verifiable](https://docs.ipfs.tech/reference/http/gateway/#trustless-verifiable-retrieval) +> content, but lack origin isolation (all content shares the same origin). Additional listening addresses and gateway behaviors can be set in the [config](#configuration) file. ### Public gateways -Protocol Labs provides a public gateway at +IPFS Foundation [provides public gateways](https://docs.ipfs.tech/concepts/public-utilities/) at `https://ipfs.io` ([path](https://specs.ipfs.tech/http-gateways/path-gateway/)), `https://dweb.link` ([subdomain](https://docs.ipfs.tech/how-to/address-ipfs-on-web/#subdomain-gateway)), and `https://trustless-gateway.link` ([trustless](https://specs.ipfs.tech/http-gateways/trustless-gateway/) only). @@ -41,6 +48,80 @@ The gateway's log level can be changed with this command: > ipfs log level core/server debug ``` +## Running in Production + +When deploying Kubo's gateway in production, be aware of these important considerations: + + +> [!IMPORTANT] +> **Reverse Proxy:** When running Kubo behind a reverse proxy (such as nginx), +> the original `Host` header **must** be forwarded to Kubo for +> [`Gateway.PublicGateways`](config.md#gatewaypublicgateways) to work. +> Kubo uses the `Host` header to match configured hostnames and detect +> subdomain gateway patterns like `{cid}.ipfs.example.org` or DNSLink hostnames. +> +> If the `Host` header is not forwarded correctly, Kubo will not recognize +> the configured gateway hostnames and requests may be handled incorrectly. +> +> If `X-Forwarded-Proto` is not set, redirects over HTTPS will use wrong protocol +> and DNSLink names will not be inlined for subdomain gateways. +> +> Example: minimal nginx configuration for `example.org` +> +> ```nginx +> server { +> listen 80; +> listen [::]:80; +> +> # IMPORTANT: Include wildcard to match subdomain gateway requests. +> # The dot prefix matches both apex domain and all subdomains. +> server_name .example.org; +> +> location / { +> proxy_pass http://127.0.0.1:8080; +> +> # IMPORTANT: Forward the original Host header to Kubo. +> # Without this, PublicGateways configuration will not work. +> proxy_set_header Host $host; +> +> # IMPORTANT: X-Forwarded-Proto is required for correct behavior: +> # - Redirects will use https:// URLs when set to "https" +> # - DNSLink names will be inlined for subdomain gateways +> # (e.g., /ipns/en.wikipedia-on-ipfs.org โ†’ en-wikipedia--on--ipfs-org.ipns.example.org) +> proxy_set_header X-Forwarded-Proto $scheme; +> proxy_set_header X-Forwarded-Host $host; +> } +> } +> ``` +> +> Common mistakes to avoid: +> +> - **Missing wildcard in `server_name`:** Using only `server_name example.org;` +> will not match subdomain requests like `{cid}.ipfs.example.org`. Always +> include `*.example.org` or use the dot prefix `.example.org`. +> +> - **Wrong `Host` header value:** Using `proxy_set_header Host $proxy_host;` +> sends the backend's hostname (e.g., `127.0.0.1:8080`) instead of the +> original `Host` header. Always use `$host` or `$http_host`. +> +> - **Missing `Host` header entirely:** If `proxy_set_header Host` is not +> specified, nginx defaults to `$proxy_host`, which breaks gateway routing. + +> [!IMPORTANT] +> **Timeouts:** Configure [`Gateway.RetrievalTimeout`](config.md#gatewayretrievaltimeout) +> based on your expected content retrieval times. + +> [!IMPORTANT] +> **Rate Limiting:** Use [`Gateway.MaxConcurrentRequests`](config.md#gatewaymaxconcurrentrequests) +> to protect against traffic spikes. + +> [!IMPORTANT] +> **CDN/Cloudflare:** If using Cloudflare or other CDNs with +> [deserialized responses](config.md#gatewaydeserializedresponses) enabled, review +> [`Gateway.MaxRangeRequestFileSize`](config.md#gatewaymaxrangerequestfilesize) to avoid +> excess bandwidth billing from range request bugs. Cloudflare users may need additional +> protection via [Cloudflare Snippets](https://github.com/ipfs/boxo/issues/856#issuecomment-3523944976). + ## Directories For convenience, the gateway (mostly) acts like a normal web-server when serving @@ -53,7 +134,7 @@ a directory: 2. Dynamically build and serve a listing of the contents of the directory. This redirect is skipped if the query string contains a -`go-get=1` parameter. See [PR#3964](https://github.com/ipfs/kubo/pull/3963) +`go-get=1` parameter. See [PR#3963](https://github.com/ipfs/kubo/pull/3963) for details ## Static Websites @@ -107,10 +188,12 @@ This is equivalent of `ipfs block get`. ### `application/vnd.ipld.car` -Returns a [CAR](https://ipld.io/specs/transport/car/) stream for specific DAG and selector. +Returns a [CAR](https://ipld.io/specs/transport/car/) stream for a DAG or a subset of it. -Right now only 'full DAG' implicit selector is implemented. -Support for user-provided IPLD selectors is tracked in https://github.com/ipfs/kubo/issues/8769. +The `dag-scope` parameter controls which blocks are included: `all` (default, entire DAG), +`entity` (logical unit like a file), or `block` (single block). For [UnixFS](https://specs.ipfs.tech/unixfs/) files, +`entity-bytes` enables byte range requests. See [IPIP-402](https://specs.ipfs.tech/ipips/ipip-0402/) +for details. This is a rough equivalent of `ipfs dag export`. From 5288946fd16f698f15f5bca029f61a59ed7fdf40 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 9 Jan 2026 18:41:10 +0100 Subject: [PATCH 16/49] feat(cli): improve ipfs dag stat output UX (#11097) - add TTY auto-detection for progress display (matching `dag export`) - use single-line progress with carriage return instead of flooding - show human-readable sizes alongside raw bytes in summary - update --progress flag to be auto-detected by default progress format: `Fetched/Processed N blocks, M bytes (X MB)` summary format: `Total Size: 99 (99 B)` --- core/commands/dag/dag.go | 9 ++++- core/commands/dag/stat.go | 40 +++++++++++++++---- docs/changelogs/v0.40.md | 25 ++++++++++++ .../fixtures/TestDagStatExpectedOutput.txt | 4 +- 4 files changed, 67 insertions(+), 11 deletions(-) diff --git a/core/commands/dag/dag.go b/core/commands/dag/dag.go index 6827e46fa..caf7a5474 100644 --- a/core/commands/dag/dag.go +++ b/core/commands/dag/dag.go @@ -7,6 +7,7 @@ import ( "io" "path" + "github.com/dustin/go-humanize" "github.com/ipfs/kubo/core/commands/cmdenv" "github.com/ipfs/kubo/core/commands/cmdutils" @@ -349,7 +350,11 @@ type DagStatSummary struct { } func (s *DagStatSummary) String() string { - return fmt.Sprintf("Total Size: %d\nUnique Blocks: %d\nShared Size: %d\nRatio: %f", s.TotalSize, s.UniqueBlocks, s.SharedSize, s.Ratio) + return fmt.Sprintf("Total Size: %d (%s)\nUnique Blocks: %d\nShared Size: %d (%s)\nRatio: %f", + s.TotalSize, humanize.Bytes(s.TotalSize), + s.UniqueBlocks, + s.SharedSize, humanize.Bytes(s.SharedSize), + s.Ratio) } func (s *DagStatSummary) incrementTotalSize(size uint64) { @@ -384,7 +389,7 @@ Note: This command skips duplicate blocks in reporting both size and the number cmds.StringArg("root", true, true, "CID of a DAG root to get statistics for").EnableStdin(), }, Options: []cmds.Option{ - cmds.BoolOption(progressOptionName, "p", "Return progressive data while reading through the DAG").WithDefault(true), + cmds.BoolOption(progressOptionName, "p", "Show progress on stderr. Auto-detected if stderr is a terminal."), }, Run: dagStat, Type: DagStatSummary{}, diff --git a/core/commands/dag/stat.go b/core/commands/dag/stat.go index bb9be7e0d..916aae71a 100644 --- a/core/commands/dag/stat.go +++ b/core/commands/dag/stat.go @@ -5,6 +5,7 @@ import ( "io" "os" + "github.com/dustin/go-humanize" mdag "github.com/ipfs/boxo/ipld/merkledag" "github.com/ipfs/boxo/ipld/merkledag/traverse" cid "github.com/ipfs/go-cid" @@ -19,7 +20,11 @@ import ( // to compute the new state func dagStat(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { - progressive := req.Options[progressOptionName].(bool) + // Default to true (emit intermediate states) for HTTP/RPC clients that want progress + progressive := true + if val, specified := req.Options[progressOptionName].(bool); specified { + progressive = val + } api, err := cmdenv.GetApi(env, req) if err != nil { return err @@ -84,6 +89,18 @@ func dagStat(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) } func finishCLIStat(res cmds.Response, re cmds.ResponseEmitter) error { + // Determine whether to show progress based on TTY detection or explicit flag + var showProgress bool + val, specified := res.Request().Options[progressOptionName] + if !specified { + // Auto-detect: show progress only if stderr is a TTY + if errStat, err := os.Stderr.Stat(); err == nil { + showProgress = (errStat.Mode() & os.ModeCharDevice) != 0 + } + } else { + showProgress = val.(bool) + } + var dagStats *DagStatSummary for { v, err := res.Next() @@ -96,17 +113,26 @@ func finishCLIStat(res cmds.Response, re cmds.ResponseEmitter) error { switch out := v.(type) { case *DagStatSummary: dagStats = out - if dagStats.Ratio == 0 { - length := len(dagStats.DagStatsArray) - if length > 0 { - currentStat := dagStats.DagStatsArray[length-1] - fmt.Fprintf(os.Stderr, "CID: %s, Size: %d, NumBlocks: %d\n", currentStat.Cid, currentStat.Size, currentStat.NumBlocks) + // Ratio == 0 means this is a progress update (not final result) + if showProgress && dagStats.Ratio == 0 { + // Sum up total progress across all DAGs being scanned + var totalBlocks int64 + var totalSize uint64 + for _, stat := range dagStats.DagStatsArray { + totalBlocks += stat.NumBlocks + totalSize += stat.Size } + fmt.Fprintf(os.Stderr, "Fetched/Processed %d blocks, %d bytes (%s)\r", totalBlocks, totalSize, humanize.Bytes(totalSize)) } default: return e.TypeErr(out, v) - } } + + // Clear the progress line before final output + if showProgress { + fmt.Fprint(os.Stderr, "\033[2K\r") + } + return re.Emit(dagStats) } diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index 19f785de8..61241bc20 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -12,6 +12,7 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [๐Ÿ”ฆ Highlights](#-highlights) - [Routing V1 HTTP API now exposed by default](#routing-v1-http-api-now-exposed-by-default) - [Track total size when adding pins](#track-total-size-when-adding-pins) + - [Improved `ipfs dag stat` output](#improved-ipfs-dag-stat-output) - [Skip bad keys when listing](#skip_bad_keys_when_listing) - [๐Ÿ“ฆ๏ธ Dependency updates](#-dependency-updates) - [๐Ÿ“ Changelog](#-changelog) @@ -34,6 +35,30 @@ Example output: Fetched/Processed 336 nodes (83 MB) ``` +#### Improved `ipfs dag stat` output + +The `ipfs dag stat` command has been improved for better terminal UX: + +- Progress output now uses a single line with carriage return, avoiding terminal flooding +- Progress is auto-detected: shown only in interactive terminals by default +- Human-readable sizes are now displayed alongside raw byte counts + +Example progress (interactive terminal): +``` +Fetched/Processed 84 blocks, 2097152 bytes (2.1 MB) +``` + +Example summary output: +``` +Summary +Total Size: 2097152 (2.1 MB) +Unique Blocks: 42 +Shared Size: 1048576 (1.0 MB) +Ratio: 1.500000 +``` + +Use `--progress=true` to force progress even when piped, or `--progress=false` to disable it. + #### Skip bad keys when listing Change the `ipfs key list` behavior to log an error and continue listing keys when a key cannot be read from the keystore or decoded. diff --git a/test/cli/fixtures/TestDagStatExpectedOutput.txt b/test/cli/fixtures/TestDagStatExpectedOutput.txt index 9e709f4a2..87bc405a1 100644 --- a/test/cli/fixtures/TestDagStatExpectedOutput.txt +++ b/test/cli/fixtures/TestDagStatExpectedOutput.txt @@ -4,9 +4,9 @@ bafyreibmdfd7c5db4kls4ty57zljfhqv36gi43l6txl44pi423wwmeskwy 2 53 bafyreie3njilzdi4ixumru4nzgecsnjtu7fzfcwhg7e6s4s5i7cnbslvn4 2 53 Summary -Total Size: 99 +Total Size: 99 (99 B) Unique Blocks: 3 -Shared Size: 7 +Shared Size: 7 (7 B) Ratio: 1.070707 From 25ebab9dae9a564f2c6f2424961feb5b7642ea31 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 9 Jan 2026 19:22:43 +0100 Subject: [PATCH 17/49] feat(p2p): add --foreground flag to listen and forward commands (#11099) * feat(p2p): add --foreground flag to listen and forward commands adds `-f/--foreground` option that keeps the command running until interrupted (SIGTERM/Ctrl+C) or closed via `ipfs p2p close`. the listener/forwarder is automatically removed when the command exits. useful for systemd services and scripts that need cleanup on exit. * docs: add p2p-tunnels.md with systemd examples - add dedicated docs/p2p-tunnels.md covering: - why p2p tunnels (NAT traversal, no public IP needed) - quick start with netcat - background and foreground modes - systemd integration with path-based activation - security considerations and troubleshooting - document Experimental.Libp2pStreamMounting in docs/config.md - simplify docs/experimental-features.md, link to new doc - add "Learn more" links to ipfs p2p listen/forward --help - update changelog entry with doc link - add cross-reference in misc/README.md * chore: reference kubo#5460 for p2p config Ref. https://github.com/ipfs/kubo/issues/5460 * fix(daemon): write api/gateway files only after HTTP server is ready fixes race condition where $IPFS_PATH/api and $IPFS_PATH/gateway files were written before the HTTP servers were ready to accept connections. this caused issues for tools like systemd path units that immediately try to connect when these files appear. changes: - add corehttp.ServeWithReady() that signals when server is ready - wait for ready signal before writing address files - use sync.WaitGroup.Go() (Go 1.25) for cleaner goroutine management - add TestAddressFileReady to verify both api and gateway files * fix(daemon): buffer errc channel and wait for all listeners - buffer error channel with len(listeners) to prevent deadlock when multiple servers write errors simultaneously - wait for ALL listeners to be ready before writing api/gateway file, not just the first one Feedback-from: https://github.com/ipfs/kubo/pull/11099#pullrequestreview-3593885839 * docs(changelog): improve p2p tunnel section clarity reframe to lead with user benefit and add example output * docs(p2p): remove obsolete race condition caveat the "First launch fails but restarts work" troubleshooting section described a race where the api file was written before the daemon was ready. this was fixed in 80b703a which ensures api/gateway files are only written after HTTP servers are ready to accept connections. --------- Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com> --- cmd/ipfs/kubo/daemon.go | 75 ++++-- core/commands/p2p.go | 179 ++++++++++++-- core/corehttp/corehttp.go | 19 +- docs/changelogs/v0.40.md | 18 ++ docs/config.md | 12 + docs/experimental-features.md | 93 +------- docs/p2p-tunnels.md | 214 +++++++++++++++++ misc/README.md | 6 + p2p/listener.go | 14 +- p2p/local.go | 7 + p2p/remote.go | 11 +- test/cli/api_file_test.go | 104 ++++++++ test/cli/p2p_test.go | 430 ++++++++++++++++++++++++++++++++++ 13 files changed, 1048 insertions(+), 134 deletions(-) create mode 100644 docs/p2p-tunnels.md create mode 100644 test/cli/api_file_test.go create mode 100644 test/cli/p2p_test.go diff --git a/cmd/ipfs/kubo/daemon.go b/cmd/ipfs/kubo/daemon.go index 97d46c7cf..49aa9c19b 100644 --- a/cmd/ipfs/kubo/daemon.go +++ b/cmd/ipfs/kubo/daemon.go @@ -883,23 +883,38 @@ func serveHTTPApi(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, error return nil, fmt.Errorf("serveHTTPApi: ConstructNode() failed: %s", err) } + // Buffer channel to prevent deadlock when multiple servers write errors simultaneously + errc := make(chan error, len(listeners)) + var wg sync.WaitGroup + + // Start all servers and wait for them to be ready before writing api file. + // This prevents race conditions where external tools (like systemd path units) + // see the file and try to connect before servers can accept connections. if len(listeners) > 0 { - // Only add an api file if the API is running. + readyChannels := make([]chan struct{}, len(listeners)) + for i, lis := range listeners { + readyChannels[i] = make(chan struct{}) + ready := readyChannels[i] + wg.Go(func() { + errc <- corehttp.ServeWithReady(node, manet.NetListener(lis), ready, opts...) + }) + } + + // Wait for all listeners to be ready or any to fail + for _, ready := range readyChannels { + select { + case <-ready: + // This listener is ready + case err := <-errc: + return nil, fmt.Errorf("serveHTTPApi: %w", err) + } + } + if err := node.Repo.SetAPIAddr(rewriteMaddrToUseLocalhostIfItsAny(listeners[0].Multiaddr())); err != nil { return nil, fmt.Errorf("serveHTTPApi: SetAPIAddr() failed: %w", err) } } - errc := make(chan error) - var wg sync.WaitGroup - for _, apiLis := range listeners { - wg.Add(1) - go func(lis manet.Listener) { - defer wg.Done() - errc <- corehttp.Serve(node, manet.NetListener(lis), opts...) - }(apiLis) - } - go func() { wg.Wait() close(errc) @@ -1058,26 +1073,42 @@ func serveHTTPGateway(req *cmds.Request, cctx *oldcmds.Context) (<-chan error, e return nil, fmt.Errorf("serveHTTPGateway: ConstructNode() failed: %s", err) } + // Buffer channel to prevent deadlock when multiple servers write errors simultaneously + errc := make(chan error, len(listeners)) + var wg sync.WaitGroup + + // Start all servers and wait for them to be ready before writing gateway file. + // This prevents race conditions where external tools (like systemd path units) + // see the file and try to connect before servers can accept connections. if len(listeners) > 0 { + readyChannels := make([]chan struct{}, len(listeners)) + for i, lis := range listeners { + readyChannels[i] = make(chan struct{}) + ready := readyChannels[i] + wg.Go(func() { + errc <- corehttp.ServeWithReady(node, manet.NetListener(lis), ready, opts...) + }) + } + + // Wait for all listeners to be ready or any to fail + for _, ready := range readyChannels { + select { + case <-ready: + // This listener is ready + case err := <-errc: + return nil, fmt.Errorf("serveHTTPGateway: %w", err) + } + } + addr, err := manet.ToNetAddr(rewriteMaddrToUseLocalhostIfItsAny(listeners[0].Multiaddr())) if err != nil { - return nil, fmt.Errorf("serveHTTPGateway: manet.ToIP() failed: %w", err) + return nil, fmt.Errorf("serveHTTPGateway: manet.ToNetAddr() failed: %w", err) } if err := node.Repo.SetGatewayAddr(addr); err != nil { return nil, fmt.Errorf("serveHTTPGateway: SetGatewayAddr() failed: %w", err) } } - errc := make(chan error) - var wg sync.WaitGroup - for _, lis := range listeners { - wg.Add(1) - go func(lis manet.Listener) { - defer wg.Done() - errc <- corehttp.Serve(node, manet.NetListener(lis), opts...) - }(lis) - } - go func() { wg.Wait() close(errc) diff --git a/core/commands/p2p.go b/core/commands/p2p.go index 1fbdc8a28..1de0bfca3 100644 --- a/core/commands/p2p.go +++ b/core/commands/p2p.go @@ -50,9 +50,17 @@ type P2PStreamsOutput struct { Streams []P2PStreamInfoOutput } +// P2PForegroundOutput is output type for foreground mode status messages +type P2PForegroundOutput struct { + Status string // "active" or "closing" + Protocol string + Address string +} + const ( allowCustomProtocolOptionName = "allow-custom-protocol" reportPeerIDOptionName = "report-peer-id" + foregroundOptionName = "foreground" ) var resolveTimeout = 10 * time.Second @@ -83,15 +91,37 @@ var p2pForwardCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Forward connections to libp2p service.", ShortDescription: ` -Forward connections made to to . +Forward connections made to to via libp2p. - specifies the libp2p protocol name to use for libp2p -connections and/or handlers. It must be prefixed with '` + P2PProtoPrefix + `'. +Creates a local TCP listener that tunnels connections through libp2p to a +remote peer's p2p listener. Similar to SSH port forwarding (-L flag). -Example: - ipfs p2p forward ` + P2PProtoPrefix + `myproto /ip4/127.0.0.1/tcp/4567 /p2p/QmPeer - - Forward connections to 127.0.0.1:4567 to '` + P2PProtoPrefix + `myproto' service on /p2p/QmPeer +ARGUMENTS: + Protocol name (must start with '` + P2PProtoPrefix + `') + Local multiaddr (e.g., /ip4/127.0.0.1/tcp/3000) + Remote peer multiaddr (e.g., /p2p/PeerID) + +FOREGROUND MODE (--foreground, -f): + + By default, the forwarder runs in the daemon and the command returns + immediately. Use --foreground to block until interrupted: + + - Ctrl+C or SIGTERM: Removes the forwarder and exits + - 'ipfs p2p close': Removes the forwarder and exits + - Daemon shutdown: Forwarder is automatically removed + + Useful for systemd services or scripts that need cleanup on exit. + +EXAMPLES: + + # Persistent forwarder (command returns immediately) + ipfs p2p forward /x/myapp /ip4/127.0.0.1/tcp/3000 /p2p/PeerID + + # Temporary forwarder (removed when command exits) + ipfs p2p forward -f /x/myapp /ip4/127.0.0.1/tcp/3000 /p2p/PeerID + +Learn more: https://github.com/ipfs/kubo/blob/master/docs/p2p-tunnels.md `, }, Arguments: []cmds.Argument{ @@ -101,6 +131,7 @@ Example: }, Options: []cmds.Option{ cmds.BoolOption(allowCustomProtocolOptionName, "Don't require /x/ prefix"), + cmds.BoolOption(foregroundOptionName, "f", "Run in foreground; forwarder is removed when command exits"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { n, err := p2pGetNode(env) @@ -130,7 +161,51 @@ Example: return errors.New("protocol name must be within '" + P2PProtoPrefix + "' namespace") } - return forwardLocal(n.Context(), n.P2P, n.Peerstore, proto, listen, targets) + listener, err := forwardLocal(n.Context(), n.P2P, n.Peerstore, proto, listen, targets) + if err != nil { + return err + } + + foreground, _ := req.Options[foregroundOptionName].(bool) + if foreground { + if err := res.Emit(&P2PForegroundOutput{ + Status: "active", + Protocol: protoOpt, + Address: listenOpt, + }); err != nil { + return err + } + // Wait for either context cancellation (Ctrl+C/daemon shutdown) + // or listener removal (ipfs p2p close) + select { + case <-req.Context.Done(): + // SIGTERM/Ctrl+C - cleanup silently (CLI stream already closing) + n.P2P.ListenersLocal.Close(func(l p2p.Listener) bool { + return l == listener + }) + return nil + case <-listener.Done(): + // Closed via "ipfs p2p close" - emit closing message + return res.Emit(&P2PForegroundOutput{ + Status: "closing", + Protocol: protoOpt, + Address: listenOpt, + }) + } + } + + return nil + }, + Type: P2PForegroundOutput{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *P2PForegroundOutput) error { + if out.Status == "active" { + fmt.Fprintf(w, "Forwarding %s to %s, waiting for interrupt...\n", out.Protocol, out.Address) + } else if out.Status == "closing" { + fmt.Fprintf(w, "Received interrupt, removing forwarder for %s\n", out.Protocol) + } + return nil + }), }, } @@ -185,14 +260,40 @@ var p2pListenCmd = &cmds.Command{ Helptext: cmds.HelpText{ Tagline: "Create libp2p service.", ShortDescription: ` -Create libp2p service and forward connections made to . +Create a libp2p protocol handler that forwards incoming connections to +. - specifies the libp2p handler name. It must be prefixed with '` + P2PProtoPrefix + `'. +When a remote peer connects using 'ipfs p2p forward', the connection is +forwarded to your local service. Similar to SSH port forwarding (server side). -Example: - ipfs p2p listen ` + P2PProtoPrefix + `myproto /ip4/127.0.0.1/tcp/1234 - - Forward connections to 'myproto' libp2p service to 127.0.0.1:1234 +ARGUMENTS: + Protocol name (must start with '` + P2PProtoPrefix + `') + Local multiaddr (e.g., /ip4/127.0.0.1/tcp/3000) + +FOREGROUND MODE (--foreground, -f): + + By default, the listener runs in the daemon and the command returns + immediately. Use --foreground to block until interrupted: + + - Ctrl+C or SIGTERM: Removes the listener and exits + - 'ipfs p2p close': Removes the listener and exits + - Daemon shutdown: Listener is automatically removed + + Useful for systemd services or scripts that need cleanup on exit. + +EXAMPLES: + + # Persistent listener (command returns immediately) + ipfs p2p listen /x/myapp /ip4/127.0.0.1/tcp/3000 + + # Temporary listener (removed when command exits) + ipfs p2p listen -f /x/myapp /ip4/127.0.0.1/tcp/3000 + + # Report connecting peer ID to the target application + ipfs p2p listen -r /x/myapp /ip4/127.0.0.1/tcp/3000 + +Learn more: https://github.com/ipfs/kubo/blob/master/docs/p2p-tunnels.md `, }, Arguments: []cmds.Argument{ @@ -202,6 +303,7 @@ Example: Options: []cmds.Option{ cmds.BoolOption(allowCustomProtocolOptionName, "Don't require /x/ prefix"), cmds.BoolOption(reportPeerIDOptionName, "r", "Send remote base58 peerid to target when a new connection is established"), + cmds.BoolOption(foregroundOptionName, "f", "Run in foreground; listener is removed when command exits"), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { n, err := p2pGetNode(env) @@ -231,8 +333,51 @@ Example: return errors.New("protocol name must be within '" + P2PProtoPrefix + "' namespace") } - _, err = n.P2P.ForwardRemote(n.Context(), proto, target, reportPeerID) - return err + listener, err := n.P2P.ForwardRemote(n.Context(), proto, target, reportPeerID) + if err != nil { + return err + } + + foreground, _ := req.Options[foregroundOptionName].(bool) + if foreground { + if err := res.Emit(&P2PForegroundOutput{ + Status: "active", + Protocol: protoOpt, + Address: targetOpt, + }); err != nil { + return err + } + // Wait for either context cancellation (Ctrl+C/daemon shutdown) + // or listener removal (ipfs p2p close) + select { + case <-req.Context.Done(): + // SIGTERM/Ctrl+C - cleanup silently (CLI stream already closing) + n.P2P.ListenersP2P.Close(func(l p2p.Listener) bool { + return l == listener + }) + return nil + case <-listener.Done(): + // Closed via "ipfs p2p close" - emit closing message + return res.Emit(&P2PForegroundOutput{ + Status: "closing", + Protocol: protoOpt, + Address: targetOpt, + }) + } + } + + return nil + }, + Type: P2PForegroundOutput{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *P2PForegroundOutput) error { + if out.Status == "active" { + fmt.Fprintf(w, "Listening on %s, forwarding to %s, waiting for interrupt...\n", out.Protocol, out.Address) + } else if out.Status == "closing" { + fmt.Fprintf(w, "Received interrupt, removing listener for %s\n", out.Protocol) + } + return nil + }), }, } @@ -271,11 +416,9 @@ func checkPort(target ma.Multiaddr) error { } // forwardLocal forwards local connections to a libp2p service -func forwardLocal(ctx context.Context, p *p2p.P2P, ps pstore.Peerstore, proto protocol.ID, bindAddr ma.Multiaddr, addr *peer.AddrInfo) error { +func forwardLocal(ctx context.Context, p *p2p.P2P, ps pstore.Peerstore, proto protocol.ID, bindAddr ma.Multiaddr, addr *peer.AddrInfo) (p2p.Listener, error) { ps.AddAddrs(addr.ID, addr.Addrs, pstore.TempAddrTTL) - // TODO: return some info - _, err := p.ForwardLocal(ctx, addr.ID, proto, bindAddr) - return err + return p.ForwardLocal(ctx, addr.ID, proto, bindAddr) } const ( diff --git a/core/corehttp/corehttp.go b/core/corehttp/corehttp.go index 344991923..6749c738b 100644 --- a/core/corehttp/corehttp.go +++ b/core/corehttp/corehttp.go @@ -78,9 +78,23 @@ func ListenAndServe(n *core.IpfsNode, listeningMultiAddr string, options ...Serv return Serve(n, manet.NetListener(list), options...) } -// Serve accepts incoming HTTP connections on the listener and pass them +// Serve accepts incoming HTTP connections on the listener and passes them // to ServeOption handlers. func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error { + return ServeWithReady(node, lis, nil, options...) +} + +// ServeWithReady is like Serve but signals on the ready channel when the +// server is about to accept connections. The channel is closed right before +// server.Serve() is called. +// +// This is useful for callers that need to perform actions (like writing +// address files) only after the server is guaranteed to be accepting +// connections, avoiding race conditions where clients see the file before +// the server is ready. +// +// Passing nil for ready is equivalent to calling Serve(). +func ServeWithReady(node *core.IpfsNode, lis net.Listener, ready chan<- struct{}, options ...ServeOption) error { // make sure we close this no matter what. defer lis.Close() @@ -107,6 +121,9 @@ func Serve(node *core.IpfsNode, lis net.Listener, options ...ServeOption) error var serverError error serverClosed := make(chan struct{}) go func() { + if ready != nil { + close(ready) + } serverError = server.Serve(lis) close(serverClosed) }() diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index 61241bc20..26914aaab 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -12,6 +12,7 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [๐Ÿ”ฆ Highlights](#-highlights) - [Routing V1 HTTP API now exposed by default](#routing-v1-http-api-now-exposed-by-default) - [Track total size when adding pins](#track-total-size-when-adding-pins) + - [๐Ÿš‡ Improved `ipfs p2p` tunnels with foreground mode](#-improved-ipfs-p2p-tunnels-with-foreground-mode) - [Improved `ipfs dag stat` output](#improved-ipfs-dag-stat-output) - [Skip bad keys when listing](#skip_bad_keys_when_listing) - [๐Ÿ“ฆ๏ธ Dependency updates](#-dependency-updates) @@ -35,6 +36,23 @@ Example output: Fetched/Processed 336 nodes (83 MB) ``` +#### ๐Ÿš‡ Improved `ipfs p2p` tunnels with foreground mode + +P2P tunnels can now run like SSH port forwarding: start a tunnel, use it, and it cleans up automatically when you're done. + +The new `--foreground` (`-f`) flag for `ipfs p2p listen` and `ipfs p2p forward` keeps the command running until interrupted. When you Ctrl+C, send SIGTERM, or stop the service, the tunnel is removed automatically: + +```console +$ ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 --foreground +Listening on /x/ssh, forwarding to /ip4/127.0.0.1/tcp/22, waiting for interrupt... +^C +Received interrupt, removing listener for /x/ssh +``` + +Without `--foreground`, commands return immediately and tunnels persist until explicitly closed (existing behavior). + +See [docs/p2p-tunnels.md](https://github.com/ipfs/kubo/blob/master/docs/p2p-tunnels.md) for usage examples. + #### Improved `ipfs dag stat` output The `ipfs dag stat` command has been improved for better terminal UX: diff --git a/docs/config.md b/docs/config.md index d8284de2f..9d41ef4e5 100644 --- a/docs/config.md +++ b/docs/config.md @@ -59,6 +59,7 @@ config file at runtime. - [`Discovery.MDNS.Enabled`](#discoverymdnsenabled) - [`Discovery.MDNS.Interval`](#discoverymdnsinterval) - [`Experimental`](#experimental) + - [`Experimental.Libp2pStreamMounting`](#experimentallibp2pstreammounting) - [`Gateway`](#gateway) - [`Gateway.NoFetch`](#gatewaynofetch) - [`Gateway.NoDNSLink`](#gatewaynodnslink) @@ -1069,6 +1070,17 @@ in the [new mDNS implementation](https://github.com/libp2p/zeroconf#readme). Toggle and configure experimental features of Kubo. Experimental features are listed [here](./experimental-features.md). +### `Experimental.Libp2pStreamMounting` + +Enables the `ipfs p2p` commands for tunneling TCP connections through libp2p +streams, similar to SSH port forwarding. + +See [docs/p2p-tunnels.md](p2p-tunnels.md) for usage examples. + +Default: `false` + +Type: `bool` + ## `Gateway` Options for the HTTP gateway. diff --git a/docs/experimental-features.md b/docs/experimental-features.md index ad3fbdfed..fdca90fbe 100644 --- a/docs/experimental-features.md +++ b/docs/experimental-features.md @@ -199,9 +199,8 @@ configured, the daemon will fail to start. ## ipfs p2p -Allows tunneling of TCP connections through Libp2p streams. If you've ever used -port forwarding with SSH (the `-L` option in OpenSSH), this feature is quite -similar. +Allows tunneling of TCP connections through libp2p streams, similar to SSH port +forwarding (`ssh -L`). ### State @@ -220,98 +219,20 @@ Experimental, will be stabilized in 0.6.0 > If you enable this and plan to expose CLI or HTTP RPC to other users or machines, > secure RPC API using [`API.Authorizations`](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations) or custom auth middleware. -The `p2p` command needs to be enabled in the config: - ```sh > ipfs config --json Experimental.Libp2pStreamMounting true ``` ### How to use -**Netcat example:** - -First, pick a protocol name for your application. Think of the protocol name as -a port number, just significantly more user-friendly. In this example, we're -going to use `/x/kickass/1.0`. - -***Setup:*** - -1. A "server" node with peer ID `$SERVER_ID` -2. A "client" node. - -***On the "server" node:*** - -First, start your application and have it listen for TCP connections on -port `$APP_PORT`. - -Then, configure the p2p listener by running: - -```sh -> ipfs p2p listen /x/kickass/1.0 /ip4/127.0.0.1/tcp/$APP_PORT -``` - -This will configure IPFS to forward all incoming `/x/kickass/1.0` streams to -`127.0.0.1:$APP_PORT` (opening a new connection to `127.0.0.1:$APP_PORT` per -incoming stream. - -***On the "client" node:*** - -First, configure the client p2p dialer, so that it forwards all inbound -connections on `127.0.0.1:SOME_PORT` to the server node listening -on `/x/kickass/1.0`. - -```sh -> ipfs p2p forward /x/kickass/1.0 /ip4/127.0.0.1/tcp/$SOME_PORT /p2p/$SERVER_ID -``` - -Next, have your application open a connection to `127.0.0.1:$SOME_PORT`. This -connection will be forwarded to the service running on `127.0.0.1:$APP_PORT` on -the remote machine. You can test it with netcat: - -***On "server" node:*** -```sh -> nc -v -l -p $APP_PORT -``` - -***On "client" node:*** -```sh -> nc -v 127.0.0.1 $SOME_PORT -``` - -You should now see that a connection has been established and be able to -exchange messages between netcat instances. - -(note that depending on your netcat version you may need to drop the `-v` flag) - -**SSH example** - -**Setup:** - -1. A "server" node with peer ID `$SERVER_ID` and running ssh server on the - default port. -2. A "client" node. - -_you can get `$SERVER_ID` by running `ipfs id -f "\n"`_ - -***First, on the "server" node:*** - -```sh -ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 -``` - -***Then, on "client" node:*** - -```sh -ipfs p2p forward /x/ssh /ip4/127.0.0.1/tcp/2222 /p2p/$SERVER_ID -``` - -You should now be able to connect to your ssh server through a libp2p connection -with `ssh [user]@127.0.0.1 -p 2222`. - +See [docs/p2p-tunnels.md](p2p-tunnels.md) for usage examples, foreground mode, +and systemd integration. ### Road to being a real feature -- [ ] More documentation +- [x] More documentation +- [x] `ipfs p2p forward` mode +- [ ] Ability to define tunnels via JSON config, similar to [`Peering.Peers`](https://github.com/ipfs/kubo/blob/master/docs/config.md#peeringpeers), see [kubo#5460](https://github.com/ipfs/kubo/issues/5460) ## p2p http proxy diff --git a/docs/p2p-tunnels.md b/docs/p2p-tunnels.md new file mode 100644 index 000000000..9f3c310d8 --- /dev/null +++ b/docs/p2p-tunnels.md @@ -0,0 +1,214 @@ +# P2P Tunnels + +Kubo supports tunneling TCP connections through libp2p streams, similar to SSH +port forwarding (`ssh -L`). This allows exposing local services to remote peers +and forwarding remote services to local ports. + +- [Why P2P Tunnels?](#why-p2p-tunnels) +- [Quick Start](#quick-start) +- [Background Mode](#background-mode) +- [Foreground Mode](#foreground-mode) + - [systemd Integration](#systemd-integration) +- [Security Considerations](#security-considerations) +- [Troubleshooting](#troubleshooting) + +## Why P2P Tunnels? + +Unlike traditional SSH tunnels, libp2p-based tunnels do not require: + +- **No public IP or open ports**: The server does not need a static IP address + or port forwarding configured on the router. Connectivity to peers behind NAT + is facilitated by [Direct Connection Upgrade through Relay (DCUtR)](https://github.com/libp2p/specs/blob/master/relay/DCUtR.md), + which enables NAT hole-punching. + +- **No DNS or IP address management**: All you need is the server's PeerID and + an agreed-upon protocol name (e.g., `/x/ssh`). Kubo handles peer discovery + and routing via the [Amino DHT](https://specs.ipfs.tech/routing/kad-dht/). + +- **Simplified firewall rules**: Since connections are established through + libp2p's existing swarm connections, no additional firewall configuration is + needed beyond what Kubo already requires. + +This makes p2p tunnels useful for connecting to machines on home networks, +behind corporate firewalls, or in environments where traditional port forwarding +is not available. + +## Quick Start + +Enable the experimental feature: + +```console +$ ipfs config --json Experimental.Libp2pStreamMounting true +``` + +Test with netcat (`nc`) - no services required: + +**On the server:** + +```console +$ ipfs p2p listen /x/test /ip4/127.0.0.1/tcp/9999 +$ nc -l -p 9999 +``` + +**On the client:** + +Replace `$SERVER_ID` with the server's peer ID (get it with `ipfs id -f "\n"` +on the server). + +```console +$ ipfs p2p forward /x/test /ip4/127.0.0.1/tcp/9998 /p2p/$SERVER_ID +$ nc 127.0.0.1 9998 +``` + +Type in either terminal and the text appears in the other. Use Ctrl+C to exit. + +## Background Mode + +By default, `ipfs p2p listen` and `ipfs p2p forward` register the tunnel with +the daemon and return immediately. The tunnel persists until explicitly closed +with `ipfs p2p close` or the daemon shuts down. + +This example exposes a local SSH server (listening on `localhost:22`) to a +remote peer. The same pattern works for any TCP service. + +**On the server** (the machine running SSH): + +Register a p2p listener that forwards incoming connections to the local SSH +server. The protocol name `/x/ssh` is an arbitrary identifier that both peers +must agree on (the `/x/` prefix is required for custom protocols). + +```console +$ ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 +``` + +**On the client:** + +Create a local port (`2222`) that tunnels through libp2p to the server's SSH +service. + +```console +$ ipfs p2p forward /x/ssh /ip4/127.0.0.1/tcp/2222 /p2p/$SERVER_ID +``` + +Now connect to SSH through the tunnel: + +```console +$ ssh user@127.0.0.1 -p 2222 +``` + +**Other services:** To tunnel a different service, change the port and protocol +name. For example, to expose a web server on port 8080, use `/x/mywebapp` and +`/ip4/127.0.0.1/tcp/8080`. + +## Foreground Mode + +Use `--foreground` (`-f`) to block until interrupted. The tunnel is +automatically removed when the command exits: + +```console +$ ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 --foreground +Listening on /x/ssh, forwarding to /ip4/127.0.0.1/tcp/22, waiting for interrupt... +^C +Received interrupt, removing listener for /x/ssh +``` + +The listener/forwarder is automatically removed when: + +- The command receives Ctrl+C or SIGTERM +- `ipfs p2p close` is called +- The daemon shuts down + +This mode is useful for systemd services and scripts that need cleanup on exit. + +### systemd Integration + +The `--foreground` flag enables clean integration with systemd. The examples +below show how to run `ipfs p2p listen` as a user service that starts +automatically when the IPFS daemon is ready. + +Ensure IPFS daemon runs as a systemd user service. See +[misc/README.md](https://github.com/ipfs/kubo/blob/master/misc/README.md#systemd) +for setup instructions and where to place unit files. + +#### P2P listener with path-based activation + +Use a `.path` unit to wait for the daemon's RPC API to be ready before starting +the p2p listener. + +**`ipfs-p2p-tunnel.path`**: + +```systemd +[Unit] +Description=Monitor for IPFS daemon startup +After=ipfs.service +Requires=ipfs.service + +[Path] +PathExists=%h/.ipfs/api +Unit=ipfs-p2p-tunnel.service + +[Install] +WantedBy=default.target +``` + +The `%h` specifier expands to the user's home directory. If you use a custom +`IPFS_PATH`, adjust accordingly. + +**`ipfs-p2p-tunnel.service`**: + +```systemd +[Unit] +Description=IPFS p2p tunnel +Requires=ipfs.service + +[Service] +ExecStart=ipfs p2p listen /x/ssh /ip4/127.0.0.1/tcp/22 -f +Restart=on-failure +RestartSec=10 + +[Install] +WantedBy=default.target +``` + +#### Enabling the services + +```console +$ systemctl --user enable ipfs.service +$ systemctl --user enable ipfs-p2p-tunnel.path +$ systemctl --user start ipfs.service +``` + +The path unit monitors `~/.ipfs/api` and starts `ipfs-p2p-tunnel.service` +once the file exists. + +## Security Considerations + +> [!WARNING] +> This feature provides CLI and HTTP RPC users with the ability to set up port +> forwarding for localhost and LAN ports. If you enable this and plan to expose +> CLI or HTTP RPC to other users or machines, secure the RPC API using +> [`API.Authorizations`](https://github.com/ipfs/kubo/blob/master/docs/config.md#apiauthorizations) +> or custom auth middleware. + +## Troubleshooting + +### Foreground listener stops when terminal closes + +When using `--foreground`, the listener stops if the terminal closes. For +persistent foreground listeners, use a systemd service, `nohup`, `tmux`, or +`screen`. Without `--foreground`, the listener persists in the daemon regardless +of terminal state. + +### Connection refused errors + +Verify: + +1. The experimental feature is enabled: `ipfs config Experimental.Libp2pStreamMounting` +2. The listener is active: `ipfs p2p ls` +3. Both peers can connect: `ipfs swarm connect /p2p/$PEER_ID` + +### Persistent tunnel configuration + +There is currently no way to define tunnels in the Kubo JSON config file. Use +`--foreground` mode with a systemd service for persistent tunnels. Support for +configuring tunnels via JSON config may be added in the future (see [kubo#5460](https://github.com/ipfs/kubo/issues/5460) - PRs welcome!). diff --git a/misc/README.md b/misc/README.md index 28511d3fc..ea683519b 100644 --- a/misc/README.md +++ b/misc/README.md @@ -39,6 +39,12 @@ To run this in your user session, save it as `~/.config/systemd/user/ipfs.servic ``` Read more about `--user` services here: [wiki.archlinux.org:Systemd ](https://wiki.archlinux.org/index.php/Systemd/User#Automatic_start-up_of_systemd_user_instances) +#### P2P tunnel services + +For running `ipfs p2p listen` or `ipfs p2p forward` as systemd services, +see [docs/p2p-tunnels.md](../docs/p2p-tunnels.md) for examples using the +`--foreground` flag and path-based activation. + ### initd - Here is a full-featured sample service file: https://github.com/dylanPowers/ipfs-linux-service/blob/master/init.d/ipfs diff --git a/p2p/listener.go b/p2p/listener.go index f5942ffa0..823f68e81 100644 --- a/p2p/listener.go +++ b/p2p/listener.go @@ -20,6 +20,10 @@ type Listener interface { // close closes the listener. Does not affect child streams close() + + // Done returns a channel that is closed when the listener is closed. + // This allows callers to detect when a listener has been removed. + Done() <-chan struct{} } // Listeners manages a group of Listener implementations, @@ -73,15 +77,13 @@ func (r *Listeners) Register(l Listener) error { return nil } +// Close removes and closes all listeners for which matchFunc returns true. +// Returns the number of listeners closed. func (r *Listeners) Close(matchFunc func(listener Listener) bool) int { - todo := make([]Listener, 0) + var todo []Listener r.Lock() for _, l := range r.Listeners { - if !matchFunc(l) { - continue - } - - if _, ok := r.Listeners[l.key()]; ok { + if matchFunc(l) { delete(r.Listeners, l.key()) todo = append(todo, l) } diff --git a/p2p/local.go b/p2p/local.go index 98028c5d4..31f70e5fc 100644 --- a/p2p/local.go +++ b/p2p/local.go @@ -23,6 +23,7 @@ type localListener struct { peer peer.ID listener manet.Listener + done chan struct{} } // ForwardLocal creates new P2P stream to a remote listener. @@ -32,6 +33,7 @@ func (p2p *P2P) ForwardLocal(ctx context.Context, peer peer.ID, proto protocol.I p2p: p2p, proto: proto, peer: peer, + done: make(chan struct{}), } maListener, err := manet.Listen(bindAddr) @@ -98,6 +100,11 @@ func (l *localListener) setupStream(local manet.Conn) { func (l *localListener) close() { l.listener.Close() + close(l.done) +} + +func (l *localListener) Done() <-chan struct{} { + return l.done } func (l *localListener) Protocol() protocol.ID { diff --git a/p2p/remote.go b/p2p/remote.go index b867cb313..fb7b7ccba 100644 --- a/p2p/remote.go +++ b/p2p/remote.go @@ -25,6 +25,8 @@ type remoteListener struct { // reportRemote if set to true makes the handler send '\n' // to target before any data is forwarded reportRemote bool + + done chan struct{} } // ForwardRemote creates new p2p listener. @@ -36,6 +38,7 @@ func (p2p *P2P) ForwardRemote(ctx context.Context, proto protocol.ID, addr ma.Mu addr: addr, reportRemote: reportRemote, + done: make(chan struct{}), } if err := p2p.ListenersP2P.Register(listener); err != nil { @@ -99,7 +102,13 @@ func (l *remoteListener) TargetAddress() ma.Multiaddr { return l.addr } -func (l *remoteListener) close() {} +func (l *remoteListener) close() { + close(l.done) +} + +func (l *remoteListener) Done() <-chan struct{} { + return l.done +} func (l *remoteListener) key() protocol.ID { return l.proto diff --git a/test/cli/api_file_test.go b/test/cli/api_file_test.go new file mode 100644 index 000000000..a0ba30fd2 --- /dev/null +++ b/test/cli/api_file_test.go @@ -0,0 +1,104 @@ +package cli + +import ( + "net/http" + "os" + "os/exec" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/require" +) + +// TestAddressFileReady verifies that when address files ($IPFS_PATH/api and +// $IPFS_PATH/gateway) are created, the corresponding HTTP servers are ready +// to accept connections immediately. This prevents race conditions for tools +// like systemd path units that start services when these files appear. +func TestAddressFileReady(t *testing.T) { + t.Parallel() + + t.Run("api file", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + node := h.NewNode().Init() + + // Start daemon in background (don't use StartDaemon which waits for API) + res := node.Runner.MustRun(harness.RunRequest{ + Path: node.IPFSBin, + Args: []string{"daemon"}, + RunFunc: (*exec.Cmd).Start, + }) + node.Daemon = res + defer node.StopDaemon() + + // Poll for api file to appear + apiFile := filepath.Join(node.Dir, "api") + var fileExists bool + for i := 0; i < 100; i++ { + if _, err := os.Stat(apiFile); err == nil { + fileExists = true + break + } + time.Sleep(100 * time.Millisecond) + } + require.True(t, fileExists, "api file should be created") + + // Read the api file to get the address + apiAddr, err := node.TryAPIAddr() + require.NoError(t, err) + + // Extract IP and port from multiaddr + ip, err := apiAddr.ValueForProtocol(4) // P_IP4 + require.NoError(t, err) + port, err := apiAddr.ValueForProtocol(6) // P_TCP + require.NoError(t, err) + + // Immediately try to use the API - should work on first attempt + url := "http://" + ip + ":" + port + "/api/v0/id" + resp, err := http.Post(url, "", nil) + require.NoError(t, err, "RPC API should be ready immediately when api file exists") + defer resp.Body.Close() + require.Equal(t, http.StatusOK, resp.StatusCode) + }) + + t.Run("gateway file", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + node := h.NewNode().Init() + + // Start daemon in background + res := node.Runner.MustRun(harness.RunRequest{ + Path: node.IPFSBin, + Args: []string{"daemon"}, + RunFunc: (*exec.Cmd).Start, + }) + node.Daemon = res + defer node.StopDaemon() + + // Poll for gateway file to appear + gatewayFile := filepath.Join(node.Dir, "gateway") + var fileExists bool + for i := 0; i < 100; i++ { + if _, err := os.Stat(gatewayFile); err == nil { + fileExists = true + break + } + time.Sleep(100 * time.Millisecond) + } + require.True(t, fileExists, "gateway file should be created") + + // Read the gateway file to get the URL (already includes http:// prefix) + gatewayURL, err := os.ReadFile(gatewayFile) + require.NoError(t, err) + + // Immediately try to use the Gateway - should work on first attempt + url := strings.TrimSpace(string(gatewayURL)) + "/ipfs/bafkqaaa" // empty file CID + resp, err := http.Get(url) + require.NoError(t, err, "Gateway should be ready immediately when gateway file exists") + defer resp.Body.Close() + require.Equal(t, http.StatusOK, resp.StatusCode) + }) +} diff --git a/test/cli/p2p_test.go b/test/cli/p2p_test.go new file mode 100644 index 000000000..2400d7d8b --- /dev/null +++ b/test/cli/p2p_test.go @@ -0,0 +1,430 @@ +package cli + +import ( + "encoding/json" + "fmt" + "io" + "net" + "net/http" + "os/exec" + "slices" + "syscall" + "testing" + "time" + + "github.com/ipfs/kubo/core/commands" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/require" +) + +// waitForListenerCount waits until the node has exactly the expected number of listeners. +func waitForListenerCount(t *testing.T, node *harness.Node, expectedCount int) { + t.Helper() + require.Eventually(t, func() bool { + lsOut := node.IPFS("p2p", "ls", "--enc=json") + var lsResult commands.P2PLsOutput + if err := json.Unmarshal(lsOut.Stdout.Bytes(), &lsResult); err != nil { + return false + } + return len(lsResult.Listeners) == expectedCount + }, 5*time.Second, 100*time.Millisecond, "expected %d listeners", expectedCount) +} + +// waitForListenerProtocol waits until the node has a listener with the given protocol. +func waitForListenerProtocol(t *testing.T, node *harness.Node, protocol string) { + t.Helper() + require.Eventually(t, func() bool { + lsOut := node.IPFS("p2p", "ls", "--enc=json") + var lsResult commands.P2PLsOutput + if err := json.Unmarshal(lsOut.Stdout.Bytes(), &lsResult); err != nil { + return false + } + return slices.ContainsFunc(lsResult.Listeners, func(l commands.P2PListenerInfoOutput) bool { + return l.Protocol == protocol + }) + }, 5*time.Second, 100*time.Millisecond, "expected listener with protocol %s", protocol) +} + +func TestP2PForeground(t *testing.T) { + t.Parallel() + + t.Run("listen foreground creates listener and removes on interrupt", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + node.StartDaemon() + + listenPort := harness.NewRandPort() + + // Start foreground listener asynchronously + res := node.Runner.Run(harness.RunRequest{ + Path: node.IPFSBin, + Args: []string{"p2p", "listen", "--foreground", "/x/fgtest", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)}, + RunFunc: (*exec.Cmd).Start, + }) + require.NoError(t, res.Err) + + // Wait for listener to be created + waitForListenerProtocol(t, node, "/x/fgtest") + + // Send SIGTERM + _ = res.Cmd.Process.Signal(syscall.SIGTERM) + _ = res.Cmd.Wait() + + // Wait for listener to be removed + waitForListenerCount(t, node, 0) + }) + + t.Run("listen foreground text output on SIGTERM", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + node.StartDaemon() + + listenPort := harness.NewRandPort() + + // Run without --enc=json to test actual text output users see + res := node.Runner.Run(harness.RunRequest{ + Path: node.IPFSBin, + Args: []string{"p2p", "listen", "--foreground", "/x/sigterm", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)}, + RunFunc: (*exec.Cmd).Start, + }) + require.NoError(t, res.Err) + + waitForListenerProtocol(t, node, "/x/sigterm") + + _ = res.Cmd.Process.Signal(syscall.SIGTERM) + _ = res.Cmd.Wait() + + // Verify stdout shows "waiting for interrupt" message + stdout := res.Stdout.String() + require.Contains(t, stdout, "waiting for interrupt") + + // Note: "Received interrupt, removing listener" message is NOT visible to CLI on SIGTERM + // because the command runs in the daemon via RPC and the response stream closes before + // the message can be emitted. The important behavior is verified in the first test: + // the listener IS removed when SIGTERM is sent. + }) + + t.Run("forward foreground creates forwarder and removes on interrupt", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(2).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + }) + nodes.StartDaemons().Connect() + + forwardPort := harness.NewRandPort() + + // Start foreground forwarder asynchronously on node 0 + res := nodes[0].Runner.Run(harness.RunRequest{ + Path: nodes[0].IPFSBin, + Args: []string{"p2p", "forward", "--foreground", "/x/fgfwd", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", forwardPort), "/p2p/" + nodes[1].PeerID().String()}, + RunFunc: (*exec.Cmd).Start, + }) + require.NoError(t, res.Err) + + // Wait for forwarder to be created + waitForListenerCount(t, nodes[0], 1) + + // Send SIGTERM + _ = res.Cmd.Process.Signal(syscall.SIGTERM) + _ = res.Cmd.Wait() + + // Wait for forwarder to be removed + waitForListenerCount(t, nodes[0], 0) + }) + + t.Run("forward foreground text output on SIGTERM", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(2).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + }) + nodes.StartDaemons().Connect() + + forwardPort := harness.NewRandPort() + + // Run without --enc=json to test actual text output users see + res := nodes[0].Runner.Run(harness.RunRequest{ + Path: nodes[0].IPFSBin, + Args: []string{"p2p", "forward", "--foreground", "/x/fwdsigterm", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", forwardPort), "/p2p/" + nodes[1].PeerID().String()}, + RunFunc: (*exec.Cmd).Start, + }) + require.NoError(t, res.Err) + + waitForListenerCount(t, nodes[0], 1) + + _ = res.Cmd.Process.Signal(syscall.SIGTERM) + _ = res.Cmd.Wait() + + // Verify stdout shows "waiting for interrupt" message + stdout := res.Stdout.String() + require.Contains(t, stdout, "waiting for interrupt") + + // Note: "Received interrupt, removing forwarder" message is NOT visible to CLI on SIGTERM + // because the response stream closes before the message can be emitted. + }) + + t.Run("listen without foreground returns immediately and persists", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + node.StartDaemon() + + listenPort := harness.NewRandPort() + + // This should return immediately (not block) + node.IPFS("p2p", "listen", "/x/nofg", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)) + + // Listener should still exist + waitForListenerProtocol(t, node, "/x/nofg") + + // Clean up + node.IPFS("p2p", "close", "-p", "/x/nofg") + }) + + t.Run("listen foreground text output on p2p close", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + node.StartDaemon() + + listenPort := harness.NewRandPort() + + // Run without --enc=json to test actual text output users see + res := node.Runner.Run(harness.RunRequest{ + Path: node.IPFSBin, + Args: []string{"p2p", "listen", "--foreground", "/x/closetest", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)}, + RunFunc: (*exec.Cmd).Start, + }) + require.NoError(t, res.Err) + + // Wait for listener to be created + waitForListenerProtocol(t, node, "/x/closetest") + + // Close the listener via ipfs p2p close command + node.IPFS("p2p", "close", "-p", "/x/closetest") + + // Wait for foreground command to exit (it should exit quickly after close) + done := make(chan error, 1) + go func() { + done <- res.Cmd.Wait() + }() + + select { + case <-done: + // Good - command exited + case <-time.After(5 * time.Second): + _ = res.Cmd.Process.Kill() + t.Fatal("foreground command did not exit after listener was closed via ipfs p2p close") + } + + // Wait for listener to be removed + waitForListenerCount(t, node, 0) + + // Verify text output shows BOTH messages when closed via p2p close + // (unlike SIGTERM, the stream is still open so "Received interrupt" is emitted) + out := res.Stdout.String() + require.Contains(t, out, "waiting for interrupt") + require.Contains(t, out, "Received interrupt, removing listener") + }) + + t.Run("forward foreground text output on p2p close", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(2).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + }) + nodes.StartDaemons().Connect() + + forwardPort := harness.NewRandPort() + + // Run without --enc=json to test actual text output users see + res := nodes[0].Runner.Run(harness.RunRequest{ + Path: nodes[0].IPFSBin, + Args: []string{"p2p", "forward", "--foreground", "/x/fwdclose", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", forwardPort), "/p2p/" + nodes[1].PeerID().String()}, + RunFunc: (*exec.Cmd).Start, + }) + require.NoError(t, res.Err) + + // Wait for forwarder to be created + waitForListenerCount(t, nodes[0], 1) + + // Close the forwarder via ipfs p2p close command + nodes[0].IPFS("p2p", "close", "-a") + + // Wait for foreground command to exit + done := make(chan error, 1) + go func() { + done <- res.Cmd.Wait() + }() + + select { + case <-done: + // Good - command exited + case <-time.After(5 * time.Second): + _ = res.Cmd.Process.Kill() + t.Fatal("foreground command did not exit after forwarder was closed via ipfs p2p close") + } + + // Wait for forwarder to be removed + waitForListenerCount(t, nodes[0], 0) + + // Verify text output shows BOTH messages when closed via p2p close + out := res.Stdout.String() + require.Contains(t, out, "waiting for interrupt") + require.Contains(t, out, "Received interrupt, removing forwarder") + }) + + t.Run("listen foreground tunnel transfers data and cleans up on SIGTERM", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(2).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + }) + nodes.StartDaemons().Connect() + + httpServerPort := harness.NewRandPort() + forwardPort := harness.NewRandPort() + + // Start HTTP server + expectedBody := "Hello from p2p tunnel!" + httpServer := &http.Server{ + Addr: fmt.Sprintf("127.0.0.1:%d", httpServerPort), + Handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(expectedBody)) + }), + } + listener, err := net.Listen("tcp", httpServer.Addr) + require.NoError(t, err) + go func() { _ = httpServer.Serve(listener) }() + defer httpServer.Close() + + // Node 0: listen --foreground + listenRes := nodes[0].Runner.Run(harness.RunRequest{ + Path: nodes[0].IPFSBin, + Args: []string{"p2p", "listen", "--foreground", "/x/httptest", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", httpServerPort)}, + RunFunc: (*exec.Cmd).Start, + }) + require.NoError(t, listenRes.Err) + + // Wait for listener to be created + waitForListenerProtocol(t, nodes[0], "/x/httptest") + + // Node 1: forward (non-foreground) + nodes[1].IPFS("p2p", "forward", "/x/httptest", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", forwardPort), "/p2p/"+nodes[0].PeerID().String()) + + // Verify data flows through tunnel + resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:%d/", forwardPort)) + require.NoError(t, err) + body, err := io.ReadAll(resp.Body) + resp.Body.Close() + require.NoError(t, err) + require.Equal(t, expectedBody, string(body)) + + // Clean up forwarder on node 1 + nodes[1].IPFS("p2p", "close", "-a") + + // SIGTERM the listen --foreground command + _ = listenRes.Cmd.Process.Signal(syscall.SIGTERM) + _ = listenRes.Cmd.Wait() + + // Wait for listener to be removed on node 0 + waitForListenerCount(t, nodes[0], 0) + }) + + t.Run("forward foreground tunnel transfers data and cleans up on SIGTERM", func(t *testing.T) { + t.Parallel() + nodes := harness.NewT(t).NewNodes(2).Init() + nodes.ForEachPar(func(n *harness.Node) { + n.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + }) + nodes.StartDaemons().Connect() + + httpServerPort := harness.NewRandPort() + forwardPort := harness.NewRandPort() + + // Start HTTP server + expectedBody := "Hello from forward foreground tunnel!" + httpServer := &http.Server{ + Addr: fmt.Sprintf("127.0.0.1:%d", httpServerPort), + Handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(expectedBody)) + }), + } + listener, err := net.Listen("tcp", httpServer.Addr) + require.NoError(t, err) + go func() { _ = httpServer.Serve(listener) }() + defer httpServer.Close() + + // Node 0: listen (non-foreground) + nodes[0].IPFS("p2p", "listen", "/x/httptest", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", httpServerPort)) + + // Node 1: forward --foreground + forwardRes := nodes[1].Runner.Run(harness.RunRequest{ + Path: nodes[1].IPFSBin, + Args: []string{"p2p", "forward", "--foreground", "/x/httptest", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", forwardPort), "/p2p/" + nodes[0].PeerID().String()}, + RunFunc: (*exec.Cmd).Start, + }) + require.NoError(t, forwardRes.Err) + + // Wait for forwarder to be created + waitForListenerCount(t, nodes[1], 1) + + // Verify data flows through tunnel + resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:%d/", forwardPort)) + require.NoError(t, err) + body, err := io.ReadAll(resp.Body) + resp.Body.Close() + require.NoError(t, err) + require.Equal(t, expectedBody, string(body)) + + // SIGTERM the forward --foreground command + _ = forwardRes.Cmd.Process.Signal(syscall.SIGTERM) + _ = forwardRes.Cmd.Wait() + + // Wait for forwarder to be removed on node 1 + waitForListenerCount(t, nodes[1], 0) + + // Clean up listener on node 0 + nodes[0].IPFS("p2p", "close", "-a") + }) + + t.Run("foreground command exits when daemon shuts down", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + node.IPFS("config", "--json", "Experimental.Libp2pStreamMounting", "true") + node.StartDaemon() + + listenPort := harness.NewRandPort() + + // Start foreground listener + res := node.Runner.Run(harness.RunRequest{ + Path: node.IPFSBin, + Args: []string{"p2p", "listen", "--foreground", "/x/daemontest", fmt.Sprintf("/ip4/127.0.0.1/tcp/%d", listenPort)}, + RunFunc: (*exec.Cmd).Start, + }) + require.NoError(t, res.Err) + + // Wait for listener to be created + waitForListenerProtocol(t, node, "/x/daemontest") + + // Stop the daemon + node.StopDaemon() + + // Wait for foreground command to exit + done := make(chan error, 1) + go func() { + done <- res.Cmd.Wait() + }() + + select { + case <-done: + // Good - foreground command exited when daemon stopped + case <-time.After(5 * time.Second): + _ = res.Cmd.Process.Kill() + t.Fatal("foreground command did not exit when daemon was stopped") + } + }) +} From ac9ae9bf5daec2cef4589a24807a376a7e83d4be Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:45:46 -0800 Subject: [PATCH 18/49] fix(provide): do not output keystore error on shutdown (#11130) * Do not output keystore error on shutdown. Closes #11127 * fix: add debug log for keystore sync interrupted by shutdown log at DEBUG level when keystore sync is interrupted during shutdown, preserving error details for debugging while keeping normal output clean --------- Co-authored-by: Marcin Rataj --- core/node/provider.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/node/provider.go b/core/node/provider.go index 1470112bb..2e385927f 100644 --- a/core/node/provider.go +++ b/core/node/provider.go @@ -489,10 +489,14 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option { strategy := cfg.Provide.Strategy.WithDefault(config.DefaultProvideStrategy) logger.Infow("provider keystore sync started", "strategy", strategy) if err := syncKeystore(ctx); err != nil { - logger.Errorw("provider keystore sync failed", "err", err, "strategy", strategy) - } else { - logger.Infow("provider keystore sync completed", "strategy", strategy) + if ctx.Err() == nil { + logger.Errorw("provider keystore sync failed", "err", err, "strategy", strategy) + } else { + logger.Debugw("provider keystore sync interrupted by shutdown", "err", err, "strategy", strategy) + } + return } + logger.Infow("provider keystore sync completed", "strategy", strategy) }() gcCtx, c := context.WithCancel(context.Background()) From 07ea37e99f898df54fa5edc114c8495fd6374ea7 Mon Sep 17 00:00:00 2001 From: Guillaume Michel Date: Fri, 9 Jan 2026 21:17:37 +0100 Subject: [PATCH 19/49] fix(provider): wait for fullrt crawl completion before providing (#11137) Co-authored-by: Marcin Rataj --- core/node/provider.go | 21 ++++++++++++++++++++- docs/changelogs/v0.40.md | 6 ++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/core/node/provider.go b/core/node/provider.go index 2e385927f..57d70feb6 100644 --- a/core/node/provider.go +++ b/core/node/provider.go @@ -322,6 +322,25 @@ type dhtImpl interface { Host() host.Host MessageSender() dht_pb.MessageSender } + +type fullrtRouter struct { + *fullrt.FullRT +} + +// GetClosestPeers overrides fullrt.FullRT's GetClosestPeers and returns an +// error if the fullrt's initial network crawl isn't complete yet. +func (fr *fullrtRouter) GetClosestPeers(ctx context.Context, key string) ([]peer.ID, error) { + if !fr.Ready() { + return nil, errors.New("fullrt: initial network crawl still running") + } + return fr.FullRT.GetClosestPeers(ctx, key) +} + +var ( + _ dhtImpl = &dht.IpfsDHT{} + _ dhtImpl = &fullrtRouter{} +) + type addrsFilter interface { FilteredAddrs() []ma.Multiaddr } @@ -400,7 +419,7 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option { } case *fullrt.FullRT: if inDht != nil { - impl = inDht + impl = &fullrtRouter{inDht} } } if impl == nil { diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index 26914aaab..8b92baf34 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -15,6 +15,7 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [๐Ÿš‡ Improved `ipfs p2p` tunnels with foreground mode](#-improved-ipfs-p2p-tunnels-with-foreground-mode) - [Improved `ipfs dag stat` output](#improved-ipfs-dag-stat-output) - [Skip bad keys when listing](#skip_bad_keys_when_listing) + - [Accelerated DHT Client and Provide Sweep now work together](#accelerated-dht-client-and-provide-sweep-now-work-together) - [๐Ÿ“ฆ๏ธ Dependency updates](#-dependency-updates) - [๐Ÿ“ Changelog](#-changelog) - [๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Contributors](#-contributors) @@ -32,6 +33,7 @@ The [Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) is n Adds total size progress tracking of pinned nodes during `ipfs pin add --progress`. The output now shows the total size of the pinned dag. Example output: + ``` Fetched/Processed 336 nodes (83 MB) ``` @@ -81,6 +83,10 @@ Use `--progress=true` to force progress even when piped, or `--progress=false` t Change the `ipfs key list` behavior to log an error and continue listing keys when a key cannot be read from the keystore or decoded. +#### Accelerated DHT Client and Provide Sweep now work together + +Previously, provide operations could start before the Accelerated DHT Client discovered enough peers, causing sweep mode to lose its efficiency benefits. Now, providing waits for the initial network crawl (about 10 minutes). Your content will be properly distributed across DHT regions after initial DHT map is created. Check `ipfs provide stat` to see when providing begins. + #### ๐Ÿ“ฆ๏ธ Dependency updates - update `go-libp2p` to [v0.46.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.46.0) From de20a78a1f8005afea0f8268ac74038a001e808a Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 9 Jan 2026 21:40:38 +0100 Subject: [PATCH 20/49] fix(routing): defensive clone of AddrInfo from provider channel (#11120) Belt-and-suspenders defense against data races where routing subsystem (DHT or delegated routing) may reuse backing array. Clones AddrInfo before publishing to QueryEvent to ensure isolated copy. Closes https://github.com/ipfs/kubo/issues/11116 --- core/commands/cmdutils/utils.go | 12 ++++++++++++ core/commands/routing.go | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/core/commands/cmdutils/utils.go b/core/commands/cmdutils/utils.go index c793f516e..30249d016 100644 --- a/core/commands/cmdutils/utils.go +++ b/core/commands/cmdutils/utils.go @@ -2,12 +2,14 @@ package cmdutils import ( "fmt" + "slices" cmds "github.com/ipfs/go-ipfs-cmds" "github.com/ipfs/boxo/path" "github.com/ipfs/go-cid" coreiface "github.com/ipfs/kubo/core/coreiface" + "github.com/libp2p/go-libp2p/core/peer" ) const ( @@ -84,3 +86,13 @@ func PathOrCidPath(str string) (path.Path, error) { // Send back original err. return nil, originalErr } + +// CloneAddrInfo returns a copy of the AddrInfo with a cloned Addrs slice. +// This prevents data races if the sender reuses the backing array. +// See: https://github.com/ipfs/kubo/issues/11116 +func CloneAddrInfo(ai peer.AddrInfo) peer.AddrInfo { + return peer.AddrInfo{ + ID: ai.ID, + Addrs: slices.Clone(ai.Addrs), + } +} diff --git a/core/commands/routing.go b/core/commands/routing.go index a49e5dd32..5e1d5334d 100644 --- a/core/commands/routing.go +++ b/core/commands/routing.go @@ -11,6 +11,7 @@ import ( "github.com/ipfs/kubo/config" cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdutils" "github.com/ipfs/kubo/core/node" mh "github.com/multiformats/go-multihash" @@ -89,7 +90,7 @@ var findProvidersRoutingCmd = &cmds.Command{ defer cancel() pchan := n.Routing.FindProvidersAsync(ctx, c, numProviders) for p := range pchan { - np := p + np := cmdutils.CloneAddrInfo(p) routing.PublishQueryEvent(ctx, &routing.QueryEvent{ Type: routing.Provider, Responses: []*peer.AddrInfo{&np}, From 447109df6423898edb7bcf86a534606df3945d6d Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Sun, 11 Jan 2026 00:39:30 +0100 Subject: [PATCH 21/49] docs: clarify Routing.Type=custom as experimental (#11111) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: mark custom routing as experimental reorganize Routing.Type section for clarity, group production and experimental options, consolidate DHT explanation, add limitations section to delegated-routing.md documenting that HTTP-only routing cannot provide content reliably * chore(config): reorder Routing sections and improve callout formatting move DelegatedRouters after Type, add config option names to CAUTION headers * docs: address reviewer feedback on config.md - clarify that `auto` can be combined with custom URLs in `Routing.DelegatedRouters` - rename headers for consistency: `Routing.Routers.[name].Type`, `Routing.Routers.[name].Parameters`, `Routing.Methods` - replace deprecated Strategic Providing reference with `Provide.*` config - remove outdated caveat about 0.39 sweep limitation - wording: "likely suffer" โ†’ "will be most affected" * docs: remove redundant Summary section from delegated-routing.md the IMPORTANT callout and Motivation section already cover what users need to know. historical version info was noise for researchers trying to configure custom routing. addresses reviewer feedback from #11111. --------- Co-authored-by: Daniel Norman <2color@users.noreply.github.com> Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com> --- docs/config.md | 180 +++++++++++++++++++------------------- docs/delegated-routing.md | 63 ++++++++----- routing/delegated.go | 7 ++ 3 files changed, 139 insertions(+), 111 deletions(-) diff --git a/docs/config.md b/docs/config.md index 9d41ef4e5..986798296 100644 --- a/docs/config.md +++ b/docs/config.md @@ -157,14 +157,14 @@ config file at runtime. - [`Reprovider.Strategy`](#providestrategy) - [`Routing`](#routing) - [`Routing.Type`](#routingtype) + - [`Routing.DelegatedRouters`](#routingdelegatedrouters) - [`Routing.AcceleratedDHTClient`](#routingaccelerateddhtclient) - [`Routing.LoopbackAddressesOnLanDHT`](#routingloopbackaddressesonlandht) - [`Routing.IgnoreProviders`](#routingignoreproviders) - - [`Routing.DelegatedRouters`](#routingdelegatedrouters) - [`Routing.Routers`](#routingrouters) - - [`Routing.Routers: Type`](#routingrouters-type) - - [`Routing.Routers: Parameters`](#routingrouters-parameters) - - [`Routing: Methods`](#routing-methods) + - [`Routing.Routers.[name].Type`](#routingroutersnametype) + - [`Routing.Routers.[name].Parameters`](#routingroutersnameparameters) + - [`Routing.Methods`](#routingmethods) - [`Swarm`](#swarm) - [`Swarm.AddrFilters`](#swarmaddrfilters) - [`Swarm.DisableBandwidthMetrics`](#swarmdisablebandwidthmetrics) @@ -2575,58 +2575,70 @@ Contains options for content, peer, and IPNS routing mechanisms. ### `Routing.Type` -There are multiple routing options: "auto", "autoclient", "none", "dht", "dhtclient", "delegated", and "custom". +Controls how your node discovers content and peers on the network. -- **DEFAULT:** If unset, or set to "auto", your node will use the public IPFS DHT (aka "Amino") - and parallel [`Routing.DelegatedRouters`](#routingdelegatedrouters) for additional speed. +**Production options:** -- If set to "autoclient", your node will behave as in "auto" but without running a DHT server. +- **`auto`** (default): Uses both the public IPFS DHT (Amino) and HTTP routers + from [`Routing.DelegatedRouters`](#routingdelegatedrouters) for faster lookups. + Your node starts as a DHT client and automatically switches to server mode + when reachable from the public internet. -- If set to "none", your node will use _no_ routing system. You'll have to - explicitly connect to peers that have the content you're looking for. +- **`autoclient`**: Same as `auto`, but never runs a DHT server. + Use this if your node is behind a firewall or NAT. -- If set to "dht" (or "dhtclient"/"dhtserver"), your node will ONLY use the Amino DHT (no HTTP routers). +- **`dht`**: Uses only the Amino DHT (no HTTP routers). Automatically switches + between client and server mode based on reachability. -- If set to "custom", all default routers are disabled, and only ones defined in `Routing.Routers` will be used. +- **`dhtclient`**: DHT-only, always running as a client. Lower resource usage. -When the DHT is enabled, it can operate in two modes: client and server. +- **`dhtserver`**: DHT-only, always running as a server. + Only use this if your node is reachable from the public internet. -- In server mode, your node will query other peers for DHT records, and will - respond to requests from other peers (both requests to store records and - requests to retrieve records). +- **`none`**: Disables all routing. You must manually connect to peers. -- In client mode, your node will query the DHT as a client but will not respond - to requests from other peers. This mode is less resource-intensive than server - mode. +**About DHT client vs server mode:** +When the DHT is enabled, your node can operate as either a client or server. +In server mode, it queries other peers and responds to their queries - this helps +the network but uses more resources. In client mode, it only queries others without +responding, which is less resource-intensive. With `auto` or `dht`, your node starts +as a client and switches to server when it detects public reachability. -When `Routing.Type` is set to `auto` or `dht`, your node will start as a DHT client, and -switch to a DHT server when and if it determines that it's reachable from the -public internet (e.g., it's not behind a firewall). +> [!CAUTION] +> **`Routing.Type` Experimental options:** +> +> These modes are for research and testing only, not production use. +> They may change without notice between releases. +> +> - **`delegated`**: Uses only HTTP routers from [`Routing.DelegatedRouters`](#routingdelegatedrouters) +> and IPNS publishers from [`Ipns.DelegatedPublishers`](#ipnsdelegatedpublishers), +> without initializing the DHT. Useful when peer-to-peer connectivity is unavailable. +> Note: cannot provide content to the network (no DHT means no provider records). +> +> - **`custom`**: Disables all default routers. You define your own routing in +> [`Routing.Routers`](#routingrouters). See [delegated-routing.md](delegated-routing.md). -To force a specific Amino DHT-only mode, client or server, set `Routing.Type` to -`dhtclient` or `dhtserver` respectively. Please do not set this to `dhtserver` -unless you're sure your node is reachable from the public network. - -When `Routing.Type` is set to `auto` or `autoclient` your node will accelerate some types of routing -by leveraging [`Routing.DelegatedRouters`](#routingdelegatedrouters) HTTP endpoints compatible with [Delegated Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) -introduced in [IPIP-337](https://github.com/ipfs/specs/pull/337) -in addition to the Amino DHT. - -When `Routing.Type` is set to `delegated`, your node will use **only** HTTP delegated routers and IPNS publishers, -without initializing the Amino DHT at all. This mode is useful for environments where peer-to-peer DHT connectivity -is not available or desired, while still enabling content routing and IPNS publishing via HTTP APIs. -This mode requires configuring [`Routing.DelegatedRouters`](#routingdelegatedrouters) for content routing and -[`Ipns.DelegatedPublishers`](#ipnsdelegatedpublishers) for IPNS publishing. - -**Note:** `delegated` mode operates as read-only for content providing - your node cannot announce content to the network -since there is no DHT connectivity. Content providing is automatically disabled when using this routing type. - -[Advanced routing rules](https://github.com/ipfs/kubo/blob/master/docs/delegated-routing.md) can be configured in `Routing.Routers` after setting `Routing.Type` to `custom`. - -Default: `auto` (DHT + [`Routing.DelegatedRouters`](#routingdelegatedrouters)) +Default: `auto` Type: `optionalString` (`null`/missing means the default) +### `Routing.DelegatedRouters` + +An array of URL hostnames for delegated routers to be queried in addition to the Amino DHT when `Routing.Type` is set to `auto` (default) or `autoclient`. +These endpoints must support the [Delegated Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/). + +The special value `"auto"` uses delegated routers from [AutoConf](#autoconf) when enabled. +You can combine `"auto"` with custom URLs (e.g., `["auto", "https://custom.example.com"]`) to query both the default delegated routers and your own endpoints. The first `"auto"` entry gets substituted with autoconf values, and other URLs are preserved. + +> [!TIP] +> Delegated routing allows IPFS implementations to offload tasks like content routing, peer routing, and naming to a separate process or server while also benefiting from HTTP caching. +> +> One can run their own delegated router either by implementing the [Delegated Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) themselves, or by using [Someguy](https://github.com/ipfs/someguy), a turn-key implementation that proxies requests to other routing systems. A public utility instance of Someguy is hosted at [`https://delegated-ipfs.dev`](https://docs.ipfs.tech/concepts/public-utilities/#delegated-routing). + +Default: `["auto"]` + +Type: `array[string]` (URLs or `"auto"`) + ### `Routing.AcceleratedDHTClient` This alternative Amino DHT client with a Full-Routing-Table strategy will @@ -2656,27 +2668,21 @@ When it is enabled: This is critical to maintain to not harm the network. - The operations `ipfs stats dht` will default to showing information about the accelerated DHT client -**Caveats:** - -1. Running the accelerated client likely will result in more resource consumption (connections, RAM, CPU, bandwidth) - - Users that are limited in the number of parallel connections their machines/networks can perform will likely suffer - - The resource usage is not smooth as the client crawls the network in rounds and reproviding is similarly done in rounds - - Users who previously had a lot of content but were unable to advertise it on the network will see an increase in - egress bandwidth as their nodes start to advertise all of their CIDs into the network. If you have lots of data - entering your node that you don't want to advertise, then consider using [Provide Strategies](#providestrategy) - to reduce the number of CIDs that you are reproviding. Similarly, if you are running a node that deals mostly with - short-lived temporary data (e.g. you use a separate node for ingesting data then for storing and serving it) then - you may benefit from using [Strategic Providing](experimental-features.md#strategic-providing) to prevent advertising - of data that you ultimately will not have. -2. Currently, the DHT is not usable for queries for the first 5-10 minutes of operation as the routing table is being -prepared. This means operations like searching the DHT for particular peers or content will not work initially. - - You can see if the DHT has been initially populated by running `ipfs stats dht` -3. Currently, the accelerated DHT client is not compatible with LAN-based DHTs and will not perform operations against -them -4. (โš ๏ธ 0.39 limitation) When used with [`Provide.DHT.SweepEnabled`](#providedhtsweepenabled), the sweep provider may -fail to estimate DHT size during the accelerated client's network crawl, resulting in all CIDs grouped into a -single region. Content still gets reprovided, but without sweep efficiency gains. Consider disabling the -accelerated client when using sweep mode. +> [!CAUTION] +> **`Routing.AcceleratedDHTClient` Caveats:** +> +> 1. Running the accelerated client likely will result in more resource consumption (connections, RAM, CPU, bandwidth) +> - Users that are limited in the number of parallel connections their machines/networks can perform will be most affected +> - The resource usage is not smooth as the client crawls the network in rounds and reproviding is similarly done in rounds +> - Users who previously had a lot of content but were unable to advertise it on the network will see an increase in +> egress bandwidth as their nodes start to advertise all of their CIDs into the network. If you have lots of data +> entering your node that you don't want to advertise, consider using [`Provide.*`](#provide) configuration +> to control which CIDs are reprovided. +> 2. Currently, the DHT is not usable for queries for the first 5-10 minutes of operation as the routing table is being +> prepared. This means operations like searching the DHT for particular peers or content will not work initially. +> - You can see if the DHT has been initially populated by running `ipfs stats dht` +> 3. Currently, the accelerated DHT client is not compatible with LAN-based DHTs and will not perform operations against +> them. Default: `false` @@ -2710,30 +2716,18 @@ Default: `[]` Type: `array[string]` -### `Routing.DelegatedRouters` - -An array of URL hostnames for delegated routers to be queried in addition to the Amino DHT when `Routing.Type` is set to `auto` (default) or `autoclient`. -These endpoints must support the [Delegated Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/). - -The special value `"auto"` uses delegated routers from [AutoConf](#autoconf) when enabled. - -> [!TIP] -> Delegated routing allows IPFS implementations to offload tasks like content routing, peer routing, and naming to a separate process or server while also benefiting from HTTP caching. -> -> One can run their own delegated router either by implementing the [Delegated Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) themselves, or by using [Someguy](https://github.com/ipfs/someguy), a turn-key implementation that proxies requests to other routing systems. A public utility instance of Someguy is hosted at [`https://delegated-ipfs.dev`](https://docs.ipfs.tech/concepts/public-utilities/#delegated-routing). - -Default: `["auto"]` - -Type: `array[string]` (URLs or `"auto"`) - ### `Routing.Routers` Alternative configuration used when `Routing.Type=custom`. -> [!WARNING] -> **EXPERIMENTAL: `Routing.Routers` configuration may change in future release** +> [!CAUTION] +> **EXPERIMENTAL: `Routing.Routers` is for research and testing only, not production use.** > -> Consider this advanced low-level config: Most users can simply use `Routing.Type=auto` or `autoclient` and set up basic config in user-friendly [`Routing.DelegatedRouters`](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingdelegatedrouters). +> - The configuration format and behavior may change without notice between releases. +> - Bugs and regressions may not be prioritized. +> - HTTP-only configurations cannot reliably provide content. See [delegated-routing.md](delegated-routing.md#limitations). +> +> Most users should use `Routing.Type=auto` or `autoclient` with [`Routing.DelegatedRouters`](#routingdelegatedrouters). Allows for replacing the default routing (Amino DHT) with alternative Router implementations. @@ -2744,9 +2738,9 @@ Default: `{}` Type: `object[string->object]` -#### `Routing.Routers: Type` +#### `Routing.Routers.[name].Type` -**EXPERIMENTAL: `Routing.Routers` configuration may change in future release** +**โš ๏ธ EXPERIMENTAL: For research and testing only. May change without notice.** It specifies the routing type that will be created. @@ -2758,9 +2752,9 @@ Currently supported types: Type: `string` -#### `Routing.Routers: Parameters` +#### `Routing.Routers.[name].Parameters` -**EXPERIMENTAL: `Routing.Routers` configuration may change in future release** +**โš ๏ธ EXPERIMENTAL: For research and testing only. May change without notice.** Parameters needed to create the specified router. Supported params per router type: @@ -2797,14 +2791,18 @@ Default: `{}` (use the safe implicit defaults) Type: `object[string->string]` -### `Routing: Methods` +### `Routing.Methods` `Methods:map` will define which routers will be executed per method used when `Routing.Type=custom`. -> [!WARNING] -> **EXPERIMENTAL: `Routing.Routers` configuration may change in future release** +> [!CAUTION] +> **EXPERIMENTAL: `Routing.Methods` is for research and testing only, not production use.** > -> Consider this advanced low-level config: Most users can simply use `Routing.Type=auto` or `autoclient` and set up basic config in user-friendly [`Routing.DelegatedRouters`](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingdelegatedrouters). +> - The configuration format and behavior may change without notice between releases. +> - Bugs and regressions may not be prioritized. +> - HTTP-only configurations cannot reliably provide content. See [delegated-routing.md](delegated-routing.md#limitations). +> +> Most users should use `Routing.Type=auto` or `autoclient` with [`Routing.DelegatedRouters`](#routingdelegatedrouters). The key will be the name of the method: `"provide"`, `"find-providers"`, `"find-peers"`, `"put-ipns"`, `"get-ipns"`. All methods must be added to the list. diff --git a/docs/delegated-routing.md b/docs/delegated-routing.md index ddc4e48ed..5e781c147 100644 --- a/docs/delegated-routing.md +++ b/docs/delegated-routing.md @@ -1,4 +1,15 @@ -# New multi-router configuration system +# Delegated Routing Notes + +- Status Date: 2025-12 + +> [!IMPORTANT] +> Most users are best served by setting delegated HTTP router URLs in [`Routing.DelegatedRouters`](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingdelegatedrouters) and `Routing.Type` to `auto` or `autoclient`, rather than using custom routing with `Routing.Routers` and `Routing.Methods` directly. +> +> The rest of this documentation describes experimental features intended only for researchers and advanced users. + +---- + +# Custom Multi-Router Configuration (Experimental) - Start Date: 2022-08-15 - Related Issues: @@ -6,27 +17,16 @@ - https://github.com/ipfs/kubo/issues/9079 - https://github.com/ipfs/kubo/pull/9877 -## Summary - -Previously we only used the Amino DHT for content routing and content -providing. - -Kubo 0.14 introduced experimental support for [delegated routing](https://github.com/ipfs/kubo/pull/8997), -which then got changed and standardized as [Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/). - -Kubo 0.23.0 release added support for [self-hosting Routing V1 HTTP API server](https://github.com/ipfs/kubo/blob/master/docs/changelogs/v0.23.md#self-hosting-routingv1-endpoint-for-delegated-routing-needs). - - -> [!TIP] -> Kubo 0.35 added support for [`Routing.DelegatedRouters`](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingdelegatedrouters). +> [!CAUTION] +> **`Routing.Type=custom` with `Routing.Routers` and `Routing.Methods` is EXPERIMENTAL.** > -> Most of users are best served by setting delegated HTTP router URLs there and `Routing.Type` to `auto` or `autoclient`, rather than custom routing with complex `Routing.Routers` and `Routing.Methods` directly. +> This feature is provided for **research and testing purposes only**. It is **not suitable for production use**. > -> The rest of this documentation should be considered only by advanced users and researchers. - -Now we need a better way to add different routers using different protocols -like [Routing V1](https://specs.ipfs.tech/routing/http-routing-v1/) or Amino -DHT, and be able to configure them (future routing systems to come) to cover different use cases. +> - The configuration format and behavior may change without notice between Kubo releases. +> - Bugs and regressions affecting custom routing may not be prioritized or fixed promptly. +> - HTTP-only routing configurations (without DHT) cannot reliably provide content to the network (๐Ÿ‘‰๏ธ see [Limitations](#limitations) below). +> +> **For production deployments**, use `Routing.Type=auto` (default) or `Routing.Type=autoclient` with [`Routing.DelegatedRouters`](https://github.com/ipfs/kubo/blob/master/docs/config.md#routingdelegatedrouters). ## Motivation @@ -362,6 +362,29 @@ I got ideas from all of the following links to create this design document: - https://www.notion.so/pl-strflt/Delegated-Routing-Thoughts-very-very-WIP-0543bc51b1bd4d63a061b0f28e195d38 - https://gist.github.com/guseggert/effa027ff4cbadd7f67598efb6704d12 +### Limitations + +#### HTTP-only routing cannot reliably provide content + +Configurations that use only HTTP routers (without any DHT router) are unable to reliably announce content (provider records) to the network. + +This limitation exists because: + +1. **No standardized HTTP API for providing**: The [Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) spec only defines read operations (`GET /routing/v1/providers/{cid}`). The write operation (`PUT /routing/v1/providers`) was never standardized. + +2. **Legacy experimental API**: The only available HTTP providing mechanism is an undocumented `PUT /routing/v1/providers` request format called `ProvideBitswap`, which is a historical experiment. See [IPIP-526](https://github.com/ipfs/specs/pull/526) for ongoing discussion about formalizing HTTP-based provider announcements. + +3. **Provider system integration**: Kubo's default provider system (`Provide.DHT.SweepEnabled=true` since v0.38) is designed for DHT-based providing. When no DHT is configured, the provider system may silently skip HTTP routers or behave unexpectedly. + +**Workarounds for testing:** + +If you need to test HTTP providing, you can try: + +- Setting `Provide.DHT.SweepEnabled=false` to use the legacy provider system +- Including at least one DHT router in your custom configuration alongside HTTP routers + +These workarounds are not guaranteed to work across Kubo versions and should not be relied upon for production use. + ### Copyright Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). diff --git a/routing/delegated.go b/routing/delegated.go index 9f6a39667..1c6d45ae1 100644 --- a/routing/delegated.go +++ b/routing/delegated.go @@ -31,6 +31,13 @@ import ( var log = logging.Logger("routing/delegated") +// Parse creates a composed router from the custom routing configuration. +// +// EXPERIMENTAL: Custom routing (Routing.Type=custom with Routing.Routers and +// Routing.Methods) is for research and testing only, not production use. +// The configuration format and behavior may change without notice between +// releases. HTTP-only configurations cannot reliably provide content. +// See docs/delegated-routing.md for limitations. func Parse(routers config.Routers, methods config.Methods, extraDHT *ExtraDHTParams, extraHTTP *ExtraHTTPParams) (routing.Routing, error) { if err := methods.Check(); err != nil { return nil, err From 56bf782cc64d7b7d4b1abc500ea71ecceedbca70 Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Tue, 13 Jan 2026 02:38:44 -0800 Subject: [PATCH 22/49] test: fix flaky ipfswatch test (#11142) Gracefully shutdown the ipfs node to ensure data is saved. Forceful shutdown is done if there is an error sending the interrupt signal to the process, such as on Windows. --- test/cli/ipfswatch_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/cli/ipfswatch_test.go b/test/cli/ipfswatch_test.go index 07e73a7ec..cd6859176 100644 --- a/test/cli/ipfswatch_test.go +++ b/test/cli/ipfswatch_test.go @@ -86,7 +86,9 @@ func TestIPFSWatch(t *testing.T) { // Kill ipfswatch to release the repo lock if result.Cmd.Process != nil { - _ = result.Cmd.Process.Kill() + if err = result.Cmd.Process.Signal(os.Interrupt); err != nil { + _ = result.Cmd.Process.Kill() + } _, _ = result.Cmd.Process.Wait() } From ec973aeb38c7ac28fede7b72a1515a2ee8ff5155 Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Tue, 13 Jan 2026 02:42:23 -0800 Subject: [PATCH 23/49] feat: improved go-ds-flatfs (#11092) * datastore: upgrade go-ds-flatfs to v0.6.0 See: https://github.com/ipfs/go-ds-flatfs/pull/142 * docs(changelog): add go-ds-flatfs atomic batch writes *documents the new flatfs batch implementation that uses atomic operations via temp directory, preventing orphan blocks on interrupted imports and reducing memory usage. * includes improved tests, batch cleanup fixes, and docs * docs(changelog): reframe go-ds-flatfs entry for users focus on user benefits instead of implementation details --- docs/changelogs/v0.40.md | 9 +++++++++ docs/examples/kubo-as-a-library/go.mod | 2 +- docs/examples/kubo-as-a-library/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index 8b92baf34..adc7f252c 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -10,6 +10,7 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [Overview](#overview) - [๐Ÿ”ฆ Highlights](#-highlights) + - [๐Ÿงน Automatic cleanup of interrupted imports](#-automatic-cleanup-of-interrupted-imports) - [Routing V1 HTTP API now exposed by default](#routing-v1-http-api-now-exposed-by-default) - [Track total size when adding pins](#track-total-size-when-adding-pins) - [๐Ÿš‡ Improved `ipfs p2p` tunnels with foreground mode](#-improved-ipfs-p2p-tunnels-with-foreground-mode) @@ -24,6 +25,14 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. ### ๐Ÿ”ฆ Highlights +#### ๐Ÿงน Automatic cleanup of interrupted imports + +If you cancel `ipfs add` or `ipfs dag import` mid-operation, Kubo now automatically cleans up incomplete data on the next daemon start. Previously, interrupted imports would leave orphan blocks in your repository that were difficult to identify and remove without pins and running explicit garbage collection. + +Batch operations also use less memory now. Block data is written to disk immediately rather than held in RAM until the batch commits. + +Under the hood, the block storage layer (flatfs) was rewritten to use atomic batch operations via a temporary staging directory. See [go-ds-flatfs#142](https://github.com/ipfs/go-ds-flatfs/pull/142) for details. + #### Routing V1 HTTP API now exposed by default The [Routing V1 HTTP API](https://specs.ipfs.tech/routing/http-routing-v1/) is now exposed by default at `http://127.0.0.1:8080/routing/v1`. This allows light clients in browsers to use Kubo Gateway as a delegated routing backend instead of running a full DHT client. Support for [IPIP-476: Delegated Routing DHT Closest Peers API](https://github.com/ipfs/specs/pull/476) is included. Can be disabled via [`Gateway.ExposeRoutingAPI`](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewayexposeroutingapi). diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index a747ec1dc..f59d136d6 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -78,7 +78,7 @@ require ( github.com/ipfs/go-cidutil v0.1.0 // indirect github.com/ipfs/go-datastore v0.9.0 // indirect github.com/ipfs/go-ds-badger v0.3.4 // indirect - github.com/ipfs/go-ds-flatfs v0.5.5 // indirect + github.com/ipfs/go-ds-flatfs v0.6.0 // indirect github.com/ipfs/go-ds-leveldb v0.5.2 // indirect github.com/ipfs/go-ds-measure v0.2.2 // indirect github.com/ipfs/go-ds-pebble v0.5.8 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index e198bdb79..790250ba1 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -288,8 +288,8 @@ github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46U github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= github.com/ipfs/go-ds-badger v0.3.4 h1:MmqFicftE0KrwMC77WjXTrPuoUxhwyFsjKONSeWrlOo= github.com/ipfs/go-ds-badger v0.3.4/go.mod h1:HfqsKJcNnIr9ZhZ+rkwS1J5PpaWjJjg6Ipmxd7KPfZ8= -github.com/ipfs/go-ds-flatfs v0.5.5 h1:lkx5C99pFBMI7T1sYF7y3v7xIYekNVNMp/95Gm9Y3tY= -github.com/ipfs/go-ds-flatfs v0.5.5/go.mod h1:bM7+m7KFUyv5dp3RBKTr3+OHgZ6h8ydCQkO7tjeO9Z4= +github.com/ipfs/go-ds-flatfs v0.6.0 h1:olAEnDNBK1VMoTRZvfzgo90H5kBP4qIZPpYMtNlBBws= +github.com/ipfs/go-ds-flatfs v0.6.0/go.mod h1:p8a/YhmAFYyuonxDbvuIANlDCgS69uqVv+iH5f8fAxY= github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= github.com/ipfs/go-ds-leveldb v0.5.2 h1:6nmxlQ2zbp4LCNdJVsmHfs9GP0eylfBNxpmY1csp0x0= github.com/ipfs/go-ds-leveldb v0.5.2/go.mod h1:2fAwmcvD3WoRT72PzEekHBkQmBDhc39DJGoREiuGmYo= diff --git a/go.mod b/go.mod index 5e653e9de..cba15d0a5 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/ipfs/go-datastore v0.9.0 github.com/ipfs/go-detect-race v0.0.1 github.com/ipfs/go-ds-badger v0.3.4 - github.com/ipfs/go-ds-flatfs v0.5.5 + github.com/ipfs/go-ds-flatfs v0.6.0 github.com/ipfs/go-ds-leveldb v0.5.2 github.com/ipfs/go-ds-measure v0.2.2 github.com/ipfs/go-ds-pebble v0.5.8 diff --git a/go.sum b/go.sum index f5f690e1b..b5833a54a 100644 --- a/go.sum +++ b/go.sum @@ -359,8 +359,8 @@ github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46U github.com/ipfs/go-ds-badger v0.0.7/go.mod h1:qt0/fWzZDoPW6jpQeqUjR5kBfhDNB65jd9YlmAvpQBk= github.com/ipfs/go-ds-badger v0.3.4 h1:MmqFicftE0KrwMC77WjXTrPuoUxhwyFsjKONSeWrlOo= github.com/ipfs/go-ds-badger v0.3.4/go.mod h1:HfqsKJcNnIr9ZhZ+rkwS1J5PpaWjJjg6Ipmxd7KPfZ8= -github.com/ipfs/go-ds-flatfs v0.5.5 h1:lkx5C99pFBMI7T1sYF7y3v7xIYekNVNMp/95Gm9Y3tY= -github.com/ipfs/go-ds-flatfs v0.5.5/go.mod h1:bM7+m7KFUyv5dp3RBKTr3+OHgZ6h8ydCQkO7tjeO9Z4= +github.com/ipfs/go-ds-flatfs v0.6.0 h1:olAEnDNBK1VMoTRZvfzgo90H5kBP4qIZPpYMtNlBBws= +github.com/ipfs/go-ds-flatfs v0.6.0/go.mod h1:p8a/YhmAFYyuonxDbvuIANlDCgS69uqVv+iH5f8fAxY= github.com/ipfs/go-ds-leveldb v0.1.0/go.mod h1:hqAW8y4bwX5LWcCtku2rFNX3vjDZCy5LZCg+cSZvYb8= github.com/ipfs/go-ds-leveldb v0.5.2 h1:6nmxlQ2zbp4LCNdJVsmHfs9GP0eylfBNxpmY1csp0x0= github.com/ipfs/go-ds-leveldb v0.5.2/go.mod h1:2fAwmcvD3WoRT72PzEekHBkQmBDhc39DJGoREiuGmYo= From 39c609b3db9b470e6c53e6936ef41d53d829aef0 Mon Sep 17 00:00:00 2001 From: Lucas Molas Date: Tue, 13 Jan 2026 17:47:36 -0300 Subject: [PATCH 24/49] feat(mfs): chroot command to change the root (#8648) Co-authored-by: Marcin Rataj --- core/commands/commands_test.go | 1 + core/commands/files.go | 168 ++++++++++++++++++++++++++++++--- core/node/core.go | 13 ++- docs/changelogs/v0.40.md | 17 ++++ test/cli/files_test.go | 106 +++++++++++++++++++++ 5 files changed, 288 insertions(+), 17 deletions(-) diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index 23782f209..893c7352c 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -90,6 +90,7 @@ func TestCommands(t *testing.T) { "/files/stat", "/files/write", "/files/chmod", + "/files/chroot", "/files/touch", "/filestore", "/filestore/dups", diff --git a/core/commands/files.go b/core/commands/files.go index 86331cbaa..6d0d708fc 100644 --- a/core/commands/files.go +++ b/core/commands/files.go @@ -16,11 +16,15 @@ import ( "time" humanize "github.com/dustin/go-humanize" + oldcmds "github.com/ipfs/kubo/commands" "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/core" "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/node" + fsrepo "github.com/ipfs/kubo/repo/fsrepo" bservice "github.com/ipfs/boxo/blockservice" + bstore "github.com/ipfs/boxo/blockstore" offline "github.com/ipfs/boxo/exchange/offline" dag "github.com/ipfs/boxo/ipld/merkledag" ft "github.com/ipfs/boxo/ipld/unixfs" @@ -28,6 +32,7 @@ import ( "github.com/ipfs/boxo/path" cid "github.com/ipfs/go-cid" cidenc "github.com/ipfs/go-cidutil/cidenc" + "github.com/ipfs/go-datastore" cmds "github.com/ipfs/go-ipfs-cmds" ipld "github.com/ipfs/go-ipld-format" logging "github.com/ipfs/go-log/v2" @@ -120,18 +125,19 @@ performance.`, cmds.BoolOption(filesFlushOptionName, "f", "Flush target and ancestors after write.").WithDefault(true), }, Subcommands: map[string]*cmds.Command{ - "read": filesReadCmd, - "write": filesWriteCmd, - "mv": filesMvCmd, - "cp": filesCpCmd, - "ls": filesLsCmd, - "mkdir": filesMkdirCmd, - "stat": filesStatCmd, - "rm": filesRmCmd, - "flush": filesFlushCmd, - "chcid": filesChcidCmd, - "chmod": filesChmodCmd, - "touch": filesTouchCmd, + "read": filesReadCmd, + "write": filesWriteCmd, + "mv": filesMvCmd, + "cp": filesCpCmd, + "ls": filesLsCmd, + "mkdir": filesMkdirCmd, + "stat": filesStatCmd, + "rm": filesRmCmd, + "flush": filesFlushCmd, + "chcid": filesChcidCmd, + "chmod": filesChmodCmd, + "chroot": filesChrootCmd, + "touch": filesTouchCmd, }, } @@ -1648,3 +1654,141 @@ Examples: return mfs.Touch(nd.FilesRoot, path, ts) }, } + +const chrootConfirmOptionName = "confirm" + +var filesChrootCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Change the MFS root CID.", + ShortDescription: ` +'ipfs files chroot' changes the root CID used by MFS (Mutable File System). +This is a recovery command for when MFS becomes corrupted and prevents the +daemon from starting. + +When run without a CID argument, resets MFS to an empty directory. + +WARNING: The old MFS root and its unpinned children will be removed during +the next garbage collection. Pin the old root first if you want to preserve. + +This command can only run when the daemon is not running. + +Examples: + + # Reset MFS to empty directory (recovery from corruption) + $ ipfs files chroot --confirm + + # Restore MFS to a known good directory CID + $ ipfs files chroot --confirm QmYourBackupCID +`, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("cid", false, false, "New root CID (defaults to empty directory if not specified)."), + }, + Options: []cmds.Option{ + cmds.BoolOption(chrootConfirmOptionName, "Confirm this potentially destructive operation."), + }, + NoRemote: true, + Extra: CreateCmdExtras(SetDoesNotUseRepo(true)), + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + confirm, _ := req.Options[chrootConfirmOptionName].(bool) + if !confirm { + return errors.New("this is a potentially destructive operation; pass --confirm to proceed") + } + + // Determine new root CID + var newRootCid cid.Cid + if len(req.Arguments) > 0 { + var err error + newRootCid, err = cid.Decode(req.Arguments[0]) + if err != nil { + return fmt.Errorf("invalid CID %q: %w", req.Arguments[0], err) + } + } else { + // Default to empty directory + newRootCid = ft.EmptyDirNode().Cid() + } + + // Get config root to open repo directly + cctx := env.(*oldcmds.Context) + cfgRoot := cctx.ConfigRoot + + // Open repo directly (daemon must not be running) + repo, err := fsrepo.Open(cfgRoot) + if err != nil { + return fmt.Errorf("opening repo (is the daemon running?): %w", err) + } + defer repo.Close() + + localDS := repo.Datastore() + bs := bstore.NewBlockstore(localDS) + + // Check new root exists locally and is a directory + hasBlock, err := bs.Has(req.Context, newRootCid) + if err != nil { + return fmt.Errorf("checking if new root exists: %w", err) + } + if !hasBlock { + // Special case: empty dir is always available (hardcoded in boxo) + emptyDirCid := ft.EmptyDirNode().Cid() + if !newRootCid.Equals(emptyDirCid) { + return fmt.Errorf("new root %s does not exist locally; fetch it first with 'ipfs block get'", newRootCid) + } + } + + // Validate it's a directory (not a file) + if hasBlock { + blk, err := bs.Get(req.Context, newRootCid) + if err != nil { + return fmt.Errorf("reading new root block: %w", err) + } + pbNode, err := dag.DecodeProtobuf(blk.RawData()) + if err != nil { + return fmt.Errorf("new root is not a valid dag-pb node: %w", err) + } + fsNode, err := ft.FSNodeFromBytes(pbNode.Data()) + if err != nil { + return fmt.Errorf("new root is not a valid UnixFS node: %w", err) + } + if fsNode.Type() != ft.TDirectory && fsNode.Type() != ft.THAMTShard { + return fmt.Errorf("new root must be a directory, got %s", fsNode.Type()) + } + } + + // Get old root for display (if exists) + var oldRootStr string + oldRootBytes, err := localDS.Get(req.Context, node.FilesRootDatastoreKey) + if err == nil { + oldRootCid, err := cid.Cast(oldRootBytes) + if err == nil { + oldRootStr = oldRootCid.String() + } + } else if !errors.Is(err, datastore.ErrNotFound) { + return fmt.Errorf("reading current MFS root: %w", err) + } + + // Write new root + err = localDS.Put(req.Context, node.FilesRootDatastoreKey, newRootCid.Bytes()) + if err != nil { + return fmt.Errorf("writing new MFS root: %w", err) + } + + // Build output message + var msg string + if oldRootStr != "" { + msg = fmt.Sprintf("MFS root changed from %s to %s\n", oldRootStr, newRootCid) + msg += fmt.Sprintf("The old root %s will be garbage collected unless pinned.\n", oldRootStr) + } else { + msg = fmt.Sprintf("MFS root set to %s\n", newRootCid) + } + + return cmds.EmitOnce(res, &MessageOutput{Message: msg}) + }, + Type: MessageOutput{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, out *MessageOutput) error { + _, err := fmt.Fprint(w, out.Message) + return err + }), + }, +} diff --git a/core/node/core.go b/core/node/core.go index a636a0c54..06e786f1f 100644 --- a/core/node/core.go +++ b/core/node/core.go @@ -30,6 +30,9 @@ import ( "github.com/ipfs/kubo/repo" ) +// FilesRootDatastoreKey is the datastore key for the MFS files root CID. +var FilesRootDatastoreKey = datastore.NewKey("/local/filesroot") + // BlockService creates new blockservice which provides an interface to fetch content-addressable blocks func BlockService(cfg *config.Config) func(lc fx.Lifecycle, bs blockstore.Blockstore, rem exchange.Interface) blockservice.BlockService { return func(lc fx.Lifecycle, bs blockstore.Blockstore, rem exchange.Interface) blockservice.BlockService { @@ -181,7 +184,6 @@ func Dag(bs blockservice.BlockService) format.DAGService { // Files loads persisted MFS root func Files(strategy string) func(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo, dag format.DAGService, bs blockstore.Blockstore, prov DHTProvider) (*mfs.Root, error) { return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo repo.Repo, dag format.DAGService, bs blockstore.Blockstore, prov DHTProvider) (*mfs.Root, error) { - dsk := datastore.NewKey("/local/filesroot") pf := func(ctx context.Context, c cid.Cid) error { rootDS := repo.Datastore() if err := rootDS.Sync(ctx, blockstore.BlockPrefix); err != nil { @@ -191,15 +193,15 @@ func Files(strategy string) func(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo return err } - if err := rootDS.Put(ctx, dsk, c.Bytes()); err != nil { + if err := rootDS.Put(ctx, FilesRootDatastoreKey, c.Bytes()); err != nil { return err } - return rootDS.Sync(ctx, dsk) + return rootDS.Sync(ctx, FilesRootDatastoreKey) } var nd *merkledag.ProtoNode ctx := helpers.LifecycleCtx(mctx, lc) - val, err := repo.Datastore().Get(ctx, dsk) + val, err := repo.Datastore().Get(ctx, FilesRootDatastoreKey) switch { case errors.Is(err, datastore.ErrNotFound): @@ -243,7 +245,8 @@ func Files(strategy string) func(mctx helpers.MetricsCtx, lc fx.Lifecycle, repo root, err := mfs.NewRoot(ctx, dag, nd, pf, prov) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to initialize MFS root from %s stored at %s: %w. "+ + "If corrupted, use 'ipfs files chroot' to reset (see --help)", nd.Cid(), FilesRootDatastoreKey, err) } lc.Append(fx.Hook{ diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index adc7f252c..dcdf9a27d 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -17,6 +17,7 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [Improved `ipfs dag stat` output](#improved-ipfs-dag-stat-output) - [Skip bad keys when listing](#skip_bad_keys_when_listing) - [Accelerated DHT Client and Provide Sweep now work together](#accelerated-dht-client-and-provide-sweep-now-work-together) + - [๐Ÿ”ง Recovery from corrupted MFS root](#-recovery-from-corrupted-mfs-root) - [๐Ÿ“ฆ๏ธ Dependency updates](#-dependency-updates) - [๐Ÿ“ Changelog](#-changelog) - [๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Contributors](#-contributors) @@ -96,6 +97,22 @@ Change the `ipfs key list` behavior to log an error and continue listing keys wh Previously, provide operations could start before the Accelerated DHT Client discovered enough peers, causing sweep mode to lose its efficiency benefits. Now, providing waits for the initial network crawl (about 10 minutes). Your content will be properly distributed across DHT regions after initial DHT map is created. Check `ipfs provide stat` to see when providing begins. +#### ๐Ÿ”ง Recovery from corrupted MFS root + +If your daemon fails to start because the MFS root is not a directory (due to misconfiguration, operational error, or disk corruption), you can now recover without deleting and recreating your repository in a new `IPFS_PATH`. + +The new `ipfs files chroot` command lets you reset the MFS (Mutable File System) root or restore it to a known valid CID: + +```console +# Reset MFS to an empty directory +$ ipfs files chroot --confirm + +# Or restore from a previously saved directory CID +$ ipfs files chroot --confirm QmYourBackupCID +``` + +See `ipfs files chroot --help` for details. + #### ๐Ÿ“ฆ๏ธ Dependency updates - update `go-libp2p` to [v0.46.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.46.0) diff --git a/test/cli/files_test.go b/test/cli/files_test.go index 4760c23aa..c2d226f90 100644 --- a/test/cli/files_test.go +++ b/test/cli/files_test.go @@ -353,3 +353,109 @@ func TestFilesNoFlushLimit(t *testing.T) { assert.Contains(t, res.Stderr.String(), "reached limit of 5 unflushed MFS operations") }) } + +func TestFilesChroot(t *testing.T) { + t.Parallel() + + // Known CIDs for testing + emptyDirCid := "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn" + + t.Run("requires --confirm flag", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + // Don't start daemon - chroot runs offline + + res := node.RunIPFS("files", "chroot") + require.NotNil(t, res.ExitErr) + assert.NotEqual(t, 0, res.ExitErr.ExitCode()) + assert.Contains(t, res.Stderr.String(), "pass --confirm to proceed") + }) + + t.Run("resets to empty directory", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + // Start daemon to create MFS state + node.StartDaemon() + node.IPFS("files", "mkdir", "/testdir") + node.StopDaemon() + + // Reset MFS to empty - should exit 0 + res := node.RunIPFS("files", "chroot", "--confirm") + assert.Nil(t, res.ExitErr, "expected exit code 0") + assert.Contains(t, res.Stdout.String(), emptyDirCid) + + // Verify daemon starts and MFS is empty + node.StartDaemon() + defer node.StopDaemon() + lsRes := node.IPFS("files", "ls", "/") + assert.Empty(t, lsRes.Stdout.Trimmed()) + }) + + t.Run("replaces with valid directory CID", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + // Start daemon to add content + node.StartDaemon() + node.IPFS("files", "mkdir", "/mydir") + // Create a temp file for content + tempFile := filepath.Join(node.Dir, "testfile.txt") + require.NoError(t, os.WriteFile(tempFile, []byte("hello"), 0644)) + node.IPFS("files", "write", "--create", "/mydir/file.txt", tempFile) + statRes := node.IPFS("files", "stat", "--hash", "/mydir") + dirCid := statRes.Stdout.Trimmed() + node.StopDaemon() + + // Reset to empty first + node.IPFS("files", "chroot", "--confirm") + + // Set root to the saved directory - should exit 0 + res := node.RunIPFS("files", "chroot", "--confirm", dirCid) + assert.Nil(t, res.ExitErr, "expected exit code 0") + assert.Contains(t, res.Stdout.String(), dirCid) + + // Verify content + node.StartDaemon() + defer node.StopDaemon() + readRes := node.IPFS("files", "read", "/file.txt") + assert.Equal(t, "hello", readRes.Stdout.Trimmed()) + }) + + t.Run("fails with non-existent CID", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + res := node.RunIPFS("files", "chroot", "--confirm", "bafybeibdxtd5thfoitjmnfhxhywokebwdmwnuqgkzjjdjhwjz7qh77777a") + require.NotNil(t, res.ExitErr) + assert.NotEqual(t, 0, res.ExitErr.ExitCode()) + assert.Contains(t, res.Stderr.String(), "does not exist locally") + }) + + t.Run("fails with file CID", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + // Add a file to get a file CID + node.StartDaemon() + fileCid := node.IPFSAddStr("hello world") + node.StopDaemon() + + // Try to set file as root - should fail with non-zero exit + res := node.RunIPFS("files", "chroot", "--confirm", fileCid) + require.NotNil(t, res.ExitErr) + assert.NotEqual(t, 0, res.ExitErr.ExitCode()) + assert.Contains(t, res.Stderr.String(), "must be a directory") + }) + + t.Run("fails while daemon is running", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + + res := node.RunIPFS("files", "chroot", "--confirm") + require.NotNil(t, res.ExitErr) + assert.NotEqual(t, 0, res.ExitErr.ExitCode()) + assert.Contains(t, res.Stderr.String(), "opening repo") + }) +} From 6a595c27a915efef36f5482b289037fe8cff1423 Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Tue, 13 Jan 2026 20:27:58 -0800 Subject: [PATCH 25/49] upgrade go-libp2p-pubsub to v0.15.0 (#11144) --- docs/changelogs/v0.40.md | 1 + docs/examples/kubo-as-a-library/go.mod | 22 ++++++------ docs/examples/kubo-as-a-library/go.sum | 44 +++++++++++------------ go.mod | 24 ++++++------- go.sum | 48 +++++++++++++------------- test/dependencies/go.mod | 20 +++++------ test/dependencies/go.sum | 44 +++++++++++------------ 7 files changed, 102 insertions(+), 101 deletions(-) diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index dcdf9a27d..ceee8cfe3 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -122,6 +122,7 @@ See `ipfs files chroot --help` for details. - update `p2p-forge` to [v0.7.0](https://github.com/ipshipyard/p2p-forge/releases/tag/v0.7.0) - update `go-ds-pebble` to [v0.5.8](https://github.com/ipfs/go-ds-pebble/releases/tag/v0.5.8) - updates `github.com/cockroachdb/pebble` to [v2.1.3](https://github.com/cockroachdb/pebble/releases/tag/v2.1.3) to enable Go 1.26 support +- update `go-libp2p-pubsub` to [v0.15.0](https://github.com/libp2p/go-libp2p-pubsub/releases/tag/v0.15.0) ### ๐Ÿ“ Changelog diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index f59d136d6..525f686f9 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -116,7 +116,7 @@ require ( github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect github.com/libp2p/go-libp2p-kad-dht v0.36.0 // indirect github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect - github.com/libp2p/go-libp2p-pubsub v0.14.2 // indirect + github.com/libp2p/go-libp2p-pubsub v0.15.0 // indirect github.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect github.com/libp2p/go-libp2p-record v0.3.1 // indirect github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect @@ -211,22 +211,22 @@ require ( go.uber.org/zap/exp v0.3.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/crypto v0.45.0 // indirect - golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect - golang.org/x/mod v0.30.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/crypto v0.47.0 // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect + golang.org/x/mod v0.32.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 // indirect + golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.39.0 // indirect + golang.org/x/tools v0.41.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.16.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/grpc v1.75.0 // indirect - google.golang.org/protobuf v1.36.10 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.4.1 // indirect ) diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 790250ba1..1c577758a 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -407,8 +407,8 @@ github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLw github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s= github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4= github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= -github.com/libp2p/go-libp2p-pubsub v0.14.2 h1:nT5lFHPQOFJcp9CW8hpKtvbpQNdl2udJuzLQWbgRum8= -github.com/libp2p/go-libp2p-pubsub v0.14.2/go.mod h1:MKPU5vMI8RRFyTP0HfdsF9cLmL1nHAeJm44AxJGJx44= +github.com/libp2p/go-libp2p-pubsub v0.15.0 h1:cG7Cng2BT82WttmPFMi50gDNV+58K626m/wR00vGL1o= +github.com/libp2p/go-libp2p-pubsub v0.15.0/go.mod h1:lr4oE8bFgQaifRcoc2uWhWWiK6tPdOEKpUuR408GFN4= github.com/libp2p/go-libp2p-pubsub-router v0.6.0 h1:D30iKdlqDt5ZmLEYhHELCMRj8b4sFAqrUcshIUvVP/s= github.com/libp2p/go-libp2p-pubsub-router v0.6.0/go.mod h1:FY/q0/RBTKsLA7l4vqC2cbRbOvyDotg8PJQ7j8FDudE= github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg= @@ -767,8 +767,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -777,8 +777,8 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -800,8 +800,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -834,8 +834,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -851,8 +851,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -897,10 +897,10 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo= -golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 h1:O1cMQHRfwNpDfDJerqRoE2oD+AFlyid87D40L/OkkJo= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -920,8 +920,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= @@ -959,8 +959,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1023,8 +1023,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/go.mod b/go.mod index cba15d0a5..bcb0ea219 100644 --- a/go.mod +++ b/go.mod @@ -54,7 +54,7 @@ require ( github.com/libp2p/go-libp2p-http v0.5.0 github.com/libp2p/go-libp2p-kad-dht v0.36.0 github.com/libp2p/go-libp2p-kbucket v0.8.0 - github.com/libp2p/go-libp2p-pubsub v0.14.2 + github.com/libp2p/go-libp2p-pubsub v0.15.0 github.com/libp2p/go-libp2p-pubsub-router v0.6.0 github.com/libp2p/go-libp2p-record v0.3.1 github.com/libp2p/go-libp2p-routing-helpers v0.7.5 @@ -87,12 +87,12 @@ require ( go.uber.org/dig v1.19.0 go.uber.org/fx v1.24.0 go.uber.org/zap v1.27.0 - golang.org/x/crypto v0.45.0 - golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 - golang.org/x/mod v0.30.0 - golang.org/x/sync v0.18.0 - golang.org/x/sys v0.38.0 - google.golang.org/protobuf v1.36.10 + golang.org/x/crypto v0.47.0 + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 + golang.org/x/mod v0.32.0 + golang.org/x/sync v0.19.0 + golang.org/x/sys v0.40.0 + google.golang.org/protobuf v1.36.11 ) require ( @@ -255,13 +255,13 @@ require ( go.uber.org/zap/exp v0.3.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/net v0.47.0 // indirect + golang.org/x/net v0.49.0 // indirect golang.org/x/oauth2 v0.33.0 // indirect - golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 // indirect + golang.org/x/term v0.39.0 // indirect + golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.39.0 // indirect + golang.org/x/tools v0.41.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.16.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect diff --git a/go.sum b/go.sum index b5833a54a..3fe11d36c 100644 --- a/go.sum +++ b/go.sum @@ -496,8 +496,8 @@ github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLw github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s= github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4= github.com/libp2p/go-libp2p-peerstore v0.1.4/go.mod h1:+4BDbDiiKf4PzpANZDAT+knVdLxvqh7hXOujessqdzs= -github.com/libp2p/go-libp2p-pubsub v0.14.2 h1:nT5lFHPQOFJcp9CW8hpKtvbpQNdl2udJuzLQWbgRum8= -github.com/libp2p/go-libp2p-pubsub v0.14.2/go.mod h1:MKPU5vMI8RRFyTP0HfdsF9cLmL1nHAeJm44AxJGJx44= +github.com/libp2p/go-libp2p-pubsub v0.15.0 h1:cG7Cng2BT82WttmPFMi50gDNV+58K626m/wR00vGL1o= +github.com/libp2p/go-libp2p-pubsub v0.15.0/go.mod h1:lr4oE8bFgQaifRcoc2uWhWWiK6tPdOEKpUuR408GFN4= github.com/libp2p/go-libp2p-pubsub-router v0.6.0 h1:D30iKdlqDt5ZmLEYhHELCMRj8b4sFAqrUcshIUvVP/s= github.com/libp2p/go-libp2p-pubsub-router v0.6.0/go.mod h1:FY/q0/RBTKsLA7l4vqC2cbRbOvyDotg8PJQ7j8FDudE= github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg= @@ -941,8 +941,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -953,8 +953,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -977,8 +977,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1027,8 +1027,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1052,8 +1052,8 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1119,10 +1119,10 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo= -golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 h1:O1cMQHRfwNpDfDJerqRoE2oD+AFlyid87D40L/OkkJo= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1131,8 +1131,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1144,8 +1144,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1200,8 +1200,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1295,8 +1295,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index 75bbdf72c..346686610 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -326,20 +326,20 @@ require ( go.uber.org/zap v1.27.0 // indirect go.uber.org/zap/exp v0.3.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect - golang.org/x/crypto v0.45.0 // indirect - golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 // indirect + golang.org/x/crypto v0.47.0 // indirect + golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect - golang.org/x/mod v0.30.0 // indirect - golang.org/x/net v0.47.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect - golang.org/x/term v0.37.0 // indirect - golang.org/x/text v0.31.0 // indirect + golang.org/x/mod v0.32.0 // indirect + golang.org/x/net v0.49.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/term v0.39.0 // indirect + golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.12.0 // indirect - golang.org/x/tools v0.39.0 // indirect + golang.org/x/tools v0.41.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect gonum.org/v1/gonum v0.16.0 // indirect - google.golang.org/protobuf v1.36.10 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index 78d6acaef..e848f4b37 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -823,10 +823,10 @@ golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98y golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39 h1:DHNhtq3sNNzrvduZZIiFyXWOL9IWaDPHqTnLJp+rCBY= -golang.org/x/exp v0.0.0-20251125195548-87e1e737ad39/go.mod h1:46edojNIoXTNOhySWIWdix628clX9ODXwPsQuG6hsK0= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU= +golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac h1:TSSpLIG4v+p0rPv1pNOQtl1I8knsO4S9trOxNMOLVP4= @@ -844,8 +844,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= -golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -865,8 +865,8 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -876,8 +876,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -903,10 +903,10 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54 h1:E2/AqCUMZGgd73TQkxUMcMla25GB9i/5HOdLr+uH7Vo= -golang.org/x/telemetry v0.0.0-20251111182119-bc8e575c7b54/go.mod h1:hKdjCMrbv9skySur+Nek8Hd0uJ0GuxJIoIX2payrIdQ= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 h1:O1cMQHRfwNpDfDJerqRoE2oD+AFlyid87D40L/OkkJo= +golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2/go.mod h1:b7fPSJ0pKZ3ccUh8gnTONJxhn3c/PS6tyzQvyqw4iA8= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -918,8 +918,8 @@ golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -932,8 +932,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -958,8 +958,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= -golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= @@ -972,8 +972,8 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhS golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From 4bdc9ad22011cab1c58332e69e9889affb95b7f2 Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Tue, 13 Jan 2026 20:44:28 -0800 Subject: [PATCH 26/49] ipfswatch: fix panic on broken link (#11145) Fix panic when broken link is created in watch directory. Closes #10017 --- cmd/ipfswatch/main.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/ipfswatch/main.go b/cmd/ipfswatch/main.go index 3ba5dd3d9..a25dcbcab 100644 --- a/cmd/ipfswatch/main.go +++ b/cmd/ipfswatch/main.go @@ -150,6 +150,7 @@ func run(ipfsPath, watchPath string) error { log.Printf("received event: %s", e) isDir, err := IsDirectory(e.Name) if err != nil { + log.Println(err) continue } switch e.Op { @@ -220,7 +221,7 @@ func addTree(w *fsnotify.Watcher, root string) error { return filepath.SkipDir case isDir: log.Println(path) - if err := w.Add(path); err != nil { + if err = w.Add(path); err != nil { return err } default: @@ -233,7 +234,10 @@ func addTree(w *fsnotify.Watcher, root string) error { func IsDirectory(path string) (bool, error) { fileInfo, err := os.Stat(path) - return fileInfo.IsDir(), err + if err != nil { + return false, err + } + return fileInfo.IsDir(), nil } func IsHidden(path string) bool { From 698354342edd396bebd99e067de732d14aca07fe Mon Sep 17 00:00:00 2001 From: Guillaume Michel Date: Thu, 15 Jan 2026 17:11:35 +0100 Subject: [PATCH 27/49] feat(provider): info log AcceleratedDHTClient crawl (#11143) * feat(provider): log fullrt crawl * fix(provider): improve AcceleratedDHTClient log messages use config option name instead of internal "fullrt" jargon, align terminology with daemon startup message, add time estimate --------- Co-authored-by: Marcin Rataj --- core/node/provider.go | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/core/node/provider.go b/core/node/provider.go index 57d70feb6..fba012422 100644 --- a/core/node/provider.go +++ b/core/node/provider.go @@ -16,6 +16,7 @@ import ( "github.com/ipfs/go-datastore" "github.com/ipfs/go-datastore/namespace" "github.com/ipfs/go-datastore/query" + log "github.com/ipfs/go-log/v2" "github.com/ipfs/kubo/config" "github.com/ipfs/kubo/repo" irouting "github.com/ipfs/kubo/routing" @@ -53,6 +54,8 @@ const ( keystoreDatastorePath = "keystore" ) +var errAcceleratedDHTNotReady = errors.New("AcceleratedDHTClient: routing table not ready") + // Interval between reprovide queue monitoring checks for slow reprovide alerts. // Used when Provide.DHT.SweepEnabled=true const reprovideAlertPollInterval = 15 * time.Minute @@ -325,13 +328,34 @@ type dhtImpl interface { type fullrtRouter struct { *fullrt.FullRT + ready bool + logger *log.ZapEventLogger +} + +func newFullRTRouter(fr *fullrt.FullRT, loggerName string) *fullrtRouter { + return &fullrtRouter{ + FullRT: fr, + ready: true, + logger: log.Logger(loggerName), + } } // GetClosestPeers overrides fullrt.FullRT's GetClosestPeers and returns an // error if the fullrt's initial network crawl isn't complete yet. func (fr *fullrtRouter) GetClosestPeers(ctx context.Context, key string) ([]peer.ID, error) { - if !fr.Ready() { - return nil, errors.New("fullrt: initial network crawl still running") + if fr.ready { + if !fr.Ready() { + fr.ready = false + fr.logger.Info("AcceleratedDHTClient: waiting for routing table initialization (5-10 min, depends on DHT size and network) to complete before providing") + return nil, errAcceleratedDHTNotReady + } + } else { + if fr.Ready() { + fr.ready = true + fr.logger.Info("AcceleratedDHTClient: routing table ready, providing can begin") + } else { + return nil, errAcceleratedDHTNotReady + } } return fr.FullRT.GetClosestPeers(ctx, key) } @@ -382,6 +406,9 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option { // provides happen as fast as possible via a dedicated worker that continuously // processes the queue regardless of this timing. bufferedIdleWriteTime = time.Minute + + // loggerName is the name of the go-log logger used by the provider. + loggerName = dhtprovider.DefaultLoggerName ) bufferedProviderOpts := []buffered.Option{ @@ -411,6 +438,8 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option { ddhtprovider.WithDedicatedPeriodicWorkers(int(cfg.Provide.DHT.DedicatedPeriodicWorkers.WithDefault(config.DefaultProvideDHTDedicatedPeriodicWorkers))), ddhtprovider.WithDedicatedBurstWorkers(int(cfg.Provide.DHT.DedicatedBurstWorkers.WithDefault(config.DefaultProvideDHTDedicatedBurstWorkers))), ddhtprovider.WithMaxProvideConnsPerWorker(int(cfg.Provide.DHT.MaxProvideConnsPerWorker.WithDefault(config.DefaultProvideDHTMaxProvideConnsPerWorker))), + + ddhtprovider.WithLoggerName(loggerName), ) if err != nil { return nil, nil, err @@ -419,7 +448,7 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option { } case *fullrt.FullRT: if inDht != nil { - impl = &fullrtRouter{inDht} + impl = newFullRTRouter(inDht, loggerName) } } if impl == nil { @@ -454,6 +483,8 @@ func SweepingProviderOpt(cfg *config.Config) fx.Option { dhtprovider.WithDedicatedPeriodicWorkers(int(cfg.Provide.DHT.DedicatedPeriodicWorkers.WithDefault(config.DefaultProvideDHTDedicatedPeriodicWorkers))), dhtprovider.WithDedicatedBurstWorkers(int(cfg.Provide.DHT.DedicatedBurstWorkers.WithDefault(config.DefaultProvideDHTDedicatedBurstWorkers))), dhtprovider.WithMaxProvideConnsPerWorker(int(cfg.Provide.DHT.MaxProvideConnsPerWorker.WithDefault(config.DefaultProvideDHTMaxProvideConnsPerWorker))), + + dhtprovider.WithLoggerName(loggerName), } prov, err := dhtprovider.New(opts...) From edb70567477435c20d3c4959db64abeebf991e92 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Thu, 15 Jan 2026 19:31:16 +0100 Subject: [PATCH 28/49] feat(config): add Gateway.MaxRequestDuration option (#11138) * feat(config): add Gateway.MaxRequestDuration option exposes the previously hardcoded 1 hour gateway request deadline as a configurable option, allowing operators to adjust it to fit deployment needs. protects gateway from edge cases and slow client attacks. boxo: https://github.com/ipfs/boxo/pull/1079 * test(gateway): add MaxRequestDuration integration test verifies config is wired correctly and 504 is returned when exceeded * docs: add MaxRequestDuration to gateway production guide --------- Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com> --- config/gateway.go | 9 ++++++ core/corehttp/gateway.go | 2 ++ docs/changelogs/v0.40.md | 7 +++++ docs/config.md | 11 +++++++ docs/examples/kubo-as-a-library/go.mod | 2 +- docs/examples/kubo-as-a-library/go.sum | 4 +-- docs/gateway.md | 4 ++- go.mod | 2 +- go.sum | 4 +-- test/cli/gateway_limits_test.go | 41 ++++++++++++++++++++++++++ test/dependencies/go.mod | 2 +- test/dependencies/go.sum | 4 +-- 12 files changed, 82 insertions(+), 10 deletions(-) diff --git a/config/gateway.go b/config/gateway.go index 3495caede..0ce7638f4 100644 --- a/config/gateway.go +++ b/config/gateway.go @@ -13,6 +13,7 @@ const ( // Gateway limit defaults from boxo DefaultRetrievalTimeout = gateway.DefaultRetrievalTimeout + DefaultMaxRequestDuration = gateway.DefaultMaxRequestDuration DefaultMaxConcurrentRequests = gateway.DefaultMaxConcurrentRequests DefaultMaxRangeRequestFileSize = 0 // 0 means no limit ) @@ -96,6 +97,14 @@ type Gateway struct { // A value of 0 disables this timeout. RetrievalTimeout *OptionalDuration `json:",omitempty"` + // MaxRequestDuration is an absolute deadline for the entire request. + // Unlike RetrievalTimeout (which resets on each data write and catches + // stalled transfers), this is a hard limit on the total time a request + // can take. Returns 504 Gateway Timeout when exceeded. + // This protects the gateway from edge cases and slow client attacks. + // A value of 0 uses the default (1 hour). + MaxRequestDuration *OptionalDuration `json:",omitempty"` + // MaxConcurrentRequests limits concurrent HTTP requests handled by the gateway. // Requests beyond this limit receive 429 Too Many Requests with Retry-After header. // A value of 0 disables the limit. diff --git a/core/corehttp/gateway.go b/core/corehttp/gateway.go index 393a668bf..6bac662ae 100644 --- a/core/corehttp/gateway.go +++ b/core/corehttp/gateway.go @@ -112,6 +112,7 @@ func Libp2pGatewayOption() ServeOption { Menu: nil, // Apply timeout and concurrency limits from user config RetrievalTimeout: cfg.Gateway.RetrievalTimeout.WithDefault(config.DefaultRetrievalTimeout), + MaxRequestDuration: cfg.Gateway.MaxRequestDuration.WithDefault(config.DefaultMaxRequestDuration), MaxConcurrentRequests: int(cfg.Gateway.MaxConcurrentRequests.WithDefault(int64(config.DefaultMaxConcurrentRequests))), MaxRangeRequestFileSize: int64(cfg.Gateway.MaxRangeRequestFileSize.WithDefault(uint64(config.DefaultMaxRangeRequestFileSize))), DiagnosticServiceURL: "", // Not used since DisableHTMLErrors=true @@ -272,6 +273,7 @@ func getGatewayConfig(n *core.IpfsNode) (gateway.Config, map[string][]string, er NoDNSLink: cfg.Gateway.NoDNSLink, PublicGateways: map[string]*gateway.PublicGateway{}, RetrievalTimeout: cfg.Gateway.RetrievalTimeout.WithDefault(config.DefaultRetrievalTimeout), + MaxRequestDuration: cfg.Gateway.MaxRequestDuration.WithDefault(config.DefaultMaxRequestDuration), MaxConcurrentRequests: int(cfg.Gateway.MaxConcurrentRequests.WithDefault(int64(config.DefaultMaxConcurrentRequests))), MaxRangeRequestFileSize: int64(cfg.Gateway.MaxRangeRequestFileSize.WithDefault(uint64(config.DefaultMaxRangeRequestFileSize))), DiagnosticServiceURL: cfg.Gateway.DiagnosticServiceURL.WithDefault(config.DefaultDiagnosticServiceURL), diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index ceee8cfe3..ab5e5762b 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -17,6 +17,7 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [Improved `ipfs dag stat` output](#improved-ipfs-dag-stat-output) - [Skip bad keys when listing](#skip_bad_keys_when_listing) - [Accelerated DHT Client and Provide Sweep now work together](#accelerated-dht-client-and-provide-sweep-now-work-together) + - [โฑ๏ธ Configurable gateway request duration limit](#๏ธ-configurable-gateway-request-duration-limit) - [๐Ÿ”ง Recovery from corrupted MFS root](#-recovery-from-corrupted-mfs-root) - [๐Ÿ“ฆ๏ธ Dependency updates](#-dependency-updates) - [๐Ÿ“ Changelog](#-changelog) @@ -97,6 +98,12 @@ Change the `ipfs key list` behavior to log an error and continue listing keys wh Previously, provide operations could start before the Accelerated DHT Client discovered enough peers, causing sweep mode to lose its efficiency benefits. Now, providing waits for the initial network crawl (about 10 minutes). Your content will be properly distributed across DHT regions after initial DHT map is created. Check `ipfs provide stat` to see when providing begins. +#### โฑ๏ธ Configurable gateway request duration limit + +[`Gateway.MaxRequestDuration`](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewaymaxrequestduration) sets an absolute deadline for gateway requests. Unlike `RetrievalTimeout` (which resets on each data write and catches stalled transfers), this is a hard limit on the total time a request can take. + +The default 1 hour limit (previously hardcoded) can now be adjusted to fit your deployment needs. This is a fallback that prevents requests from hanging indefinitely when subsystem timeouts are misconfigured or fail to trigger. Returns 504 Gateway Timeout when exceeded. + #### ๐Ÿ”ง Recovery from corrupted MFS root If your daemon fails to start because the MFS root is not a directory (due to misconfiguration, operational error, or disk corruption), you can now recover without deleting and recreating your repository in a new `IPFS_PATH`. diff --git a/docs/config.md b/docs/config.md index 986798296..99b92838c 100644 --- a/docs/config.md +++ b/docs/config.md @@ -67,6 +67,7 @@ config file at runtime. - [`Gateway.DisableHTMLErrors`](#gatewaydisablehtmlerrors) - [`Gateway.ExposeRoutingAPI`](#gatewayexposeroutingapi) - [`Gateway.RetrievalTimeout`](#gatewayretrievaltimeout) + - [`Gateway.MaxRequestDuration`](#gatewaymaxrequestduration) - [`Gateway.MaxRangeRequestFileSize`](#gatewaymaxrangerequestfilesize) - [`Gateway.MaxConcurrentRequests`](#gatewaymaxconcurrentrequests) - [`Gateway.HTTPHeaders`](#gatewayhttpheaders) @@ -1178,6 +1179,16 @@ Default: `30s` Type: `optionalDuration` +### `Gateway.MaxRequestDuration` + +An absolute deadline for the entire gateway request. Unlike [`RetrievalTimeout`](#gatewayretrievaltimeout) (which resets on each data write and catches stalled transfers), this is a hard limit on the total time a request can take. + +Returns 504 Gateway Timeout when exceeded. This protects the gateway from edge cases and slow client attacks. + +Default: `1h` + +Type: `optionalDuration` + ### `Gateway.MaxRangeRequestFileSize` Maximum file size for HTTP range requests on deserialized responses. Range requests for files larger than this limit return 501 Not Implemented. diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 525f686f9..698eccfa3 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -7,7 +7,7 @@ go 1.25 replace github.com/ipfs/kubo => ./../../.. require ( - github.com/ipfs/boxo v0.35.3-0.20251202220026-0842ad274a0c + github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 github.com/ipfs/kubo v0.0.0-00010101000000-000000000000 github.com/libp2p/go-libp2p v0.46.0 github.com/multiformats/go-multiaddr v0.16.1 diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 1c577758a..dba9d67aa 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -265,8 +265,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 h1:OqNqsGZPX8zh3eFMO8Lf8EHRRnSGBMqcd github.com/ipfs-shipyard/nopfs/ipfs v0.25.0/go.mod h1:BxhUdtBgOXg1B+gAPEplkg/GpyTZY+kCMSfsJvvydqU= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/boxo v0.35.3-0.20251202220026-0842ad274a0c h1:mczpALnNzNhmggehO5Ehr9+Q8+NiJyKJfT4EPwi01d0= -github.com/ipfs/boxo v0.35.3-0.20251202220026-0842ad274a0c/go.mod h1:Abmp1if6bMQG87/0SQPIB9fkxJnZMLCt2nQw3yUZHH0= +github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 h1:pRQYSSGnGQa921d8v0uhXg2BGzoSf9ndTWTlR7ImVoo= +github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2/go.mod h1:Abmp1if6bMQG87/0SQPIB9fkxJnZMLCt2nQw3yUZHH0= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= diff --git a/docs/gateway.md b/docs/gateway.md index d51eab4cc..31b753e51 100644 --- a/docs/gateway.md +++ b/docs/gateway.md @@ -109,7 +109,9 @@ When deploying Kubo's gateway in production, be aware of these important conside > [!IMPORTANT] > **Timeouts:** Configure [`Gateway.RetrievalTimeout`](config.md#gatewayretrievaltimeout) -> based on your expected content retrieval times. +> to terminate stalled transfers (resets on each data write, catches unresponsive operations), +> and [`Gateway.MaxRequestDuration`](config.md#gatewaymaxrequestduration) as a fallback +> deadline (default: 1 hour, catches cases when other timeouts are misconfigured or fail to fire). > [!IMPORTANT] > **Rate Limiting:** Use [`Gateway.MaxConcurrentRequests`](config.md#gatewaymaxconcurrentrequests) diff --git a/go.mod b/go.mod index bcb0ea219..109a2ca99 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/hashicorp/go-version v1.7.0 github.com/ipfs-shipyard/nopfs v0.0.14 github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 - github.com/ipfs/boxo v0.35.3-0.20251202220026-0842ad274a0c + github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 github.com/ipfs/go-block-format v0.2.3 github.com/ipfs/go-cid v0.6.0 github.com/ipfs/go-cidutil v0.1.0 diff --git a/go.sum b/go.sum index 3fe11d36c..afed01f14 100644 --- a/go.sum +++ b/go.sum @@ -336,8 +336,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 h1:OqNqsGZPX8zh3eFMO8Lf8EHRRnSGBMqcd github.com/ipfs-shipyard/nopfs/ipfs v0.25.0/go.mod h1:BxhUdtBgOXg1B+gAPEplkg/GpyTZY+kCMSfsJvvydqU= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/boxo v0.35.3-0.20251202220026-0842ad274a0c h1:mczpALnNzNhmggehO5Ehr9+Q8+NiJyKJfT4EPwi01d0= -github.com/ipfs/boxo v0.35.3-0.20251202220026-0842ad274a0c/go.mod h1:Abmp1if6bMQG87/0SQPIB9fkxJnZMLCt2nQw3yUZHH0= +github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 h1:pRQYSSGnGQa921d8v0uhXg2BGzoSf9ndTWTlR7ImVoo= +github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2/go.mod h1:Abmp1if6bMQG87/0SQPIB9fkxJnZMLCt2nQw3yUZHH0= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= diff --git a/test/cli/gateway_limits_test.go b/test/cli/gateway_limits_test.go index 990eabb1a..3e3b8540a 100644 --- a/test/cli/gateway_limits_test.go +++ b/test/cli/gateway_limits_test.go @@ -58,6 +58,47 @@ func TestGatewayLimits(t *testing.T) { assert.Contains(t, resp.Body, "Unable to retrieve content within timeout period") }) + t.Run("MaxRequestDuration", func(t *testing.T) { + t.Parallel() + + // Create a node with a short max request duration + node := harness.NewT(t).NewNode().Init() + node.UpdateConfig(func(cfg *config.Config) { + // Set a short absolute deadline (500ms) for the entire request + cfg.Gateway.MaxRequestDuration = config.NewOptionalDuration(500 * time.Millisecond) + // Set retrieval timeout much longer so MaxRequestDuration fires first + cfg.Gateway.RetrievalTimeout = config.NewOptionalDuration(30 * time.Second) + }) + node.StartDaemon() + defer node.StopDaemon() + + // Add content that can be retrieved quickly + cid := node.IPFSAddStr("test content for max request duration") + + client := node.GatewayClient() + + // Fast request for local content should succeed (well within 500ms) + resp := client.Get("/ipfs/" + cid) + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, "test content for max request duration", resp.Body) + + // Request for non-existent content should timeout due to MaxRequestDuration + // This CID has no providers and will block during content routing + nonExistentCID := "bafkreif6lrhgz3fpiwypdk65qrqiey7svgpggruhbylrgv32l3izkqpsc4" + + // Create a client with a longer timeout than MaxRequestDuration + // to ensure we receive the gateway's 504 response + clientWithTimeout := &harness.HTTPClient{ + Client: &http.Client{ + Timeout: 5 * time.Second, + }, + BaseURL: client.BaseURL, + } + + resp = clientWithTimeout.Get("/ipfs/" + nonExistentCID) + assert.Equal(t, http.StatusGatewayTimeout, resp.StatusCode, "Expected 504 when request exceeds MaxRequestDuration") + }) + t.Run("MaxConcurrentRequests", func(t *testing.T) { t.Parallel() diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index 346686610..98bf7ebf1 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -135,7 +135,7 @@ require ( github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect - github.com/ipfs/boxo v0.35.3-0.20251202220026-0842ad274a0c // indirect + github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 // indirect github.com/ipfs/go-bitfield v1.1.0 // indirect github.com/ipfs/go-block-format v0.2.3 // indirect github.com/ipfs/go-cid v0.6.0 // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index e848f4b37..32d404247 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -294,8 +294,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/boxo v0.35.3-0.20251202220026-0842ad274a0c h1:mczpALnNzNhmggehO5Ehr9+Q8+NiJyKJfT4EPwi01d0= -github.com/ipfs/boxo v0.35.3-0.20251202220026-0842ad274a0c/go.mod h1:Abmp1if6bMQG87/0SQPIB9fkxJnZMLCt2nQw3yUZHH0= +github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 h1:pRQYSSGnGQa921d8v0uhXg2BGzoSf9ndTWTlR7ImVoo= +github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2/go.mod h1:Abmp1if6bMQG87/0SQPIB9fkxJnZMLCt2nQw3yUZHH0= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-block-format v0.2.3 h1:mpCuDaNXJ4wrBJLrtEaGFGXkferrw5eqVvzaHhtFKQk= From 824a47ae11c827c1bd38a8932c36940cadec5e0f Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 16 Jan 2026 00:27:09 +0100 Subject: [PATCH 29/49] feat(pubsub): persistent validation and diagnostic commands (#11110) * feat(pubsub): persistent seqno validation and diagnostic commands - upgrade go-libp2p-pubsub to v0.15.0 - add persistent seqno validator using BasicSeqnoValidator stores max seen seqno per peer at /pubsub/seqno/ survives daemon restarts, addresses message cycling in large networks (#9665) - add `ipfs pubsub reset` command to clear validator state - add `ipfs diag datastore get/count` commands for datastore inspection requires daemon to be stopped, useful for debugging - change pubsub status from Deprecated to Experimental - add CLI tests for pubsub and diag datastore commands - remove flaky pubsub_msg_seen_cache_test.go (replaced by CLI tests) * fix(pubsub): improve reset command and add deprecation warnings - use batched delete for efficient bulk reset - check key existence before reporting deleted count - sync datastore after deletions to ensure persistence - show "no validator state found" when resetting non-existent peer - log deprecation warnings when using --enable-pubsub-experiment or --enable-namesys-pubsub CLI flags * refactor(test): add datastore helpers to test harness --------- Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com> --- cmd/ipfs/kubo/daemon.go | 12 +- core/commands/commands_test.go | 4 + core/commands/diag.go | 189 +++++++- core/commands/pubsub.go | 189 ++++++-- core/node/libp2p/pubsub.go | 73 +++- core/node/libp2p/pubsub_test.go | 130 ++++++ docs/changelogs/v0.40.md | 19 + docs/config.md | 103 +++-- docs/experimental-features.md | 15 +- test/cli/diag_datastore_test.go | 147 +++++++ test/cli/harness/node.go | 25 ++ test/cli/pubsub_test.go | 403 ++++++++++++++++++ .../integration/pubsub_msg_seen_cache_test.go | 285 ------------- 13 files changed, 1219 insertions(+), 375 deletions(-) create mode 100644 core/node/libp2p/pubsub_test.go create mode 100644 test/cli/diag_datastore_test.go create mode 100644 test/cli/pubsub_test.go delete mode 100644 test/integration/pubsub_msg_seen_cache_test.go diff --git a/cmd/ipfs/kubo/daemon.go b/cmd/ipfs/kubo/daemon.go index 49aa9c19b..6d5a209cb 100644 --- a/cmd/ipfs/kubo/daemon.go +++ b/cmd/ipfs/kubo/daemon.go @@ -181,8 +181,8 @@ Headers. cmds.BoolOption(enableGCKwd, "Enable automatic periodic repo garbage collection"), cmds.BoolOption(adjustFDLimitKwd, "Check and raise file descriptor limits if needed").WithDefault(true), cmds.BoolOption(migrateKwd, "If true, assume yes at the migrate prompt. If false, assume no."), - cmds.BoolOption(enablePubSubKwd, "DEPRECATED"), - cmds.BoolOption(enableIPNSPubSubKwd, "Enable IPNS over pubsub. Implicitly enables pubsub, overrides Ipns.UsePubsub config."), + cmds.BoolOption(enablePubSubKwd, "DEPRECATED CLI flag. Use Pubsub.Enabled config instead."), + cmds.BoolOption(enableIPNSPubSubKwd, "DEPRECATED CLI flag. Use Ipns.UsePubsub config instead."), cmds.BoolOption(enableMultiplexKwd, "DEPRECATED"), cmds.StringOption(agentVersionSuffix, "Optional suffix to the AgentVersion presented by `ipfs id` and exposed via libp2p identify protocol."), @@ -397,10 +397,14 @@ func daemonFunc(req *cmds.Request, re cmds.ResponseEmitter, env cmds.Environment fmt.Printf("PeerID: %s\n", cfg.Identity.PeerID) - if !psSet { + if psSet { + log.Error("The --enable-pubsub-experiment flag is deprecated. Use Pubsub.Enabled config option instead.") + } else { pubsub = cfg.Pubsub.Enabled.WithDefault(false) } - if !ipnsPsSet { + if ipnsPsSet { + log.Error("The --enable-namesys-pubsub flag is deprecated. Use Ipns.UsePubsub config option instead.") + } else { ipnsps = cfg.Ipns.UsePubsub.WithDefault(false) } diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index 893c7352c..49e359e24 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -76,6 +76,9 @@ func TestCommands(t *testing.T) { "/diag/cmds", "/diag/cmds/clear", "/diag/cmds/set-time", + "/diag/datastore", + "/diag/datastore/count", + "/diag/datastore/get", "/diag/profile", "/diag/sys", "/files", @@ -170,6 +173,7 @@ func TestCommands(t *testing.T) { "/pubsub/ls", "/pubsub/peers", "/pubsub/pub", + "/pubsub/reset", "/pubsub/sub", "/refs", "/refs/local", diff --git a/core/commands/diag.go b/core/commands/diag.go index 89b46381f..777e9445f 100644 --- a/core/commands/diag.go +++ b/core/commands/diag.go @@ -1,7 +1,16 @@ package commands import ( + "encoding/hex" + "errors" + "fmt" + "io" + + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/query" cmds "github.com/ipfs/go-ipfs-cmds" + oldcmds "github.com/ipfs/kubo/commands" + fsrepo "github.com/ipfs/kubo/repo/fsrepo" ) var DiagCmd = &cmds.Command{ @@ -10,8 +19,182 @@ var DiagCmd = &cmds.Command{ }, Subcommands: map[string]*cmds.Command{ - "sys": sysDiagCmd, - "cmds": ActiveReqsCmd, - "profile": sysProfileCmd, + "sys": sysDiagCmd, + "cmds": ActiveReqsCmd, + "profile": sysProfileCmd, + "datastore": diagDatastoreCmd, + }, +} + +var diagDatastoreCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Low-level datastore inspection for debugging and testing.", + ShortDescription: ` +'ipfs diag datastore' provides low-level access to the datastore for debugging +and testing purposes. + +WARNING: FOR DEBUGGING/TESTING ONLY + +These commands expose internal datastore details and should not be used +in production workflows. The datastore format may change between versions. + +The daemon must not be running when calling these commands. + +EXAMPLE + +Inspecting pubsub seqno validator state: + + $ ipfs diag datastore count /pubsub/seqno/ + 2 + $ ipfs diag datastore get --hex /pubsub/seqno/12D3KooW... + Key: /pubsub/seqno/12D3KooW... + Hex Dump: + 00000000 18 81 81 c8 91 c0 ea f6 |........| +`, + }, + Subcommands: map[string]*cmds.Command{ + "get": diagDatastoreGetCmd, + "count": diagDatastoreCountCmd, + }, +} + +const diagDatastoreHexOptionName = "hex" + +type diagDatastoreGetResult struct { + Key string `json:"key"` + Value []byte `json:"value"` + HexDump string `json:"hex_dump,omitempty"` +} + +var diagDatastoreGetCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Read a raw key from the datastore.", + ShortDescription: ` +Returns the value stored at the given datastore key. +Default output is raw bytes. Use --hex for human-readable hex dump. + +The daemon must not be running when using this command. + +WARNING: FOR DEBUGGING/TESTING ONLY +`, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("key", true, false, "Datastore key to read (e.g., /pubsub/seqno/)"), + }, + Options: []cmds.Option{ + cmds.BoolOption(diagDatastoreHexOptionName, "Output hex dump instead of raw bytes"), + }, + NoRemote: true, + PreRun: DaemonNotRunning, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + cctx := env.(*oldcmds.Context) + repo, err := fsrepo.Open(cctx.ConfigRoot) + if err != nil { + return fmt.Errorf("failed to open repo: %w", err) + } + defer repo.Close() + + keyStr := req.Arguments[0] + key := datastore.NewKey(keyStr) + ds := repo.Datastore() + + val, err := ds.Get(req.Context, key) + if err != nil { + if errors.Is(err, datastore.ErrNotFound) { + return fmt.Errorf("key not found: %s", keyStr) + } + return fmt.Errorf("failed to read key: %w", err) + } + + result := &diagDatastoreGetResult{ + Key: keyStr, + Value: val, + } + + if hexDump, _ := req.Options[diagDatastoreHexOptionName].(bool); hexDump { + result.HexDump = hex.Dump(val) + } + + return cmds.EmitOnce(res, result) + }, + Type: diagDatastoreGetResult{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, result *diagDatastoreGetResult) error { + if result.HexDump != "" { + fmt.Fprintf(w, "Key: %s\nHex Dump:\n%s", result.Key, result.HexDump) + return nil + } + // Raw bytes output + _, err := w.Write(result.Value) + return err + }), + }, +} + +type diagDatastoreCountResult struct { + Prefix string `json:"prefix"` + Count int64 `json:"count"` +} + +var diagDatastoreCountCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Count entries matching a datastore prefix.", + ShortDescription: ` +Counts the number of datastore entries whose keys start with the given prefix. + +The daemon must not be running when using this command. + +WARNING: FOR DEBUGGING/TESTING ONLY +`, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("prefix", true, false, "Datastore key prefix (e.g., /pubsub/seqno/)"), + }, + NoRemote: true, + PreRun: DaemonNotRunning, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + cctx := env.(*oldcmds.Context) + repo, err := fsrepo.Open(cctx.ConfigRoot) + if err != nil { + return fmt.Errorf("failed to open repo: %w", err) + } + defer repo.Close() + + prefix := req.Arguments[0] + ds := repo.Datastore() + + q := query.Query{ + Prefix: prefix, + KeysOnly: true, + } + + results, err := ds.Query(req.Context, q) + if err != nil { + return fmt.Errorf("failed to query datastore: %w", err) + } + defer results.Close() + + var count int64 + for result := range results.Next() { + if result.Error != nil { + return fmt.Errorf("query error: %w", result.Error) + } + count++ + } + + return cmds.EmitOnce(res, &diagDatastoreCountResult{ + Prefix: prefix, + Count: count, + }) + }, + Type: diagDatastoreCountResult{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, result *diagDatastoreCountResult) error { + _, err := fmt.Fprintf(w, "%d\n", result.Count) + return err + }), }, } diff --git a/core/commands/pubsub.go b/core/commands/pubsub.go index 9e81ef281..e2efd35e6 100644 --- a/core/commands/pubsub.go +++ b/core/commands/pubsub.go @@ -8,26 +8,35 @@ import ( "net/http" "slices" - cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" - mbase "github.com/multiformats/go-multibase" - + "github.com/ipfs/go-datastore" + "github.com/ipfs/go-datastore/query" cmds "github.com/ipfs/go-ipfs-cmds" + cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" options "github.com/ipfs/kubo/core/coreiface/options" + "github.com/ipfs/kubo/core/node/libp2p" + "github.com/libp2p/go-libp2p/core/peer" + mbase "github.com/multiformats/go-multibase" ) var PubsubCmd = &cmds.Command{ - Status: cmds.Deprecated, + Status: cmds.Experimental, Helptext: cmds.HelpText{ Tagline: "An experimental publish-subscribe system on ipfs.", ShortDescription: ` ipfs pubsub allows you to publish messages to a given topic, and also to subscribe to new messages on a given topic. -DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) +EXPERIMENTAL FEATURE - It is not intended in its current state to be used in a production - environment. To use, the daemon must be run with - '--enable-pubsub-experiment'. + This is an opt-in feature optimized for IPNS over PubSub + (https://specs.ipfs.tech/ipns/ipns-pubsub-router/). + + The default message validator is designed for IPNS record protocol. + For custom pubsub applications requiring different validation logic, + use go-libp2p-pubsub (https://github.com/libp2p/go-libp2p-pubsub) + directly in a dedicated binary. + + To enable, set 'Pubsub.Enabled' config to true. `, }, Subcommands: map[string]*cmds.Command{ @@ -35,6 +44,7 @@ DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) "sub": PubsubSubCmd, "ls": PubsubLsCmd, "peers": PubsubPeersCmd, + "reset": PubsubResetCmd, }, } @@ -46,17 +56,18 @@ type pubsubMessage struct { } var PubsubSubCmd = &cmds.Command{ - Status: cmds.Deprecated, + Status: cmds.Experimental, Helptext: cmds.HelpText{ Tagline: "Subscribe to messages on a given topic.", ShortDescription: ` ipfs pubsub sub subscribes to messages on a given topic. -DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) +EXPERIMENTAL FEATURE - It is not intended in its current state to be used in a production - environment. To use, the daemon must be run with - '--enable-pubsub-experiment'. + This is an opt-in feature optimized for IPNS over PubSub + (https://specs.ipfs.tech/ipns/ipns-pubsub-router/). + + To enable, set 'Pubsub.Enabled' config to true. PEER ENCODING @@ -145,18 +156,19 @@ TOPIC AND DATA ENCODING } var PubsubPubCmd = &cmds.Command{ - Status: cmds.Deprecated, + Status: cmds.Experimental, Helptext: cmds.HelpText{ Tagline: "Publish data to a given pubsub topic.", ShortDescription: ` ipfs pubsub pub publishes a message to a specified topic. It reads binary data from stdin or a file. -DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) +EXPERIMENTAL FEATURE - It is not intended in its current state to be used in a production - environment. To use, the daemon must be run with - '--enable-pubsub-experiment'. + This is an opt-in feature optimized for IPNS over PubSub + (https://specs.ipfs.tech/ipns/ipns-pubsub-router/). + + To enable, set 'Pubsub.Enabled' config to true. HTTP RPC ENCODING @@ -201,17 +213,18 @@ HTTP RPC ENCODING } var PubsubLsCmd = &cmds.Command{ - Status: cmds.Deprecated, + Status: cmds.Experimental, Helptext: cmds.HelpText{ Tagline: "List subscribed topics by name.", ShortDescription: ` ipfs pubsub ls lists out the names of topics you are currently subscribed to. -DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) +EXPERIMENTAL FEATURE - It is not intended in its current state to be used in a production - environment. To use, the daemon must be run with - '--enable-pubsub-experiment'. + This is an opt-in feature optimized for IPNS over PubSub + (https://specs.ipfs.tech/ipns/ipns-pubsub-router/). + + To enable, set 'Pubsub.Enabled' config to true. TOPIC ENCODING @@ -273,7 +286,7 @@ func safeTextListEncoder(req *cmds.Request, w io.Writer, list *stringList) error } var PubsubPeersCmd = &cmds.Command{ - Status: cmds.Deprecated, + Status: cmds.Experimental, Helptext: cmds.HelpText{ Tagline: "List peers we are currently pubsubbing with.", ShortDescription: ` @@ -281,11 +294,12 @@ ipfs pubsub peers with no arguments lists out the pubsub peers you are currently connected to. If given a topic, it will list connected peers who are subscribed to the named topic. -DEPRECATED FEATURE (see https://github.com/ipfs/kubo/issues/9717) +EXPERIMENTAL FEATURE - It is not intended in its current state to be used in a production - environment. To use, the daemon must be run with - '--enable-pubsub-experiment'. + This is an opt-in feature optimized for IPNS over PubSub + (https://specs.ipfs.tech/ipns/ipns-pubsub-router/). + + To enable, set 'Pubsub.Enabled' config to true. TOPIC AND DATA ENCODING @@ -367,3 +381,122 @@ func urlArgsDecoder(req *cmds.Request, env cmds.Environment) error { } return nil } + +type pubsubResetResult struct { + Deleted int64 `json:"deleted"` +} + +var PubsubResetCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Reset pubsub validator state.", + ShortDescription: ` +Clears persistent sequence number state used by the pubsub validator. + +WARNING: FOR TESTING ONLY - DO NOT USE IN PRODUCTION + +Resets validator state that protects against replay attacks. After reset, +previously seen messages may be accepted again until their sequence numbers +are re-learned. + +Use cases: +- Testing pubsub functionality +- Recovery from a peer sending artificially high sequence numbers + (which would cause subsequent messages from that peer to be rejected) + +The --peer flag limits the reset to a specific peer's state. +Without --peer, all validator state is cleared. + +NOTE: This only resets the persistent seqno validator state. The in-memory +seen messages cache (Pubsub.SeenMessagesTTL) auto-expires and can only be +fully cleared by restarting the daemon. +`, + }, + Options: []cmds.Option{ + cmds.StringOption(peerOptionName, "p", "Only reset state for this peer ID"), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + n, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + ds := n.Repo.Datastore() + ctx := req.Context + + peerOpt, _ := req.Options[peerOptionName].(string) + + var deleted int64 + if peerOpt != "" { + // Reset specific peer + pid, err := peer.Decode(peerOpt) + if err != nil { + return fmt.Errorf("invalid peer ID: %w", err) + } + key := datastore.NewKey(libp2p.SeqnoStorePrefix + pid.String()) + exists, err := ds.Has(ctx, key) + if err != nil { + return fmt.Errorf("failed to check seqno state: %w", err) + } + if exists { + if err := ds.Delete(ctx, key); err != nil { + return fmt.Errorf("failed to delete seqno state: %w", err) + } + deleted = 1 + } + } else { + // Reset all peers using batched delete for efficiency + q := query.Query{ + Prefix: libp2p.SeqnoStorePrefix, + KeysOnly: true, + } + results, err := ds.Query(ctx, q) + if err != nil { + return fmt.Errorf("failed to query seqno state: %w", err) + } + defer results.Close() + + batch, err := ds.Batch(ctx) + if err != nil { + return fmt.Errorf("failed to create batch: %w", err) + } + + for result := range results.Next() { + if result.Error != nil { + return fmt.Errorf("query error: %w", result.Error) + } + if err := batch.Delete(ctx, datastore.NewKey(result.Key)); err != nil { + return fmt.Errorf("failed to batch delete key %s: %w", result.Key, err) + } + deleted++ + } + + if err := batch.Commit(ctx); err != nil { + return fmt.Errorf("failed to commit batch delete: %w", err) + } + } + + // Sync to ensure deletions are persisted + if err := ds.Sync(ctx, datastore.NewKey(libp2p.SeqnoStorePrefix)); err != nil { + return fmt.Errorf("failed to sync datastore: %w", err) + } + + return cmds.EmitOnce(res, &pubsubResetResult{Deleted: deleted}) + }, + Type: pubsubResetResult{}, + Encoders: cmds.EncoderMap{ + cmds.Text: cmds.MakeTypedEncoder(func(req *cmds.Request, w io.Writer, result *pubsubResetResult) error { + peerOpt, _ := req.Options[peerOptionName].(string) + if peerOpt != "" { + if result.Deleted == 0 { + _, err := fmt.Fprintf(w, "No validator state found for peer %s\n", peerOpt) + return err + } + _, err := fmt.Fprintf(w, "Reset validator state for peer %s\n", peerOpt) + return err + } + _, err := fmt.Fprintf(w, "Reset validator state for %d peer(s)\n", result.Deleted) + return err + }), + }, +} diff --git a/core/node/libp2p/pubsub.go b/core/node/libp2p/pubsub.go index 072d74ee1..f9d758786 100644 --- a/core/node/libp2p/pubsub.go +++ b/core/node/libp2p/pubsub.go @@ -1,26 +1,85 @@ package libp2p import ( + "context" + "errors" + "log/slog" + + "github.com/ipfs/go-datastore" + logging "github.com/ipfs/go-log/v2" pubsub "github.com/libp2p/go-libp2p-pubsub" "github.com/libp2p/go-libp2p/core/discovery" "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/peer" "go.uber.org/fx" "github.com/ipfs/kubo/core/node/helpers" + "github.com/ipfs/kubo/repo" ) +type pubsubParams struct { + fx.In + + Repo repo.Repo + Host host.Host + Discovery discovery.Discovery +} + func FloodSub(pubsubOptions ...pubsub.Option) interface{} { - return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, disc discovery.Discovery) (service *pubsub.PubSub, err error) { - return pubsub.NewFloodSub(helpers.LifecycleCtx(mctx, lc), host, append(pubsubOptions, pubsub.WithDiscovery(disc))...) + return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, params pubsubParams) (service *pubsub.PubSub, err error) { + return pubsub.NewFloodSub( + helpers.LifecycleCtx(mctx, lc), + params.Host, + append(pubsubOptions, + pubsub.WithDiscovery(params.Discovery), + pubsub.WithDefaultValidator(newSeqnoValidator(params.Repo.Datastore())))..., + ) } } func GossipSub(pubsubOptions ...pubsub.Option) interface{} { - return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, host host.Host, disc discovery.Discovery) (service *pubsub.PubSub, err error) { - return pubsub.NewGossipSub(helpers.LifecycleCtx(mctx, lc), host, append( - pubsubOptions, - pubsub.WithDiscovery(disc), - pubsub.WithFloodPublish(true))..., + return func(mctx helpers.MetricsCtx, lc fx.Lifecycle, params pubsubParams) (service *pubsub.PubSub, err error) { + return pubsub.NewGossipSub( + helpers.LifecycleCtx(mctx, lc), + params.Host, + append(pubsubOptions, + pubsub.WithDiscovery(params.Discovery), + pubsub.WithFloodPublish(true), // flood own publications to all peers for reliable IPNS delivery + pubsub.WithDefaultValidator(newSeqnoValidator(params.Repo.Datastore())))..., ) } } + +func newSeqnoValidator(ds datastore.Datastore) pubsub.ValidatorEx { + return pubsub.NewBasicSeqnoValidator(&seqnoStore{ds: ds}, slog.New(logging.SlogHandler()).With("logger", "pubsub")) +} + +// SeqnoStorePrefix is the datastore prefix for pubsub seqno validator state. +const SeqnoStorePrefix = "/pubsub/seqno/" + +// seqnoStore implements pubsub.PeerMetadataStore using the repo datastore. +// It stores the maximum seen sequence number per peer to prevent message +// cycles when network diameter exceeds the timecache span. +type seqnoStore struct { + ds datastore.Datastore +} + +var _ pubsub.PeerMetadataStore = (*seqnoStore)(nil) + +// Get returns the stored seqno for a peer, or (nil, nil) if the peer is unknown. +// Returning (nil, nil) for unknown peers allows BasicSeqnoValidator to accept +// the first message from any peer. +func (s *seqnoStore) Get(ctx context.Context, p peer.ID) ([]byte, error) { + key := datastore.NewKey(SeqnoStorePrefix + p.String()) + val, err := s.ds.Get(ctx, key) + if errors.Is(err, datastore.ErrNotFound) { + return nil, nil + } + return val, err +} + +// Put stores the seqno for a peer. +func (s *seqnoStore) Put(ctx context.Context, p peer.ID, val []byte) error { + key := datastore.NewKey(SeqnoStorePrefix + p.String()) + return s.ds.Put(ctx, key, val) +} diff --git a/core/node/libp2p/pubsub_test.go b/core/node/libp2p/pubsub_test.go new file mode 100644 index 000000000..6c38b8f8c --- /dev/null +++ b/core/node/libp2p/pubsub_test.go @@ -0,0 +1,130 @@ +package libp2p + +import ( + "encoding/binary" + "testing" + + "github.com/ipfs/go-datastore" + syncds "github.com/ipfs/go-datastore/sync" + "github.com/libp2p/go-libp2p/core/peer" + "github.com/stretchr/testify/require" +) + +// TestSeqnoStore tests the seqnoStore implementation which backs the +// BasicSeqnoValidator. The validator prevents message cycles when network +// diameter exceeds the timecache span by tracking the maximum sequence number +// seen from each peer. +func TestSeqnoStore(t *testing.T) { + ctx := t.Context() + ds := syncds.MutexWrap(datastore.NewMapDatastore()) + store := &seqnoStore{ds: ds} + + peerA, err := peer.Decode("12D3KooWGC6TvWhfapngX6wvJHMYvKpDMXPb3ZnCZ6dMoaMtimQ5") + require.NoError(t, err) + peerB, err := peer.Decode("12D3KooWJRqDKTRjvXeGdUEgwkHNsoghYMBUagNYgLPdA4mqdTeo") + require.NoError(t, err) + + // BasicSeqnoValidator expects Get to return (nil, nil) for unknown peers, + // not an error. This allows the validator to accept the first message from + // any peer without special-casing. + t.Run("unknown peer returns nil without error", func(t *testing.T) { + val, err := store.Get(ctx, peerA) + require.NoError(t, err) + require.Nil(t, val, "unknown peer should return nil, not empty slice") + }) + + // Verify basic store/retrieve functionality with a sequence number encoded + // as big-endian uint64, matching the format used by BasicSeqnoValidator. + t.Run("stores and retrieves seqno", func(t *testing.T) { + seqno := uint64(12345) + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, seqno) + + err := store.Put(ctx, peerA, data) + require.NoError(t, err) + + val, err := store.Get(ctx, peerA) + require.NoError(t, err) + require.Equal(t, seqno, binary.BigEndian.Uint64(val)) + }) + + // Each peer must have isolated storage. If peer data leaked between peers, + // the validator would incorrectly reject valid messages or accept replays. + t.Run("isolates seqno per peer", func(t *testing.T) { + seqnoA := uint64(100) + seqnoB := uint64(200) + dataA := make([]byte, 8) + dataB := make([]byte, 8) + binary.BigEndian.PutUint64(dataA, seqnoA) + binary.BigEndian.PutUint64(dataB, seqnoB) + + err := store.Put(ctx, peerA, dataA) + require.NoError(t, err) + err = store.Put(ctx, peerB, dataB) + require.NoError(t, err) + + valA, err := store.Get(ctx, peerA) + require.NoError(t, err) + require.Equal(t, seqnoA, binary.BigEndian.Uint64(valA)) + + valB, err := store.Get(ctx, peerB) + require.NoError(t, err) + require.Equal(t, seqnoB, binary.BigEndian.Uint64(valB)) + }) + + // The validator updates the stored seqno when accepting messages with + // higher seqnos. This test verifies that updates work correctly. + t.Run("updates seqno to higher value", func(t *testing.T) { + seqno1 := uint64(1000) + seqno2 := uint64(2000) + data1 := make([]byte, 8) + data2 := make([]byte, 8) + binary.BigEndian.PutUint64(data1, seqno1) + binary.BigEndian.PutUint64(data2, seqno2) + + err := store.Put(ctx, peerA, data1) + require.NoError(t, err) + + err = store.Put(ctx, peerA, data2) + require.NoError(t, err) + + val, err := store.Get(ctx, peerA) + require.NoError(t, err) + require.Equal(t, seqno2, binary.BigEndian.Uint64(val)) + }) + + // Verify the datastore key format. This is important for: + // 1. Debugging: operators can inspect/clear pubsub state + // 2. Migrations: future changes need to know the key format + t.Run("uses expected datastore key format", func(t *testing.T) { + seqno := uint64(42) + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, seqno) + + err := store.Put(ctx, peerA, data) + require.NoError(t, err) + + // Verify we can read directly from datastore with expected key + expectedKey := datastore.NewKey("/pubsub/seqno/" + peerA.String()) + val, err := ds.Get(ctx, expectedKey) + require.NoError(t, err) + require.Equal(t, seqno, binary.BigEndian.Uint64(val)) + }) + + // Verify data persists when creating a new store instance with the same + // underlying datastore. This simulates node restart. + t.Run("persists across store instances", func(t *testing.T) { + seqno := uint64(99999) + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, seqno) + + err := store.Put(ctx, peerB, data) + require.NoError(t, err) + + // Create new store instance with same datastore + store2 := &seqnoStore{ds: ds} + val, err := store2.Get(ctx, peerB) + require.NoError(t, err) + require.Equal(t, seqno, binary.BigEndian.Uint64(val)) + }) +} diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index ab5e5762b..ff7b77fc1 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -13,6 +13,8 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [๐Ÿงน Automatic cleanup of interrupted imports](#-automatic-cleanup-of-interrupted-imports) - [Routing V1 HTTP API now exposed by default](#routing-v1-http-api-now-exposed-by-default) - [Track total size when adding pins](#track-total-size-when-adding-pins) + - [Improved IPNS over PubSub validation](#improved-ipns-over-pubsub-validation) + - [New `ipfs diag datastore` commands](#new-ipfs-diag-datastore-commands) - [๐Ÿš‡ Improved `ipfs p2p` tunnels with foreground mode](#-improved-ipfs-p2p-tunnels-with-foreground-mode) - [Improved `ipfs dag stat` output](#improved-ipfs-dag-stat-output) - [Skip bad keys when listing](#skip_bad_keys_when_listing) @@ -49,6 +51,23 @@ Example output: Fetched/Processed 336 nodes (83 MB) ``` +#### Improved IPNS over PubSub validation + +[IPNS over PubSub](https://specs.ipfs.tech/ipns/ipns-pubsub-router/) implementation in Kubo is now more reliable. Duplicate messages are rejected even in large networks where messages may cycle back after the in-memory cache expires. + +Kubo now persists the maximum seen sequence number per peer to the datastore ([go-libp2p-pubsub#BasicSeqnoValidator](https://pkg.go.dev/github.com/libp2p/go-libp2p-pubsub#BasicSeqnoValidator)), providing stronger duplicate detection that survives node restarts. This addresses message flooding issues reported in [#9665](https://github.com/ipfs/kubo/issues/9665). + +Kubo's pubsub is optimized for IPNS use case. For custom pubsub applications requiring different validation logic, use [go-libp2p-pubsub](https://github.com/libp2p/go-libp2p-pubsub) directly in a dedicated binary. + +#### New `ipfs diag datastore` commands + +New experimental commands for low-level datastore inspection: + +- `ipfs diag datastore get ` - Read raw value at a datastore key (use `--hex` for hex dump) +- `ipfs diag datastore count ` - Count entries matching a datastore prefix + +The daemon must not be running when using these commands. Run `ipfs diag datastore --help` for usage examples. + #### ๐Ÿš‡ Improved `ipfs p2p` tunnels with foreground mode P2P tunnels can now run like SSH port forwarding: start a tunnel, use it, and it cleans up automatically when you're done. diff --git a/docs/config.md b/docs/config.md index 99b92838c..e6ab44d04 100644 --- a/docs/config.md +++ b/docs/config.md @@ -146,6 +146,8 @@ config file at runtime. - [`Provider.Strategy`](#providerstrategy) - [`Provider.WorkerCount`](#providerworkercount) - [`Pubsub`](#pubsub) + - [When to use a dedicated pubsub node](#when-to-use-a-dedicated-pubsub-node) + - [Message deduplication](#message-deduplication) - [`Pubsub.Enabled`](#pubsubenabled) - [`Pubsub.Router`](#pubsubrouter) - [`Pubsub.DisableSigning`](#pubsubdisablesigning) @@ -1787,7 +1789,7 @@ Type: `optionalDuration` ### `Ipns.UsePubsub` -Enables IPFS over pubsub experiment for publishing IPNS records in real time. +Enables [IPNS over PubSub](https://specs.ipfs.tech/ipns/ipns-pubsub-router/) for publishing and resolving IPNS records in real time. **EXPERIMENTAL:** read about current limitations at [experimental-features.md#ipns-pubsub](./experimental-features.md#ipns-pubsub). @@ -2405,16 +2407,56 @@ Replaced with [`Provide.DHT.MaxWorkers`](#providedhtmaxworkers). ## `Pubsub` -**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) +Pubsub configures Kubo's opt-in, opinionated [libp2p pubsub](https://docs.libp2p.io/concepts/pubsub/overview/) instance. +To enable, set `Pubsub.Enabled` to `true`. -Pubsub configures the `ipfs pubsub` subsystem. To use, it must be enabled by -passing the `--enable-pubsub-experiment` flag to the daemon -or via the `Pubsub.Enabled` flag below. +**EXPERIMENTAL:** This is an opt-in feature. Its primary use case is +[IPNS over PubSub](https://specs.ipfs.tech/ipns/ipns-pubsub-router/), which +enables real-time IPNS record propagation. See [`Ipns.UsePubsub`](#ipnsusepubsub) +for details. + +The `ipfs pubsub` commands can also be used for basic publish/subscribe +operations, but only if Kubo's built-in message validation (described below) is +acceptable for your use case. + +### When to use a dedicated pubsub node + +Kubo's pubsub is optimized for IPNS. It uses opinionated message validation +that may not fit all applications. If you need custom Message ID computation, +different deduplication logic, or validation rules beyond what Kubo provides, +consider building a dedicated pubsub node using +[go-libp2p-pubsub](https://github.com/libp2p/go-libp2p-pubsub) directly. + +### Message deduplication + +Kubo uses two layers of message deduplication to handle duplicate messages that +may arrive via different network paths: + +**Layer 1: In-memory TimeCache (Message ID)** + +When a message arrives, Kubo computes its Message ID (hash of the message +content) and checks an in-memory cache. If the ID was seen recently, the +message is dropped. This cache is controlled by: + +- [`Pubsub.SeenMessagesTTL`](#pubsubseenmessagesttl) - how long Message IDs are remembered (default: 120s) +- [`Pubsub.SeenMessagesStrategy`](#pubsubseenmessagesstrategy) - whether TTL resets on each sighting + +This cache is fast but limited: it only works within the TTL window and is +cleared on node restart. + +**Layer 2: Persistent Seqno Validator (per-peer)** + +For stronger deduplication, Kubo tracks the maximum sequence number seen from +each peer and persists it to the datastore. Messages with sequence numbers +lower than the recorded maximum are rejected. This prevents replay attacks and +handles message cycles in large networks where messages may take longer than +the TimeCache TTL to propagate. + +This layer survives node restarts. The state can be inspected or cleared using +`ipfs pubsub reset` (for testing/recovery only). ### `Pubsub.Enabled` -**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) - Enables the pubsub system. Default: `false` @@ -2423,8 +2465,6 @@ Type: `flag` ### `Pubsub.Router` -**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) - Sets the default router used by pubsub to route messages to peers. This can be one of: - `"floodsub"` - floodsub is a basic router that simply _floods_ messages to all @@ -2440,10 +2480,9 @@ Type: `string` (one of `"floodsub"`, `"gossipsub"`, or `""` (apply default)) ### `Pubsub.DisableSigning` -**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) +Disables message signing and signature verification. -Disables message signing and signature verification. Enable this option if -you're operating in a completely trusted network. +**FOR TESTING ONLY - DO NOT USE IN PRODUCTION** It is _not_ safe to disable signing even if you don't care _who_ sent the message because spoofed messages can be used to silence real messages by @@ -2455,20 +2494,12 @@ Type: `bool` ### `Pubsub.SeenMessagesTTL` -**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) +Controls the time window for the in-memory Message ID cache (Layer 1 +deduplication). Messages with the same ID seen within this window are dropped. -Controls the time window within which duplicate messages, identified by Message -ID, will be identified and won't be emitted again. - -A smaller value for this parameter means that Pubsub messages in the cache will -be garbage collected sooner, which can result in a smaller cache. At the same -time, if there are slower nodes in the network that forward older messages, -this can cause more duplicates to be propagated through the network. - -Conversely, a larger value for this parameter means that Pubsub messages in the -cache will be garbage collected later, which can result in a larger cache for -the same traffic pattern. However, it is less likely that duplicates will be -propagated through the network. +A smaller value reduces memory usage but may cause more duplicates in networks +with slow nodes. A larger value uses more memory but provides better duplicate +detection within the time window. Default: see `TimeCacheDuration` from [go-libp2p-pubsub](https://github.com/libp2p/go-libp2p-pubsub) @@ -2476,24 +2507,12 @@ Type: `optionalDuration` ### `Pubsub.SeenMessagesStrategy` -**DEPRECATED**: See [#9717](https://github.com/ipfs/kubo/issues/9717) +Determines how the TTL countdown for the Message ID cache works. -Determines how the time-to-live (TTL) countdown for deduplicating Pubsub -messages is calculated. - -The Pubsub seen messages cache is a LRU cache that keeps messages for up to a -specified time duration. After this duration has elapsed, expired messages will -be purged from the cache. - -The `last-seen` cache is a sliding-window cache. Every time a message is seen -again with the SeenMessagesTTL duration, its timestamp slides forward. This -keeps frequently occurring messages cached and prevents them from being -continually propagated, especially because of issues that might increase the -number of duplicate messages in the network. - -The `first-seen` cache will store new messages and purge them after the -SeenMessagesTTL duration, even if they are seen multiple times within this -duration. +- `last-seen` - Sliding window: TTL resets each time the message is seen again. + Keeps frequently-seen messages in cache longer, preventing continued propagation. +- `first-seen` - Fixed window: TTL counts from first sighting only. Messages are + purged after the TTL regardless of how many times they're seen. Default: `last-seen` (see [go-libp2p-pubsub](https://github.com/libp2p/go-libp2p-pubsub)) diff --git a/docs/experimental-features.md b/docs/experimental-features.md index fdca90fbe..2b490e44a 100644 --- a/docs/experimental-features.md +++ b/docs/experimental-features.md @@ -375,6 +375,8 @@ kubo now automatically shards when directory block is bigger than 256KB, ensurin ## IPNS pubsub +Specification: [IPNS PubSub Router](https://specs.ipfs.tech/ipns/ipns-pubsub-router/) + ### In Version 0.4.14 : @@ -389,13 +391,18 @@ kubo now automatically shards when directory block is bigger than 256KB, ensurin 0.11.0 : - Can be enabled via `Ipns.UsePubsub` flag in config +0.40.0 : + - Persistent message sequence number validation to prevent message cycles + in large networks + ### State Experimental, default-disabled. -Utilizes pubsub for publishing ipns records in real time. +Utilizes pubsub for publishing IPNS records in real time. When it is enabled: + - IPNS publishers push records to a name-specific pubsub topic, in addition to publishing to the DHT. - IPNS resolvers subscribe to the name-specific topic on first @@ -404,9 +411,6 @@ When it is enabled: Both the publisher and the resolver nodes need to have the feature enabled for it to work effectively. -Note: While IPNS pubsub has been available since 0.4.14, it received major changes in 0.5.0. -Users interested in this feature should upgrade to at least 0.5.0 - ### How to enable Run your daemon with the `--enable-namesys-pubsub` flag @@ -416,13 +420,12 @@ ipfs config --json Ipns.UsePubsub true ``` NOTE: -- This feature implicitly enables [ipfs pubsub](#ipfs-pubsub). +- This feature implicitly enables pubsub. - Passing `--enable-namesys-pubsub` CLI flag overrides `Ipns.UsePubsub` config. ### Road to being a real feature - [ ] Needs more people to use and report on how well it works -- [ ] Pubsub enabled as a real feature ## AutoRelay diff --git a/test/cli/diag_datastore_test.go b/test/cli/diag_datastore_test.go new file mode 100644 index 000000000..2a69f60cc --- /dev/null +++ b/test/cli/diag_datastore_test.go @@ -0,0 +1,147 @@ +package cli + +import ( + "encoding/json" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestDiagDatastore(t *testing.T) { + t.Parallel() + + t.Run("diag datastore get returns error for non-existent key", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + // Don't start daemon - these commands require daemon to be stopped + + res := node.RunIPFS("diag", "datastore", "get", "/nonexistent/key") + assert.Error(t, res.Err) + assert.Contains(t, res.Stderr.String(), "key not found") + }) + + t.Run("diag datastore get returns raw bytes by default", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + // Add some data to create a known datastore key + // We need daemon for add, then stop it + node.StartDaemon() + cid := node.IPFSAddStr("test data for diag datastore") + node.IPFS("pin", "add", cid) + node.StopDaemon() + + // Test count to verify we have entries + count := node.DatastoreCount("/") + t.Logf("total datastore entries: %d", count) + assert.NotEqual(t, int64(0), count, "should have datastore entries after pinning") + }) + + t.Run("diag datastore get --hex returns hex dump", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + // Add and pin some data + node.StartDaemon() + cid := node.IPFSAddStr("test data for hex dump") + node.IPFS("pin", "add", cid) + node.StopDaemon() + + // Test with existing keys in pins namespace + count := node.DatastoreCount("/pins/") + t.Logf("pins datastore entries: %d", count) + + if count != 0 { + t.Log("pins datastore has entries, hex dump format tested implicitly") + } + }) + + t.Run("diag datastore count returns 0 for empty prefix", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + count := node.DatastoreCount("/definitely/nonexistent/prefix/") + assert.Equal(t, int64(0), count) + }) + + t.Run("diag datastore count returns JSON with --enc=json", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + res := node.IPFS("diag", "datastore", "count", "/pubsub/seqno/", "--enc=json") + assert.NoError(t, res.Err) + + var result struct { + Prefix string `json:"prefix"` + Count int64 `json:"count"` + } + err := json.Unmarshal(res.Stdout.Bytes(), &result) + require.NoError(t, err) + assert.Equal(t, "/pubsub/seqno/", result.Prefix) + assert.Equal(t, int64(0), result.Count) + }) + + t.Run("diag datastore get returns JSON with --enc=json", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + // Test error case with JSON encoding + res := node.RunIPFS("diag", "datastore", "get", "/nonexistent", "--enc=json") + assert.Error(t, res.Err) + }) + + t.Run("diag datastore count counts entries correctly", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + + // Add multiple pins to create multiple entries + node.StartDaemon() + cid1 := node.IPFSAddStr("data 1") + cid2 := node.IPFSAddStr("data 2") + cid3 := node.IPFSAddStr("data 3") + + node.IPFS("pin", "add", cid1) + node.IPFS("pin", "add", cid2) + node.IPFS("pin", "add", cid3) + node.StopDaemon() + + // Count should reflect the pins (plus any system entries) + count := node.DatastoreCount("/") + t.Logf("total entries after adding 3 pins: %d", count) + + // Should have more than 0 entries + assert.NotEqual(t, int64(0), count) + }) + + t.Run("diag datastore commands work offline", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init() + // Don't start daemon - these commands require daemon to be stopped + + // Count should work offline + count := node.DatastoreCount("/pubsub/seqno/") + assert.Equal(t, int64(0), count) + + // Get should return error for missing key (but command should work) + res := node.RunIPFS("diag", "datastore", "get", "/nonexistent/key") + assert.Error(t, res.Err) + assert.Contains(t, res.Stderr.String(), "key not found") + }) + + t.Run("diag datastore commands require daemon to be stopped", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + + // Both get and count require repo lock, which is held by the running daemon + res := node.RunIPFS("diag", "datastore", "get", "/test") + assert.Error(t, res.Err, "get should fail when daemon is running") + assert.Contains(t, res.Stderr.String(), "ipfs daemon is running") + + res = node.RunIPFS("diag", "datastore", "count", "/pubsub/seqno/") + assert.Error(t, res.Err, "count should fail when daemon is running") + assert.Contains(t, res.Stderr.String(), "ipfs daemon is running") + }) +} diff --git a/test/cli/harness/node.go b/test/cli/harness/node.go index 0315e81df..8d5262db0 100644 --- a/test/cli/harness/node.go +++ b/test/cli/harness/node.go @@ -730,3 +730,28 @@ func (n *Node) APIClient() *HTTPClient { BaseURL: n.APIURL(), } } + +// DatastoreCount returns the count of entries matching the given prefix. +// Requires the daemon to be stopped. +func (n *Node) DatastoreCount(prefix string) int64 { + res := n.IPFS("diag", "datastore", "count", prefix) + count, _ := strconv.ParseInt(strings.TrimSpace(res.Stdout.String()), 10, 64) + return count +} + +// DatastoreGet retrieves the value at the given key. +// Requires the daemon to be stopped. Returns nil if key not found. +func (n *Node) DatastoreGet(key string) []byte { + res := n.RunIPFS("diag", "datastore", "get", key) + if res.Err != nil { + return nil + } + return res.Stdout.Bytes() +} + +// DatastoreHasKey checks if a key exists in the datastore. +// Requires the daemon to be stopped. +func (n *Node) DatastoreHasKey(key string) bool { + res := n.RunIPFS("diag", "datastore", "get", key) + return res.Err == nil +} diff --git a/test/cli/pubsub_test.go b/test/cli/pubsub_test.go new file mode 100644 index 000000000..4ce3ca8bf --- /dev/null +++ b/test/cli/pubsub_test.go @@ -0,0 +1,403 @@ +package cli + +import ( + "context" + "encoding/json" + "slices" + "testing" + "time" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// waitForSubscription waits until the node has a subscription to the given topic. +func waitForSubscription(t *testing.T, node *harness.Node, topic string) { + t.Helper() + require.Eventually(t, func() bool { + res := node.RunIPFS("pubsub", "ls") + if res.Err != nil { + return false + } + return slices.Contains(res.Stdout.Lines(), topic) + }, 5*time.Second, 100*time.Millisecond, "expected subscription to topic %s", topic) +} + +// waitForMessagePropagation waits for pubsub messages to propagate through the network +// and for seqno state to be persisted to the datastore. +func waitForMessagePropagation(t *testing.T) { + t.Helper() + time.Sleep(1 * time.Second) +} + +// publishMessages publishes n messages from publisher to the given topic with +// a small delay between each to allow for ordered delivery. +func publishMessages(t *testing.T, publisher *harness.Node, topic string, n int) { + t.Helper() + for i := 0; i < n; i++ { + publisher.PipeStrToIPFS("msg", "pubsub", "pub", topic) + time.Sleep(50 * time.Millisecond) + } +} + +// TestPubsub tests pubsub functionality and the persistent seqno validator. +// +// Pubsub has two deduplication layers: +// +// Layer 1: MessageID-based TimeCache (in-memory) +// - Controlled by Pubsub.SeenMessagesTTL config (default 120s) +// - Tested in go-libp2p-pubsub (see timecache in github.com/libp2p/go-libp2p-pubsub) +// - Only tested implicitly here via message delivery (timing-sensitive, not practical for CLI tests) +// +// Layer 2: Per-peer seqno validator (persistent in datastore) +// - Stores max seen seqno per peer at /pubsub/seqno/ +// - Tested directly below: persistence, updates, reset, survives restart +// - Validator: go-libp2p-pubsub BasicSeqnoValidator +func TestPubsub(t *testing.T) { + t.Parallel() + + // enablePubsub configures a node with pubsub enabled + enablePubsub := func(n *harness.Node) { + n.SetIPFSConfig("Pubsub.Enabled", true) + n.SetIPFSConfig("Routing.Type", "none") // simplify test setup + } + + t.Run("basic pub/sub message delivery", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + + // Create two connected nodes with pubsub enabled + nodes := h.NewNodes(2).Init() + nodes.ForEachPar(enablePubsub) + nodes = nodes.StartDaemons().Connect() + defer nodes.StopDaemons() + + subscriber := nodes[0] + publisher := nodes[1] + + const topic = "test-topic" + const message = "hello pubsub" + + // Start subscriber in background + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + // Use a channel to receive the message + msgChan := make(chan string, 1) + go func() { + // Subscribe and wait for one message + res := subscriber.RunIPFS("pubsub", "sub", "--enc=json", topic) + if res.Err == nil { + // Parse JSON output to get message data + lines := res.Stdout.Lines() + if len(lines) > 0 { + var msg struct { + Data []byte `json:"data"` + } + if json.Unmarshal([]byte(lines[0]), &msg) == nil { + msgChan <- string(msg.Data) + } + } + } + }() + + // Wait for subscriber to be ready + waitForSubscription(t, subscriber, topic) + + // Publish message + publisher.PipeStrToIPFS(message, "pubsub", "pub", topic) + + // Wait for message or timeout + select { + case received := <-msgChan: + assert.Equal(t, message, received) + case <-ctx.Done(): + // Subscriber may not receive in time due to test timing - that's OK + // The main goal is to test the seqno validator state persistence + t.Log("subscriber did not receive message in time (this is acceptable)") + } + }) + + t.Run("seqno validator state is persisted", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + + // Create two connected nodes with pubsub + nodes := h.NewNodes(2).Init() + nodes.ForEachPar(enablePubsub) + nodes = nodes.StartDaemons().Connect() + + node1 := nodes[0] + node2 := nodes[1] + node2PeerID := node2.PeerID().String() + + const topic = "seqno-test" + + // Start subscriber on node1 + go func() { + node1.RunIPFS("pubsub", "sub", topic) + }() + waitForSubscription(t, node1, topic) + + // Publish multiple messages from node2 to trigger seqno validation + publishMessages(t, node2, topic, 3) + + // Wait for messages to propagate and seqno to be stored + waitForMessagePropagation(t) + + // Stop daemons to check datastore (diag datastore requires daemon to be stopped) + nodes.StopDaemons() + + // Check that seqno state exists + count := node1.DatastoreCount("/pubsub/seqno/") + t.Logf("seqno entries count: %d", count) + + // There should be at least one seqno entry (from node2) + assert.NotEqual(t, int64(0), count, "expected seqno state to be persisted") + + // Verify the specific peer's key exists and test --hex output format + key := "/pubsub/seqno/" + node2PeerID + res := node1.RunIPFS("diag", "datastore", "get", "--hex", key) + if res.Err == nil { + t.Logf("seqno for peer %s:\n%s", node2PeerID, res.Stdout.String()) + assert.Contains(t, res.Stdout.String(), "Hex Dump:") + } else { + // Key might not exist if messages didn't propagate - log but don't fail + t.Logf("seqno key not found for peer %s (messages may not have propagated)", node2PeerID) + } + }) + + t.Run("seqno updates when receiving multiple messages", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + + // Create two connected nodes with pubsub + nodes := h.NewNodes(2).Init() + nodes.ForEachPar(enablePubsub) + nodes = nodes.StartDaemons().Connect() + + node1 := nodes[0] + node2 := nodes[1] + node2PeerID := node2.PeerID().String() + + const topic = "seqno-update-test" + seqnoKey := "/pubsub/seqno/" + node2PeerID + + // Start subscriber on node1 + go func() { + node1.RunIPFS("pubsub", "sub", topic) + }() + waitForSubscription(t, node1, topic) + + // Send first message + node2.PipeStrToIPFS("msg1", "pubsub", "pub", topic) + time.Sleep(500 * time.Millisecond) + + // Stop daemons to check seqno (diag datastore requires daemon to be stopped) + nodes.StopDaemons() + + // Get seqno after first message + res1 := node1.RunIPFS("diag", "datastore", "get", seqnoKey) + var seqno1 []byte + if res1.Err == nil { + seqno1 = res1.Stdout.Bytes() + t.Logf("seqno after first message: %d bytes", len(seqno1)) + } else { + t.Logf("seqno not found after first message (message may not have propagated)") + } + + // Restart daemons for second message + nodes = nodes.StartDaemons().Connect() + + // Resubscribe + go func() { + node1.RunIPFS("pubsub", "sub", topic) + }() + waitForSubscription(t, node1, topic) + + // Send second message + node2.PipeStrToIPFS("msg2", "pubsub", "pub", topic) + time.Sleep(500 * time.Millisecond) + + // Stop daemons to check seqno + nodes.StopDaemons() + + // Get seqno after second message + res2 := node1.RunIPFS("diag", "datastore", "get", seqnoKey) + var seqno2 []byte + if res2.Err == nil { + seqno2 = res2.Stdout.Bytes() + t.Logf("seqno after second message: %d bytes", len(seqno2)) + } else { + t.Logf("seqno not found after second message") + } + + // If both messages were received, seqno should have been updated + // The seqno is a uint64 that should increase with each message + if len(seqno1) > 0 && len(seqno2) > 0 { + // seqno2 should be >= seqno1 (it's the max seen seqno) + // We just verify they're both non-empty and potentially different + t.Logf("seqno1: %x", seqno1) + t.Logf("seqno2: %x", seqno2) + // The seqno validator stores the max seqno seen, so seqno2 >= seqno1 + // We can't do a simple byte comparison due to potential endianness + // but both should be valid uint64 values (8 bytes) + assert.Equal(t, 8, len(seqno2), "seqno should be 8 bytes (uint64)") + } + }) + + t.Run("pubsub reset clears seqno state", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + + // Create two connected nodes + nodes := h.NewNodes(2).Init() + nodes.ForEachPar(enablePubsub) + nodes = nodes.StartDaemons().Connect() + + node1 := nodes[0] + node2 := nodes[1] + + const topic = "reset-test" + + // Start subscriber and exchange messages + go func() { + node1.RunIPFS("pubsub", "sub", topic) + }() + waitForSubscription(t, node1, topic) + + publishMessages(t, node2, topic, 3) + waitForMessagePropagation(t) + + // Stop daemons to check initial count + nodes.StopDaemons() + + // Verify there is state before resetting + initialCount := node1.DatastoreCount("/pubsub/seqno/") + t.Logf("initial seqno count: %d", initialCount) + + // Restart node1 to run pubsub reset + node1.StartDaemon() + + // Reset all seqno state (while daemon is running) + res := node1.IPFS("pubsub", "reset") + assert.NoError(t, res.Err) + t.Logf("reset output: %s", res.Stdout.String()) + + // Stop daemon to verify state was cleared + node1.StopDaemon() + + // Verify state was cleared + finalCount := node1.DatastoreCount("/pubsub/seqno/") + t.Logf("final seqno count: %d", finalCount) + assert.Equal(t, int64(0), finalCount, "seqno state should be cleared after reset") + }) + + t.Run("pubsub reset with peer flag", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + + // Create three connected nodes + nodes := h.NewNodes(3).Init() + nodes.ForEachPar(enablePubsub) + nodes = nodes.StartDaemons().Connect() + + node1 := nodes[0] + node2 := nodes[1] + node3 := nodes[2] + node2PeerID := node2.PeerID().String() + node3PeerID := node3.PeerID().String() + + const topic = "peer-reset-test" + + // Start subscriber on node1 + go func() { + node1.RunIPFS("pubsub", "sub", topic) + }() + waitForSubscription(t, node1, topic) + + // Publish from both node2 and node3 + for range 3 { + node2.PipeStrToIPFS("msg2", "pubsub", "pub", topic) + node3.PipeStrToIPFS("msg3", "pubsub", "pub", topic) + time.Sleep(50 * time.Millisecond) + } + waitForMessagePropagation(t) + + // Stop node2 and node3 + node2.StopDaemon() + node3.StopDaemon() + + // Reset only node2's state (while node1 daemon is running) + res := node1.IPFS("pubsub", "reset", "--peer", node2PeerID) + require.NoError(t, res.Err) + t.Logf("reset output: %s", res.Stdout.String()) + + // Stop node1 daemon to check datastore + node1.StopDaemon() + + // Check that node2's key is gone + res = node1.RunIPFS("diag", "datastore", "get", "/pubsub/seqno/"+node2PeerID) + assert.Error(t, res.Err, "node2's seqno key should be deleted") + + // Check that node3's key still exists (if it was created) + res = node1.RunIPFS("diag", "datastore", "get", "/pubsub/seqno/"+node3PeerID) + // Note: node3's key might not exist if messages didn't propagate + // So we just log the result without asserting + if res.Err == nil { + t.Logf("node3's seqno key still exists (as expected)") + } else { + t.Logf("node3's seqno key not found (messages may not have propagated)") + } + }) + + t.Run("seqno state survives daemon restart", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + + // Create and start single node + node := h.NewNode().Init() + enablePubsub(node) + node.StartDaemon() + + // We need another node to publish messages + node2 := h.NewNode().Init() + enablePubsub(node2) + node2.StartDaemon() + node.Connect(node2) + + const topic = "restart-test" + + // Start subscriber and exchange messages + go func() { + node.RunIPFS("pubsub", "sub", topic) + }() + waitForSubscription(t, node, topic) + + publishMessages(t, node2, topic, 3) + waitForMessagePropagation(t) + + // Stop daemons to check datastore + node.StopDaemon() + node2.StopDaemon() + + // Get count before restart + beforeCount := node.DatastoreCount("/pubsub/seqno/") + t.Logf("seqno count before restart: %d", beforeCount) + + // Restart node (simulate restart scenario) + node.StartDaemon() + time.Sleep(500 * time.Millisecond) + + // Stop daemon to check datastore again + node.StopDaemon() + + // Get count after restart + afterCount := node.DatastoreCount("/pubsub/seqno/") + t.Logf("seqno count after restart: %d", afterCount) + + // Count should be the same (state persisted) + assert.Equal(t, beforeCount, afterCount, "seqno state should survive daemon restart") + }) +} diff --git a/test/integration/pubsub_msg_seen_cache_test.go b/test/integration/pubsub_msg_seen_cache_test.go deleted file mode 100644 index 85cc8ae9f..000000000 --- a/test/integration/pubsub_msg_seen_cache_test.go +++ /dev/null @@ -1,285 +0,0 @@ -package integrationtest - -import ( - "bytes" - "context" - "fmt" - "io" - "testing" - "time" - - "go.uber.org/fx" - - "github.com/ipfs/boxo/bootstrap" - "github.com/ipfs/kubo/config" - "github.com/ipfs/kubo/core" - "github.com/ipfs/kubo/core/coreapi" - libp2p2 "github.com/ipfs/kubo/core/node/libp2p" - "github.com/ipfs/kubo/repo" - - "github.com/ipfs/go-datastore" - syncds "github.com/ipfs/go-datastore/sync" - - pubsub "github.com/libp2p/go-libp2p-pubsub" - pubsub_pb "github.com/libp2p/go-libp2p-pubsub/pb" - "github.com/libp2p/go-libp2p-pubsub/timecache" - "github.com/libp2p/go-libp2p/core/peer" - - mock "github.com/ipfs/kubo/core/mock" - mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" -) - -func TestMessageSeenCacheTTL(t *testing.T) { - t.Skip("skipping PubSub seen cache TTL test due to flakiness") - if err := RunMessageSeenCacheTTLTest(t, "10s"); err != nil { - t.Fatal(err) - } -} - -func mockNode(ctx context.Context, mn mocknet.Mocknet, pubsubEnabled bool, seenMessagesCacheTTL string) (*core.IpfsNode, error) { - ds := syncds.MutexWrap(datastore.NewMapDatastore()) - cfg, err := config.Init(io.Discard, 2048) - if err != nil { - return nil, err - } - count := len(mn.Peers()) - cfg.Addresses.Swarm = []string{ - fmt.Sprintf("/ip4/18.0.%d.%d/tcp/4001", count>>16, count&0xFF), - } - cfg.Datastore = config.Datastore{} - if pubsubEnabled { - cfg.Pubsub.Enabled = config.True - var ttl *config.OptionalDuration - if len(seenMessagesCacheTTL) > 0 { - ttl = &config.OptionalDuration{} - if err = ttl.UnmarshalJSON([]byte(seenMessagesCacheTTL)); err != nil { - return nil, err - } - } - cfg.Pubsub.SeenMessagesTTL = ttl - } - return core.NewNode(ctx, &core.BuildCfg{ - Online: true, - Routing: libp2p2.DHTServerOption, - Repo: &repo.Mock{ - C: *cfg, - D: ds, - }, - Host: mock.MockHostOption(mn), - ExtraOpts: map[string]bool{ - "pubsub": pubsubEnabled, - }, - }) -} - -func RunMessageSeenCacheTTLTest(t *testing.T, seenMessagesCacheTTL string) error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - var bootstrapNode, consumerNode, producerNode *core.IpfsNode - var bootstrapPeerID, consumerPeerID, producerPeerID peer.ID - - mn := mocknet.New() - bootstrapNode, err := mockNode(ctx, mn, false, "") // no need for PubSub configuration - if err != nil { - t.Fatal(err) - } - bootstrapPeerID = bootstrapNode.PeerHost.ID() - defer bootstrapNode.Close() - - consumerNode, err = mockNode(ctx, mn, true, seenMessagesCacheTTL) // use passed seen cache TTL - if err != nil { - t.Fatal(err) - } - consumerPeerID = consumerNode.PeerHost.ID() - defer consumerNode.Close() - - ttl, err := time.ParseDuration(seenMessagesCacheTTL) - if err != nil { - t.Fatal(err) - } - - // Used for logging the timeline - startTime := time.Time{} - - // Used for overriding the message ID - sendMsgID := "" - - // Set up the pubsub message ID generation override for the producer - core.RegisterFXOptionFunc(func(info core.FXNodeInfo) ([]fx.Option, error) { - var pubsubOptions []pubsub.Option - pubsubOptions = append( - pubsubOptions, - pubsub.WithSeenMessagesTTL(ttl), - pubsub.WithMessageIdFn(func(pmsg *pubsub_pb.Message) string { - now := time.Now() - if startTime.Second() == 0 { - startTime = now - } - timeElapsed := now.Sub(startTime).Seconds() - msg := string(pmsg.Data) - from, _ := peer.IDFromBytes(pmsg.From) - var msgID string - if from == producerPeerID { - msgID = sendMsgID - t.Logf("sending [%s] with message ID [%s] at T%fs", msg, msgID, timeElapsed) - } else { - msgID = pubsub.DefaultMsgIdFn(pmsg) - } - return msgID - }), - pubsub.WithSeenMessagesStrategy(timecache.Strategy_LastSeen), - ) - return append( - info.FXOptions, - fx.Provide(libp2p2.TopicDiscovery()), - fx.Decorate(libp2p2.GossipSub(pubsubOptions...)), - ), nil - }) - - producerNode, err = mockNode(ctx, mn, false, "") // PubSub configuration comes from overrides above - if err != nil { - t.Fatal(err) - } - producerPeerID = producerNode.PeerHost.ID() - defer producerNode.Close() - - t.Logf("bootstrap peer=%s, consumer peer=%s, producer peer=%s", bootstrapPeerID, consumerPeerID, producerPeerID) - - producerAPI, err := coreapi.NewCoreAPI(producerNode) - if err != nil { - t.Fatal(err) - } - consumerAPI, err := coreapi.NewCoreAPI(consumerNode) - if err != nil { - t.Fatal(err) - } - - err = mn.LinkAll() - if err != nil { - t.Fatal(err) - } - - bis := bootstrapNode.Peerstore.PeerInfo(bootstrapNode.PeerHost.ID()) - bcfg := bootstrap.BootstrapConfigWithPeers([]peer.AddrInfo{bis}) - if err = producerNode.Bootstrap(bcfg); err != nil { - t.Fatal(err) - } - if err = consumerNode.Bootstrap(bcfg); err != nil { - t.Fatal(err) - } - - // Set up the consumer subscription - const TopicName = "topic" - consumerSubscription, err := consumerAPI.PubSub().Subscribe(ctx, TopicName) - if err != nil { - t.Fatal(err) - } - // Utility functions defined inline to include context in closure - now := func() float64 { - return time.Since(startTime).Seconds() - } - ctr := 0 - msgGen := func() string { - ctr++ - return fmt.Sprintf("msg_%d", ctr) - } - produceMessage := func() string { - msgTxt := msgGen() - err = producerAPI.PubSub().Publish(ctx, TopicName, []byte(msgTxt)) - if err != nil { - t.Fatal(err) - } - return msgTxt - } - consumeMessage := func(msgTxt string, shouldFind bool) { - // Set up a separate timed context for receiving messages - rxCtx, rxCancel := context.WithTimeout(context.Background(), time.Second) - defer rxCancel() - msg, err := consumerSubscription.Next(rxCtx) - if shouldFind { - if err != nil { - t.Logf("expected but did not receive [%s] at T%fs", msgTxt, now()) - t.Fatal(err) - } - t.Logf("received [%s] at T%fs", string(msg.Data()), now()) - if !bytes.Equal(msg.Data(), []byte(msgTxt)) { - t.Fatalf("consumed data [%s] does not match published data [%s]", string(msg.Data()), msgTxt) - } - } else { - if err == nil { - t.Logf("not expected but received [%s] at T%fs", string(msg.Data()), now()) - t.Fail() - } - t.Logf("did not receive [%s] at T%fs", msgTxt, now()) - } - } - - const MsgID1 = "MsgID1" - const MsgID2 = "MsgID2" - const MsgID3 = "MsgID3" - - // Send message 1 with the message ID we're going to duplicate - sentMsg1 := time.Now() - sendMsgID = MsgID1 - msgTxt := produceMessage() - // Should find the message because it's new - consumeMessage(msgTxt, true) - - // Send message 2 with a duplicate message ID - sendMsgID = MsgID1 - msgTxt = produceMessage() - // Should NOT find message because it got deduplicated (sent 2 times within the SeenMessagesTTL window). - consumeMessage(msgTxt, false) - - // Send message 3 with a new message ID - sendMsgID = MsgID2 - msgTxt = produceMessage() - // Should find the message because it's new - consumeMessage(msgTxt, true) - - // Wait till just before the SeenMessagesTTL window has passed since message 1 was sent - time.Sleep(time.Until(sentMsg1.Add(ttl - 100*time.Millisecond))) - - // Send message 4 with a duplicate message ID - sendMsgID = MsgID1 - msgTxt = produceMessage() - // Should NOT find the message because it got deduplicated (sent 3 times within the SeenMessagesTTL window). This - // time, however, the expiration for the message should also get pushed out for a whole SeenMessagesTTL window since - // the default time cache now implements a sliding window algorithm. - consumeMessage(msgTxt, false) - - // Send message 5 with a duplicate message ID. This will be a second after the last attempt above since NOT finding - // a message takes a second to determine. That would put this attempt at ~1 second after the SeenMessagesTTL window - // starting at message 1 has expired. - sentMsg5 := time.Now() - sendMsgID = MsgID1 - msgTxt = produceMessage() - // Should NOT find the message, because it got deduplicated (sent 2 times since the updated SeenMessagesTTL window - // started). This time again, the expiration should get pushed out for another SeenMessagesTTL window. - consumeMessage(msgTxt, false) - - // Send message 6 with a message ID that hasn't been seen within a SeenMessagesTTL window - sendMsgID = MsgID2 - msgTxt = produceMessage() - // Should find the message since last read > SeenMessagesTTL, so it looks like a new message. - consumeMessage(msgTxt, true) - - // Sleep for a full SeenMessagesTTL window to let cache entries time out - time.Sleep(time.Until(sentMsg5.Add(ttl + 100*time.Millisecond))) - - // Send message 7 with a duplicate message ID - sendMsgID = MsgID1 - msgTxt = produceMessage() - // Should find the message this time since last read > SeenMessagesTTL, so it looks like a new message. - consumeMessage(msgTxt, true) - - // Send message 8 with a brand new message ID - // - // This step is not strictly necessary, but has been added for good measure. - sendMsgID = MsgID3 - msgTxt = produceMessage() - // Should find the message because it's new - consumeMessage(msgTxt, true) - return nil -} From c1fd4d70f58e682bfe73fa4b50d17581c823c671 Mon Sep 17 00:00:00 2001 From: segfault_bits <149077711+sneaxhuh@users.noreply.github.com> Date: Fri, 16 Jan 2026 06:57:32 +0530 Subject: [PATCH 30/49] feat(cli): ls --long (#11103) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Implements the -l/--long flag for the ipfs ls command to display Unix-style file permissions and modification times, similar to the traditional ls -l. When the --long flag is used, the output includes: - File mode/permissions in Unix format (e.g., -rw-r--r--, drwxr-xr-x) - File hash (CID) - File size (when --size is also specified) - Modification time in human-readable format - File name The permission string implementation handles all file types and special bits: - File types: regular (-), directory (d), symlink (l), named pipe (p), socket (s), character device (c), block device (b) - Special permission bits: setuid (s/S), setgid (s/S), sticky (t/T) - Lowercase when execute bit is set, uppercase when not set The timestamp format follows Unix ls conventions: - Recent files (within 6 months): "Jan 02 15:04" - Older files: "Jan 02 2006" Signed-off-by: sneax * fix(ls): correct --long flag header order and help text - fix header column order: was "Mode Hash Size Name ModTime" but data outputs "Mode Hash Size ModTime Name", now headers match data order - remove redundant if/else branch in directory output that had identical code in both branches - add example output to help text showing format with mode, hash, size, mtime, and name columns - document that files without preserved metadata show '----------' for mode and '-' for mtime - add changelog entry for v0.40 * test(ls): add format stability tests for --long flag add tests to prevent formatting regressions in ipfs ls --long output: unit tests (core/commands/ls_test.go): - TestFormatMode: 20 cases covering all file types (regular, dir, symlink, pipe, socket, block/char devices) and special permission bits (setuid, setgid, sticky with/without execute) - TestFormatModTime: zero time, old time (year format), future time, format length consistency integration tests (test/cli/ls_test.go): - explicit full output comparison with deterministic CIDs to catch any formatting changes - header column order verification for --long with --size=true/false - files without preserved metadata (---------- and - placeholders) - directory output (trailing slash, d prefix in mode) requested in: https://github.com/ipfs/kubo/pull/11103#issuecomment-3745043561 * fix(ls): improve --long flag docs and fix minor issues - improved godocs for formatMode and formatModTime functions - fixed permBit signature: char rune โ†’ char byte (avoids unnecessary cast) - clarified help text: mode/mtime are optional UnixFS metadata - documented that times are displayed in UTC - fixed flaky time test by using 1 month ago instead of 1 hour - removed hardcoded CID assertion that would break on DAG changes * fix(ls): show "-" for missing mode in --long output display "-" instead of "----------" when mode metadata is not preserved. this avoids ambiguity with Unix mode 0000 and matches how missing mtime is already displayed. follows common Unix tool conventions (ps, netstat) where "-" indicates "not available". --------- Signed-off-by: sneax Co-authored-by: Marcin Rataj --- core/commands/ls.go | 215 ++++++++++++++++++++++++++++++--- core/commands/ls_test.go | 189 +++++++++++++++++++++++++++++ docs/changelogs/v0.40.md | 5 + test/cli/ls_test.go | 254 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 643 insertions(+), 20 deletions(-) create mode 100644 core/commands/ls_test.go create mode 100644 test/cli/ls_test.go diff --git a/core/commands/ls.go b/core/commands/ls.go index 327b159a1..1f54753f1 100644 --- a/core/commands/ls.go +++ b/core/commands/ls.go @@ -48,6 +48,7 @@ const ( lsResolveTypeOptionName = "resolve-type" lsSizeOptionName = "size" lsStreamOptionName = "stream" + lsLongOptionName = "long" ) var LsCmd = &cmds.Command{ @@ -57,7 +58,26 @@ var LsCmd = &cmds.Command{ Displays the contents of an IPFS or IPNS object(s) at the given path, with the following format: - + + +With the --long (-l) option, display optional file mode (permissions) and +modification time in a format similar to Unix 'ls -l': + + + +Mode and mtime are optional UnixFS metadata. They are only present if the +content was imported with 'ipfs add --preserve-mode' and '--preserve-mtime'. +Without preserved metadata, both mode and mtime display '-'. Times are in UTC. + +Example with --long and preserved metadata: + + -rw-r--r-- QmZULkCELmmk5XNf... 1234 Jan 15 10:30 document.txt + -rwxr-xr-x QmaRGe7bVmVaLmxb... 5678 Dec 01 2023 script.sh + drwxr-xr-x QmWWEQhcLufF3qPm... - Nov 20 2023 subdir/ + +Example with --long without preserved metadata: + + - QmZULkCELmmk5XNf... 1234 - document.txt The JSON output contains type information. `, @@ -71,6 +91,7 @@ The JSON output contains type information. cmds.BoolOption(lsResolveTypeOptionName, "Resolve linked objects to find out their types.").WithDefault(true), cmds.BoolOption(lsSizeOptionName, "Resolve linked objects to find out their file size.").WithDefault(true), cmds.BoolOption(lsStreamOptionName, "s", "Enable experimental streaming of directory entries as they are traversed."), + cmds.BoolOption(lsLongOptionName, "l", "Use a long listing format, showing file mode and modification time."), }, Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { api, err := cmdenv.GetApi(env, req) @@ -215,10 +236,121 @@ The JSON output contains type information. Type: LsOutput{}, } +// formatMode converts os.FileMode to a 10-character Unix ls-style string. +// +// Format: [type][owner rwx][group rwx][other rwx] +// +// Type indicators: - (regular), d (directory), l (symlink), p (named pipe), +// s (socket), c (char device), b (block device). +// +// Special bits replace the execute position: setuid on owner (s/S), +// setgid on group (s/S), sticky on other (t/T). Lowercase when the +// underlying execute bit is also set, uppercase when not. +func formatMode(mode os.FileMode) string { + var buf [10]byte + + // File type - handle all special file types like ls does + switch { + case mode&os.ModeDir != 0: + buf[0] = 'd' + case mode&os.ModeSymlink != 0: + buf[0] = 'l' + case mode&os.ModeNamedPipe != 0: + buf[0] = 'p' + case mode&os.ModeSocket != 0: + buf[0] = 's' + case mode&os.ModeDevice != 0: + if mode&os.ModeCharDevice != 0 { + buf[0] = 'c' + } else { + buf[0] = 'b' + } + default: + buf[0] = '-' + } + + // Owner permissions (bits 8,7,6) + buf[1] = permBit(mode, 0400, 'r') // read + buf[2] = permBit(mode, 0200, 'w') // write + // Handle setuid bit for owner execute + if mode&os.ModeSetuid != 0 { + if mode&0100 != 0 { + buf[3] = 's' + } else { + buf[3] = 'S' + } + } else { + buf[3] = permBit(mode, 0100, 'x') // execute + } + + // Group permissions (bits 5,4,3) + buf[4] = permBit(mode, 0040, 'r') // read + buf[5] = permBit(mode, 0020, 'w') // write + // Handle setgid bit for group execute + if mode&os.ModeSetgid != 0 { + if mode&0010 != 0 { + buf[6] = 's' + } else { + buf[6] = 'S' + } + } else { + buf[6] = permBit(mode, 0010, 'x') // execute + } + + // Other permissions (bits 2,1,0) + buf[7] = permBit(mode, 0004, 'r') // read + buf[8] = permBit(mode, 0002, 'w') // write + // Handle sticky bit for other execute + if mode&os.ModeSticky != 0 { + if mode&0001 != 0 { + buf[9] = 't' + } else { + buf[9] = 'T' + } + } else { + buf[9] = permBit(mode, 0001, 'x') // execute + } + + return string(buf[:]) +} + +// permBit returns the permission character if the bit is set. +func permBit(mode os.FileMode, bit os.FileMode, char byte) byte { + if mode&bit != 0 { + return char + } + return '-' +} + +// formatModTime formats time.Time for display, following Unix ls conventions. +// +// Returns "-" for zero time. Otherwise returns a 12-character string: +// recent files (within 6 months) show "Jan 02 15:04", +// older or future files show "Jan 02 2006". +// +// The output uses the timezone embedded in t (UTC for IPFS metadata). +func formatModTime(t time.Time) string { + if t.IsZero() { + return "-" + } + + // Format: "Jan 02 15:04" for times within the last 6 months + // Format: "Jan 02 2006" for older times (similar to ls) + now := time.Now() + sixMonthsAgo := now.AddDate(0, -6, 0) + + if t.After(sixMonthsAgo) && t.Before(now.Add(24*time.Hour)) { + return t.Format("Jan 02 15:04") + } + return t.Format("Jan 02 2006") +} + func tabularOutput(req *cmds.Request, w io.Writer, out *LsOutput, lastObjectHash string, ignoreBreaks bool) string { headers, _ := req.Options[lsHeadersOptionNameTime].(bool) stream, _ := req.Options[lsStreamOptionName].(bool) size, _ := req.Options[lsSizeOptionName].(bool) + long, _ := req.Options[lsLongOptionName].(bool) + // in streaming mode we can't automatically align the tabs // so we take a best guess var minTabWidth int @@ -242,9 +374,21 @@ func tabularOutput(req *cmds.Request, w io.Writer, out *LsOutput, lastObjectHash fmt.Fprintf(tw, "%s:\n", object.Hash) } if headers { - s := "Hash\tName" - if size { - s = "Hash\tSize\tName" + var s string + if long { + // Long format: Mode Hash [Size] ModTime Name + if size { + s = "Mode\tHash\tSize\tModTime\tName" + } else { + s = "Mode\tHash\tModTime\tName" + } + } else { + // Standard format: Hash [Size] Name + if size { + s = "Hash\tSize\tName" + } else { + s = "Hash\tName" + } } fmt.Fprintln(tw, s) } @@ -253,23 +397,54 @@ func tabularOutput(req *cmds.Request, w io.Writer, out *LsOutput, lastObjectHash for _, link := range object.Links { var s string - switch link.Type { - case unixfs.TDirectory, unixfs.THAMTShard, unixfs.TMetadata: - if size { - s = "%[1]s\t-\t%[3]s/\n" - } else { - s = "%[1]s\t%[3]s/\n" - } - default: - if size { - s = "%s\t%v\t%s\n" - } else { - s = "%[1]s\t%[3]s\n" - } - } + isDir := link.Type == unixfs.TDirectory || link.Type == unixfs.THAMTShard || link.Type == unixfs.TMetadata - // TODO: Print link.Mode and link.ModTime? - fmt.Fprintf(tw, s, link.Hash, link.Size, cmdenv.EscNonPrint(link.Name)) + if long { + // Long format: Mode Hash Size ModTime Name + var mode string + if link.Mode == 0 { + // No mode metadata preserved. Show "-" to indicate + // "not available" rather than "----------" (mode 0000). + mode = "-" + } else { + mode = formatMode(link.Mode) + } + modTime := formatModTime(link.ModTime) + + if isDir { + if size { + s = "%s\t%s\t-\t%s\t%s/\n" + } else { + s = "%s\t%s\t%s\t%s/\n" + } + fmt.Fprintf(tw, s, mode, link.Hash, modTime, cmdenv.EscNonPrint(link.Name)) + } else { + if size { + s = "%s\t%s\t%v\t%s\t%s\n" + fmt.Fprintf(tw, s, mode, link.Hash, link.Size, modTime, cmdenv.EscNonPrint(link.Name)) + } else { + s = "%s\t%s\t%s\t%s\n" + fmt.Fprintf(tw, s, mode, link.Hash, modTime, cmdenv.EscNonPrint(link.Name)) + } + } + } else { + // Standard format: Hash [Size] Name + switch { + case isDir: + if size { + s = "%[1]s\t-\t%[3]s/\n" + } else { + s = "%[1]s\t%[3]s/\n" + } + default: + if size { + s = "%s\t%v\t%s\n" + } else { + s = "%[1]s\t%[3]s\n" + } + } + fmt.Fprintf(tw, s, link.Hash, link.Size, cmdenv.EscNonPrint(link.Name)) + } } } tw.Flush() diff --git a/core/commands/ls_test.go b/core/commands/ls_test.go new file mode 100644 index 000000000..243d72e5d --- /dev/null +++ b/core/commands/ls_test.go @@ -0,0 +1,189 @@ +package commands + +import ( + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestFormatMode(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + mode os.FileMode + expected string + }{ + // File types + { + name: "regular file with rw-r--r--", + mode: 0644, + expected: "-rw-r--r--", + }, + { + name: "regular file with rwxr-xr-x", + mode: 0755, + expected: "-rwxr-xr-x", + }, + { + name: "regular file with no permissions", + mode: 0, + expected: "----------", + }, + { + name: "regular file with full permissions", + mode: 0777, + expected: "-rwxrwxrwx", + }, + { + name: "directory with rwxr-xr-x", + mode: os.ModeDir | 0755, + expected: "drwxr-xr-x", + }, + { + name: "directory with rwx------", + mode: os.ModeDir | 0700, + expected: "drwx------", + }, + { + name: "symlink with rwxrwxrwx", + mode: os.ModeSymlink | 0777, + expected: "lrwxrwxrwx", + }, + { + name: "named pipe with rw-r--r--", + mode: os.ModeNamedPipe | 0644, + expected: "prw-r--r--", + }, + { + name: "socket with rw-rw-rw-", + mode: os.ModeSocket | 0666, + expected: "srw-rw-rw-", + }, + { + name: "block device with rw-rw----", + mode: os.ModeDevice | 0660, + expected: "brw-rw----", + }, + { + name: "character device with rw-rw-rw-", + mode: os.ModeDevice | os.ModeCharDevice | 0666, + expected: "crw-rw-rw-", + }, + + // Special permission bits - setuid + { + name: "setuid with execute", + mode: os.ModeSetuid | 0755, + expected: "-rwsr-xr-x", + }, + { + name: "setuid without execute", + mode: os.ModeSetuid | 0644, + expected: "-rwSr--r--", + }, + + // Special permission bits - setgid + { + name: "setgid with execute", + mode: os.ModeSetgid | 0755, + expected: "-rwxr-sr-x", + }, + { + name: "setgid without execute", + mode: os.ModeSetgid | 0745, + expected: "-rwxr-Sr-x", + }, + + // Special permission bits - sticky + { + name: "sticky with execute", + mode: os.ModeSticky | 0755, + expected: "-rwxr-xr-t", + }, + { + name: "sticky without execute", + mode: os.ModeSticky | 0754, + expected: "-rwxr-xr-T", + }, + + // Combined special bits + { + name: "setuid + setgid + sticky all with execute", + mode: os.ModeSetuid | os.ModeSetgid | os.ModeSticky | 0777, + expected: "-rwsrwsrwt", + }, + { + name: "setuid + setgid + sticky none with execute", + mode: os.ModeSetuid | os.ModeSetgid | os.ModeSticky | 0666, + expected: "-rwSrwSrwT", + }, + + // Directory with special bits + { + name: "directory with sticky bit", + mode: os.ModeDir | os.ModeSticky | 0755, + expected: "drwxr-xr-t", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + result := formatMode(tc.mode) + assert.Equal(t, tc.expected, result) + }) + } +} + +func TestFormatModTime(t *testing.T) { + t.Parallel() + + t.Run("zero time returns dash", func(t *testing.T) { + t.Parallel() + result := formatModTime(time.Time{}) + assert.Equal(t, "-", result) + }) + + t.Run("old time shows year format", func(t *testing.T) { + t.Parallel() + // Use a time clearly in the past (more than 6 months ago) + oldTime := time.Date(2020, time.March, 15, 10, 30, 0, 0, time.UTC) + result := formatModTime(oldTime) + // Format: "Jan 02 2006" (note: two spaces before year) + assert.Equal(t, "Mar 15 2020", result) + }) + + t.Run("very old time shows year format", func(t *testing.T) { + t.Parallel() + veryOldTime := time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) + result := formatModTime(veryOldTime) + assert.Equal(t, "Jan 01 2000", result) + }) + + t.Run("future time shows year format", func(t *testing.T) { + t.Parallel() + // Times more than 24h in the future should show year format + futureTime := time.Now().AddDate(1, 0, 0) + result := formatModTime(futureTime) + // Should contain the future year + assert.Contains(t, result, " ") // two spaces before year + assert.Regexp(t, `^[A-Z][a-z]{2} \d{2} \d{4}$`, result) // matches "Mon DD YYYY" + assert.Contains(t, result, futureTime.Format("2006")) // contains the year + }) + + t.Run("format lengths are consistent", func(t *testing.T) { + t.Parallel() + // Both formats should produce 12-character strings for alignment + oldTime := time.Date(2020, time.March, 15, 10, 30, 0, 0, time.UTC) + oldResult := formatModTime(oldTime) + assert.Len(t, oldResult, 12, "old time format should be 12 chars") + + // Recent time: use 1 month ago to ensure it's always within the 6-month window + recentTime := time.Now().AddDate(0, -1, 0) + recentResult := formatModTime(recentTime) + assert.Len(t, recentResult, 12, "recent time format should be 12 chars") + }) +} diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index ff7b77fc1..524852a30 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -21,6 +21,7 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [Accelerated DHT Client and Provide Sweep now work together](#accelerated-dht-client-and-provide-sweep-now-work-together) - [โฑ๏ธ Configurable gateway request duration limit](#๏ธ-configurable-gateway-request-duration-limit) - [๐Ÿ”ง Recovery from corrupted MFS root](#-recovery-from-corrupted-mfs-root) + - [๐Ÿ“‹ Long listing format for `ipfs ls`](#-long-listing-format-for-ipfs-ls) - [๐Ÿ“ฆ๏ธ Dependency updates](#-dependency-updates) - [๐Ÿ“ Changelog](#-changelog) - [๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Contributors](#-contributors) @@ -139,6 +140,10 @@ $ ipfs files chroot --confirm QmYourBackupCID See `ipfs files chroot --help` for details. +#### ๐Ÿ“‹ Long listing format for `ipfs ls` + +The `ipfs ls` command now supports `--long` (`-l`) flag for displaying Unix-style file permissions and modification times. This works with files added using `--preserve-mode` and `--preserve-mtime`. See `ipfs ls --help` for format details and examples. + #### ๐Ÿ“ฆ๏ธ Dependency updates - update `go-libp2p` to [v0.46.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.46.0) diff --git a/test/cli/ls_test.go b/test/cli/ls_test.go new file mode 100644 index 000000000..d1bd98677 --- /dev/null +++ b/test/cli/ls_test.go @@ -0,0 +1,254 @@ +package cli + +import ( + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLsLongFormat(t *testing.T) { + t.Parallel() + + t.Run("long format shows mode and mtime when preserved", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + + // Create a test directory structure with known permissions + testDir := filepath.Join(node.Dir, "testdata") + require.NoError(t, os.MkdirAll(testDir, 0755)) + + // Create files with specific permissions + file1 := filepath.Join(testDir, "readable.txt") + require.NoError(t, os.WriteFile(file1, []byte("hello"), 0644)) + + file2 := filepath.Join(testDir, "executable.sh") + require.NoError(t, os.WriteFile(file2, []byte("#!/bin/sh\necho hi"), 0755)) + + // Set a known mtime in the past (to get year format, avoiding flaky time-based tests) + oldTime := time.Date(2020, time.June, 15, 10, 30, 0, 0, time.UTC) + require.NoError(t, os.Chtimes(file1, oldTime, oldTime)) + require.NoError(t, os.Chtimes(file2, oldTime, oldTime)) + + // Add with preserved mode and mtime + addRes := node.IPFS("add", "-r", "--preserve-mode", "--preserve-mtime", "-Q", testDir) + dirCid := addRes.Stdout.Trimmed() + + // Run ls with --long flag + lsRes := node.IPFS("ls", "--long", dirCid) + output := lsRes.Stdout.String() + + // Verify format: Mode Hash Size ModTime Name + lines := strings.Split(strings.TrimSpace(output), "\n") + require.Len(t, lines, 2, "expected 2 files in output") + + // Check executable.sh line (should be first alphabetically) + assert.Contains(t, lines[0], "-rwxr-xr-x", "executable should have 755 permissions") + assert.Contains(t, lines[0], "Jun 15 2020", "should show mtime with year format") + assert.Contains(t, lines[0], "executable.sh", "should show filename") + + // Check readable.txt line + assert.Contains(t, lines[1], "-rw-r--r--", "readable file should have 644 permissions") + assert.Contains(t, lines[1], "Jun 15 2020", "should show mtime with year format") + assert.Contains(t, lines[1], "readable.txt", "should show filename") + }) + + t.Run("long format shows dash for files without preserved mode or mtime", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + + // Create and add a file without --preserve-mode or --preserve-mtime + testFile := filepath.Join(node.Dir, "nopreserve.txt") + require.NoError(t, os.WriteFile(testFile, []byte("test content"), 0644)) + + addRes := node.IPFS("add", "-Q", testFile) + fileCid := addRes.Stdout.Trimmed() + + // Create a wrapper directory to list + node.IPFS("files", "mkdir", "/testdir") + node.IPFS("files", "cp", "/ipfs/"+fileCid, "/testdir/file.txt") + statRes := node.IPFS("files", "stat", "--hash", "/testdir") + dirCid := statRes.Stdout.Trimmed() + + // Run ls with --long flag + lsRes := node.IPFS("ls", "--long", dirCid) + output := lsRes.Stdout.String() + + // Files without preserved mode or mtime should show "-" for both columns + // Format: "-" (mode) "-" (mtime) + assert.Regexp(t, `^-\s+\S+\s+\d+\s+-\s+`, output, "missing mode and mtime should both show dash") + }) + + t.Run("long format with headers shows correct column order", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + + // Create a simple test file + testDir := filepath.Join(node.Dir, "headertest") + require.NoError(t, os.MkdirAll(testDir, 0755)) + testFile := filepath.Join(testDir, "file.txt") + require.NoError(t, os.WriteFile(testFile, []byte("hello"), 0644)) + + oldTime := time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC) + require.NoError(t, os.Chtimes(testFile, oldTime, oldTime)) + + addRes := node.IPFS("add", "-r", "--preserve-mode", "--preserve-mtime", "-Q", testDir) + dirCid := addRes.Stdout.Trimmed() + + // Run ls with --long and --headers (--size defaults to true) + lsRes := node.IPFS("ls", "--long", "--headers", dirCid) + output := lsRes.Stdout.String() + lines := strings.Split(strings.TrimSpace(output), "\n") + + // First line should be headers in correct order: Mode Hash Size ModTime Name + require.GreaterOrEqual(t, len(lines), 2) + headerFields := strings.Fields(lines[0]) + require.Len(t, headerFields, 5, "header should have 5 columns") + assert.Equal(t, "Mode", headerFields[0]) + assert.Equal(t, "Hash", headerFields[1]) + assert.Equal(t, "Size", headerFields[2]) + assert.Equal(t, "ModTime", headerFields[3]) + assert.Equal(t, "Name", headerFields[4]) + + // Data line should have matching columns + dataFields := strings.Fields(lines[1]) + require.GreaterOrEqual(t, len(dataFields), 5) + assert.Regexp(t, `^-[rwx-]{9}$`, dataFields[0], "first field should be mode") + assert.Regexp(t, `^Qm`, dataFields[1], "second field should be CID") + assert.Regexp(t, `^\d+$`, dataFields[2], "third field should be size") + }) + + t.Run("long format with headers and size=false", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + + testDir := filepath.Join(node.Dir, "headertest2") + require.NoError(t, os.MkdirAll(testDir, 0755)) + testFile := filepath.Join(testDir, "file.txt") + require.NoError(t, os.WriteFile(testFile, []byte("hello"), 0644)) + + oldTime := time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC) + require.NoError(t, os.Chtimes(testFile, oldTime, oldTime)) + + addRes := node.IPFS("add", "-r", "--preserve-mode", "--preserve-mtime", "-Q", testDir) + dirCid := addRes.Stdout.Trimmed() + + // Run ls with --long --headers --size=false + lsRes := node.IPFS("ls", "--long", "--headers", "--size=false", dirCid) + output := lsRes.Stdout.String() + lines := strings.Split(strings.TrimSpace(output), "\n") + + // Header should be: Mode Hash ModTime Name (no Size) + require.GreaterOrEqual(t, len(lines), 2) + headerFields := strings.Fields(lines[0]) + require.Len(t, headerFields, 4, "header should have 4 columns without size") + assert.Equal(t, "Mode", headerFields[0]) + assert.Equal(t, "Hash", headerFields[1]) + assert.Equal(t, "ModTime", headerFields[2]) + assert.Equal(t, "Name", headerFields[3]) + }) + + t.Run("long format for directories shows trailing slash", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + + // Create nested directory structure + testDir := filepath.Join(node.Dir, "dirtest") + subDir := filepath.Join(testDir, "subdir") + require.NoError(t, os.MkdirAll(subDir, 0755)) + require.NoError(t, os.WriteFile(filepath.Join(subDir, "file.txt"), []byte("hi"), 0644)) + + addRes := node.IPFS("add", "-r", "--preserve-mode", "-Q", testDir) + dirCid := addRes.Stdout.Trimmed() + + // Run ls with --long flag + lsRes := node.IPFS("ls", "--long", dirCid) + output := lsRes.Stdout.String() + + // Directory should end with / + assert.Contains(t, output, "subdir/", "directory should have trailing slash") + // Directory should show 'd' in mode + assert.Contains(t, output, "drwxr-xr-x", "directory should show directory mode") + }) + + t.Run("long format without size flag", func(t *testing.T) { + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + + testDir := filepath.Join(node.Dir, "nosizetest") + require.NoError(t, os.MkdirAll(testDir, 0755)) + testFile := filepath.Join(testDir, "file.txt") + require.NoError(t, os.WriteFile(testFile, []byte("hello world"), 0644)) + + oldTime := time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC) + require.NoError(t, os.Chtimes(testFile, oldTime, oldTime)) + + addRes := node.IPFS("add", "-r", "--preserve-mode", "--preserve-mtime", "-Q", testDir) + dirCid := addRes.Stdout.Trimmed() + + // Run ls with --long but --size=false + lsRes := node.IPFS("ls", "--long", "--size=false", dirCid) + output := lsRes.Stdout.String() + + // Should still have mode and mtime, but format differs (no size column) + assert.Contains(t, output, "-rw-r--r--") + assert.Contains(t, output, "Jan 01 2020") + assert.Contains(t, output, "file.txt") + }) + + t.Run("long format output is stable", func(t *testing.T) { + // This test ensures the output format doesn't change due to refactors + t.Parallel() + node := harness.NewT(t).NewNode().Init().StartDaemon() + defer node.StopDaemon() + + testDir := filepath.Join(node.Dir, "stabletest") + require.NoError(t, os.MkdirAll(testDir, 0755)) + testFile := filepath.Join(testDir, "test.txt") + require.NoError(t, os.WriteFile(testFile, []byte("stable"), 0644)) + + // Use a fixed time for reproducibility + fixedTime := time.Date(2020, time.December, 25, 12, 0, 0, 0, time.UTC) + require.NoError(t, os.Chtimes(testFile, fixedTime, fixedTime)) + + addRes := node.IPFS("add", "-r", "--preserve-mode", "--preserve-mtime", "-Q", testDir) + dirCid := addRes.Stdout.Trimmed() + + // The CID should be deterministic given same content, mode, and mtime + // This is the expected CID for this specific test data + lsRes := node.IPFS("ls", "--long", dirCid) + output := strings.TrimSpace(lsRes.Stdout.String()) + + // Verify the format: ModeHashSizeModTimeName + fields := strings.Fields(output) + require.GreaterOrEqual(t, len(fields), 5, "output should have at least 5 fields") + + // Field 0: mode (10 chars, starts with - for regular file) + assert.Regexp(t, `^-[rwx-]{9}$`, fields[0], "mode should be Unix permission format") + + // Field 1: CID (starts with Qm or bafy) + assert.Regexp(t, `^(Qm|bafy)`, fields[1], "second field should be CID") + + // Field 2: size (numeric) + assert.Regexp(t, `^\d+$`, fields[2], "third field should be numeric size") + + // Fields 3-4: date (e.g., "Dec 25 2020" or "Dec 25 12:00") + // The date format is "Mon DD YYYY" for old files + assert.Equal(t, "Dec", fields[3]) + assert.Equal(t, "25", fields[4]) + + // Last field: filename + assert.Equal(t, "test.txt", fields[len(fields)-1]) + }) +} From 8848b537cf472fe0f032bc3d1f453785e574afbd Mon Sep 17 00:00:00 2001 From: Guillaume Michel Date: Wed, 21 Jan 2026 05:59:56 +0100 Subject: [PATCH 31/49] fix(commands/reprovide): update manual reprovide error message (#11151) Reprovide Sweep is now the default behaviour since v0.39.0. Update manual reprovide error message to reflect this change. --- core/commands/routing.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/commands/routing.go b/core/commands/routing.go index 5e1d5334d..629c30515 100644 --- a/core/commands/routing.go +++ b/core/commands/routing.go @@ -17,6 +17,7 @@ import ( dag "github.com/ipfs/boxo/ipld/merkledag" "github.com/ipfs/boxo/ipns" + "github.com/ipfs/boxo/provider" cid "github.com/ipfs/go-cid" cmds "github.com/ipfs/go-ipfs-cmds" ipld "github.com/ipfs/go-ipld-format" @@ -296,9 +297,9 @@ Trigger reprovider to announce our data to network. if cfg.Provide.DHT.Interval.WithDefault(config.DefaultProvideDHTInterval) == 0 { return errors.New("invalid configuration: Provide.DHT.Interval is set to '0'") } - provideSys, ok := nd.Provider.(*node.LegacyProvider) + provideSys, ok := nd.Provider.(provider.Reprovider) if !ok { - return errors.New("manual reprovide not available with experimental sweeping provider (Provide.DHT.SweepEnabled=true)") + return errors.New("manual reprovide only available with legacy provider (Provide.DHT.SweepEnabled=false)") } err = provideSys.Reprovide(req.Context) From 1128d810424f08363028afbb0422efe4efc326eb Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 23 Jan 2026 05:27:30 +0100 Subject: [PATCH 32/49] chore: add gomod dependabot config (#11153) experimental setup to surface missed ecosystem updates via monthly PRs groups ipfs, libp2p, multiformats, ipld, and golang.org/x dependencies to reduce noise while ensuring visibility into available updates --- .github/dependabot.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a..60b670878 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,25 @@ updates: directory: "/" schedule: interval: "weekly" + + - package-ecosystem: "gomod" + directory: "/" + schedule: + interval: "monthly" + open-pull-requests-limit: 10 + labels: + - "dependencies" + groups: + ipfs-ecosystem: + patterns: + - "github.com/ipfs/*" + - "github.com/ipfs-shipyard/*" + - "github.com/ipshipyard/*" + - "github.com/multiformats/*" + - "github.com/ipld/*" + libp2p-ecosystem: + patterns: + - "github.com/libp2p/*" + golang-x: + patterns: + - "golang.org/x/*" From 6d253a6b8034a03650e6ab6ea39537946fc6c590 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 23 Jan 2026 06:17:32 +0100 Subject: [PATCH 33/49] chore(ci): dependabot fixes (#11164) * chore: exclude ancient +incompatible versions from go.mod prevents Dependabot from failing when it tries to update go-ipfs-cmds and go-libp2p directly and resolves to pre-Go-modules v2.x/v6.x versions that reference deleted packages * chore(deps): group opentelemetry, prometheus, and uber packages reduces PR noise by batching related observability dependencies * ci: add workflow to run make mod_tidy on Dependabot PRs ensures all go.mod/go.sum files stay in sync when Dependabot updates dependencies in the root module supports manual dispatch with PR number for existing PRs --- .github/dependabot.yml | 12 ++++++ .github/workflows/dependabot-tidy.yml | 61 +++++++++++++++++++++++++++ go.mod | 7 +++ 3 files changed, 80 insertions(+) create mode 100644 .github/workflows/dependabot-tidy.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 60b670878..4f285be44 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,3 +1,4 @@ +# Dependabot PRs are auto-tidied by .github/workflows/dependabot-tidy.yml version: 2 updates: - package-ecosystem: "github-actions" @@ -26,3 +27,14 @@ updates: golang-x: patterns: - "golang.org/x/*" + opentelemetry: + patterns: + - "go.opentelemetry.io/*" + prometheus: + patterns: + - "github.com/prometheus/*" + - "contrib.go.opencensus.io/*" + - "go.opencensus.io" + uber: + patterns: + - "go.uber.org/*" diff --git a/.github/workflows/dependabot-tidy.yml b/.github/workflows/dependabot-tidy.yml new file mode 100644 index 000000000..49bdbc9f0 --- /dev/null +++ b/.github/workflows/dependabot-tidy.yml @@ -0,0 +1,61 @@ +# Dependabot only updates go.mod/go.sum in the root module, but this repo has +# multiple Go modules (see docs/examples/). This workflow runs `make mod_tidy` +# on Dependabot PRs to keep all go.sum files in sync, preventing go-check CI +# failures. +name: Dependabot Tidy + +on: + pull_request_target: + types: [opened, synchronize] + workflow_dispatch: + inputs: + pr_number: + description: 'PR number to run mod_tidy on' + required: true + type: number + +permissions: + contents: write + pull-requests: write + +jobs: + tidy: + if: github.actor == 'dependabot[bot]' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - name: Get PR info + id: pr + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + pr_number="${{ inputs.pr_number }}" + else + pr_number="${{ github.event.pull_request.number }}" + fi + echo "number=$pr_number" >> $GITHUB_OUTPUT + branch=$(gh pr view "$pr_number" --repo "${{ github.repository }}" --json headRefName -q '.headRefName') + echo "branch=$branch" >> $GITHUB_OUTPUT + - uses: actions/checkout@v4 + with: + ref: ${{ steps.pr.outputs.branch }} + token: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - name: Run make mod_tidy + run: make mod_tidy + - name: Check for changes + id: git-check + run: | + if [[ -n $(git status --porcelain) ]]; then + echo "modified=true" >> $GITHUB_OUTPUT + fi + - name: Commit changes + if: steps.git-check.outputs.modified == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git commit -m "chore: run make mod_tidy" + git push diff --git a/go.mod b/go.mod index 109a2ca99..10124e22e 100644 --- a/go.mod +++ b/go.mod @@ -271,3 +271,10 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.4.1 // indirect ) + +// Exclude ancient +incompatible versions that confuse Dependabot. +// These pre-Go-modules versions reference packages that no longer exist. +exclude ( + github.com/ipfs/go-ipfs-cmds v2.0.1+incompatible + github.com/libp2p/go-libp2p v6.0.23+incompatible +) From b7a35a4bd0041ab019d7705f4cb364690c3219cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 06:34:24 +0100 Subject: [PATCH 34/49] chore(deps): bump actions/setup-go from 5 to 6 (#11165) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5 to 6. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependabot-tidy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot-tidy.yml b/.github/workflows/dependabot-tidy.yml index 49bdbc9f0..91db8518a 100644 --- a/.github/workflows/dependabot-tidy.yml +++ b/.github/workflows/dependabot-tidy.yml @@ -40,7 +40,7 @@ jobs: with: ref: ${{ steps.pr.outputs.branch }} token: ${{ secrets.GITHUB_TOKEN }} - - uses: actions/setup-go@v5 + - uses: actions/setup-go@v6 with: go-version-file: go.mod - name: Run make mod_tidy From 4a80af4b544817117063b48ef3768d928513fbb5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 06:35:20 +0100 Subject: [PATCH 35/49] chore(deps): bump actions/checkout from 4 to 6 (#11166) Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/dependabot-tidy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot-tidy.yml b/.github/workflows/dependabot-tidy.yml index 91db8518a..da435380f 100644 --- a/.github/workflows/dependabot-tidy.yml +++ b/.github/workflows/dependabot-tidy.yml @@ -36,7 +36,7 @@ jobs: echo "number=$pr_number" >> $GITHUB_OUTPUT branch=$(gh pr view "$pr_number" --repo "${{ github.repository }}" --json headRefName -q '.headRefName') echo "branch=$branch" >> $GITHUB_OUTPUT - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: ref: ${{ steps.pr.outputs.branch }} token: ${{ secrets.GITHUB_TOKEN }} From bc9b3886101809e170ea66062f2b7b660424c05e Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 23 Jan 2026 06:41:47 +0100 Subject: [PATCH 36/49] fix(kad-dht): peerstore and exploration loop (#11139) * fix(routing): update kad-dht with peerstore address clone fix closes #11116 See https://github.com/ipfs/kubo/issues/11116 for context of this fix * fix(routing): update kad-dht with CPL exploration fix fixes an infinite loop when all peers share the same CPL during provider exploration See https://github.com/libp2p/go-libp2p-kad-dht/pull/1216 * fix(routing): update kad-dht with shutdown loop check https://github.com/libp2p/go-libp2p-kad-dht/pull/1217 * depend on latest kad-dht fix * bump kad-dht to v0.37.0 --------- Co-authored-by: guillaumemichel Co-authored-by: Guillaume Michel --- docs/examples/kubo-as-a-library/go.mod | 2 +- docs/examples/kubo-as-a-library/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- test/dependencies/go.mod | 2 +- test/dependencies/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 698eccfa3..45b9a9629 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -114,7 +114,7 @@ require ( github.com/libp2p/go-doh-resolver v0.5.0 // indirect github.com/libp2p/go-flow-metrics v0.3.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect - github.com/libp2p/go-libp2p-kad-dht v0.36.0 // indirect + github.com/libp2p/go-libp2p-kad-dht v0.37.0 // indirect github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect github.com/libp2p/go-libp2p-pubsub v0.15.0 // indirect github.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index dba9d67aa..43f8aefa9 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -401,8 +401,8 @@ github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl9 github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= -github.com/libp2p/go-libp2p-kad-dht v0.36.0 h1:7QuXhV36+Vyj+L6A7mrYkn2sYLrbRcbjvsYDu/gXhn8= -github.com/libp2p/go-libp2p-kad-dht v0.36.0/go.mod h1:O24LxTH9Rt3I5XU8nmiA9VynS4TrTwAyj+zBJKB05vQ= +github.com/libp2p/go-libp2p-kad-dht v0.37.0 h1:V1IkFzK9taNS1UNAx260foulcBPH+watAUFjNo2qMUY= +github.com/libp2p/go-libp2p-kad-dht v0.37.0/go.mod h1:o4FPa1ea++UVAMJ1c+kyjUmj3CKm9+ZCyzQb4uutCFM= github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s= github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4= diff --git a/go.mod b/go.mod index 10124e22e..3c9c892a3 100644 --- a/go.mod +++ b/go.mod @@ -52,7 +52,7 @@ require ( github.com/libp2p/go-doh-resolver v0.5.0 github.com/libp2p/go-libp2p v0.46.0 github.com/libp2p/go-libp2p-http v0.5.0 - github.com/libp2p/go-libp2p-kad-dht v0.36.0 + github.com/libp2p/go-libp2p-kad-dht v0.37.0 github.com/libp2p/go-libp2p-kbucket v0.8.0 github.com/libp2p/go-libp2p-pubsub v0.15.0 github.com/libp2p/go-libp2p-pubsub-router v0.6.0 diff --git a/go.sum b/go.sum index afed01f14..896ed6617 100644 --- a/go.sum +++ b/go.sum @@ -490,8 +490,8 @@ github.com/libp2p/go-libp2p-gostream v0.6.0 h1:QfAiWeQRce6pqnYfmIVWJFXNdDyfiR/qk github.com/libp2p/go-libp2p-gostream v0.6.0/go.mod h1:Nywu0gYZwfj7Jc91PQvbGU8dIpqbQQkjWgDuOrFaRdA= github.com/libp2p/go-libp2p-http v0.5.0 h1:+x0AbLaUuLBArHubbbNRTsgWz0RjNTy6DJLOxQ3/QBc= github.com/libp2p/go-libp2p-http v0.5.0/go.mod h1:glh87nZ35XCQyFsdzZps6+F4HYI6DctVFY5u1fehwSg= -github.com/libp2p/go-libp2p-kad-dht v0.36.0 h1:7QuXhV36+Vyj+L6A7mrYkn2sYLrbRcbjvsYDu/gXhn8= -github.com/libp2p/go-libp2p-kad-dht v0.36.0/go.mod h1:O24LxTH9Rt3I5XU8nmiA9VynS4TrTwAyj+zBJKB05vQ= +github.com/libp2p/go-libp2p-kad-dht v0.37.0 h1:V1IkFzK9taNS1UNAx260foulcBPH+watAUFjNo2qMUY= +github.com/libp2p/go-libp2p-kad-dht v0.37.0/go.mod h1:o4FPa1ea++UVAMJ1c+kyjUmj3CKm9+ZCyzQb4uutCFM= github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s= github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4= diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index 98bf7ebf1..5ae573c55 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -183,7 +183,7 @@ require ( github.com/libp2p/go-flow-metrics v0.3.0 // indirect github.com/libp2p/go-libp2p v0.46.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect - github.com/libp2p/go-libp2p-kad-dht v0.36.0 // indirect + github.com/libp2p/go-libp2p-kad-dht v0.37.0 // indirect github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect github.com/libp2p/go-libp2p-record v0.3.1 // indirect github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index 32d404247..d2b06032c 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -419,8 +419,8 @@ github.com/libp2p/go-libp2p v0.46.0 h1:0T2yvIKpZ3DVYCuPOFxPD1layhRU486pj9rSlGWYn github.com/libp2p/go-libp2p v0.46.0/go.mod h1:TbIDnpDjBLa7isdgYpbxozIVPBTmM/7qKOJP4SFySrQ= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= -github.com/libp2p/go-libp2p-kad-dht v0.36.0 h1:7QuXhV36+Vyj+L6A7mrYkn2sYLrbRcbjvsYDu/gXhn8= -github.com/libp2p/go-libp2p-kad-dht v0.36.0/go.mod h1:O24LxTH9Rt3I5XU8nmiA9VynS4TrTwAyj+zBJKB05vQ= +github.com/libp2p/go-libp2p-kad-dht v0.37.0 h1:V1IkFzK9taNS1UNAx260foulcBPH+watAUFjNo2qMUY= +github.com/libp2p/go-libp2p-kad-dht v0.37.0/go.mod h1:o4FPa1ea++UVAMJ1c+kyjUmj3CKm9+ZCyzQb4uutCFM= github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s= github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4= github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg= From f6a5c5f5c54b9d05680fd802b89bdc9885511a6c Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Thu, 22 Jan 2026 23:38:59 -0800 Subject: [PATCH 37/49] chore: upgrade go-ds-pebble to v0.5.9 (#11170) - upgrade go-ds-pebble to [v0.5.9](https://github.com/ipfs/go-ds-pebble/releases/tag/v0.5.9) - git ignore ipfswatch binary --- cmd/ipfswatch/.gitignore | 3 +++ docs/changelogs/v0.40.md | 4 ++-- docs/examples/kubo-as-a-library/go.mod | 6 +++--- docs/examples/kubo-as-a-library/go.sum | 12 ++++++------ go.mod | 6 +++--- go.sum | 12 ++++++------ test/dependencies/go.mod | 4 ++-- test/dependencies/go.sum | 8 ++++---- 8 files changed, 29 insertions(+), 26 deletions(-) create mode 100644 cmd/ipfswatch/.gitignore diff --git a/cmd/ipfswatch/.gitignore b/cmd/ipfswatch/.gitignore new file mode 100644 index 000000000..620dcc97f --- /dev/null +++ b/cmd/ipfswatch/.gitignore @@ -0,0 +1,3 @@ +ipfswatch +ipfswatch-test-cover +ipfswatch.exe diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index 524852a30..ca7fede84 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -151,8 +151,8 @@ The `ipfs ls` command now supports `--long` (`-l`) flag for displaying Unix-styl - Fixed mDNS discovery on Windows and macOS by filtering addresses to reduce packet size ([go-libp2p#3434](https://github.com/libp2p/go-libp2p/pull/3434)). - update `quic-go` to [v0.57.1](https://github.com/quic-go/quic-go/releases/tag/v0.57.1) (incl. [v0.56.0](https://github.com/quic-go/quic-go/releases/tag/v0.56.0) + [v0.57.0](https://github.com/quic-go/quic-go/releases/tag/v0.57.0)) - update `p2p-forge` to [v0.7.0](https://github.com/ipshipyard/p2p-forge/releases/tag/v0.7.0) -- update `go-ds-pebble` to [v0.5.8](https://github.com/ipfs/go-ds-pebble/releases/tag/v0.5.8) - - updates `github.com/cockroachdb/pebble` to [v2.1.3](https://github.com/cockroachdb/pebble/releases/tag/v2.1.3) to enable Go 1.26 support +- update `go-ds-pebble` to [v0.5.9](https://github.com/ipfs/go-ds-pebble/releases/tag/v0.5.9) + - updates `github.com/cockroachdb/pebble` to [v2.1.4](https://github.com/cockroachdb/pebble/releases/tag/v2.1.4) to enable Go 1.26 support - update `go-libp2p-pubsub` to [v0.15.0](https://github.com/libp2p/go-libp2p-pubsub/releases/tag/v0.15.0) ### ๐Ÿ“ Changelog diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 45b9a9629..80682672a 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -18,7 +18,7 @@ require ( github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/DataDog/zstd v1.5.7 // indirect github.com/Jorropo/jsync v1.0.1 // indirect - github.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657 // indirect + github.com/RaduBerinde/axisds v0.1.0 // indirect github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54 // indirect github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect @@ -34,7 +34,7 @@ require ( github.com/cockroachdb/crlib v0.0.0-20241112164430-1264a2edc35b // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble/v2 v2.1.3 // indirect + github.com/cockroachdb/pebble/v2 v2.1.4 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect @@ -81,7 +81,7 @@ require ( github.com/ipfs/go-ds-flatfs v0.6.0 // indirect github.com/ipfs/go-ds-leveldb v0.5.2 // indirect github.com/ipfs/go-ds-measure v0.2.2 // indirect - github.com/ipfs/go-ds-pebble v0.5.8 // indirect + github.com/ipfs/go-ds-pebble v0.5.9 // indirect github.com/ipfs/go-dsqueue v0.1.1 // indirect github.com/ipfs/go-fs-lock v0.1.1 // indirect github.com/ipfs/go-ipfs-cmds v0.15.0 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 43f8aefa9..c9c490305 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -27,8 +27,8 @@ github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwS github.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU= github.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657 h1:8XBWWQD+vFF+JqOsm16t0Kab1a7YWV8+GISVEP8AuZ8= -github.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657/go.mod h1:UHGJonU9z4YYGKJxSaC6/TNcLOBptpmM5m2Cksbnw0Y= +github.com/RaduBerinde/axisds v0.1.0 h1:YItk/RmU5nvlsv/awo2Fjx97Mfpt4JfgtEVAGPrLdz8= +github.com/RaduBerinde/axisds v0.1.0/go.mod h1:UHGJonU9z4YYGKJxSaC6/TNcLOBptpmM5m2Cksbnw0Y= github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54 h1:bsU8Tzxr/PNz75ayvCnxKZWEYdLMPDkUgticP4a4Bvk= github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54/go.mod h1:0tr7FllbE9gJkHq7CVeeDDFAFKQVy5RnCSSNBOvdqbc= github.com/aclements/go-perfevent v0.0.0-20240301234650-f7843625020f h1:JjxwchlOepwsUWcQwD2mLUAGE9aCp0/ehy6yCHFBOvo= @@ -84,8 +84,8 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895 h1:XANOgPYtvELQ/h4IrmPAohXqe2pWA8Bwhejr3VQoZsA= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895/go.mod h1:aPd7gM9ov9M8v32Yy5NJrDyOcD8z642dqs+F0CeNXfA= -github.com/cockroachdb/pebble/v2 v2.1.3 h1:irU503OnjRoJBrkZQIJvwv9c4WvpUeOJxhRApojB8D8= -github.com/cockroachdb/pebble/v2 v2.1.3/go.mod h1:B1UgWsyR+L+UvZXNgpxw+WqsUKA8VQ/bb//FXOHghB8= +github.com/cockroachdb/pebble/v2 v2.1.4 h1:j9wPgMDbkErFdAKYFGhsoCcvzcjR+6zrJ4jhKtJ6bOk= +github.com/cockroachdb/pebble/v2 v2.1.4/go.mod h1:Reo1RTniv1UjVTAu/Fv74y5i3kJ5gmVrPhO9UtFiKn8= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b h1:VXvSNzmr8hMj8XTuY0PT9Ane9qZGul/p67vGYwl9BFI= @@ -295,8 +295,8 @@ github.com/ipfs/go-ds-leveldb v0.5.2 h1:6nmxlQ2zbp4LCNdJVsmHfs9GP0eylfBNxpmY1csp github.com/ipfs/go-ds-leveldb v0.5.2/go.mod h1:2fAwmcvD3WoRT72PzEekHBkQmBDhc39DJGoREiuGmYo= github.com/ipfs/go-ds-measure v0.2.2 h1:4kwvBGbbSXNYe4ANlg7qTIYoZU6mNlqzQHdVqICkqGI= github.com/ipfs/go-ds-measure v0.2.2/go.mod h1:b/87ak0jMgH9Ylt7oH0+XGy4P8jHx9KG09Qz+pOeTIs= -github.com/ipfs/go-ds-pebble v0.5.8 h1:NbAfKQo+m39Nka6gt8PARAyH+VoHtRInB6CFCmT+wqo= -github.com/ipfs/go-ds-pebble v0.5.8/go.mod h1:AJjJTHgads/Fn5+tuWmaDGjGEbks7Wgx82NQ/pwmEhc= +github.com/ipfs/go-ds-pebble v0.5.9 h1:D1FEuMxjbEmDADNqsyT74n9QHVAn12nv9i9Qa15AFYc= +github.com/ipfs/go-ds-pebble v0.5.9/go.mod h1:XmUBN05l6B+tMg7mpMS75ZcKW/CX01uZMhhWw85imQA= github.com/ipfs/go-dsqueue v0.1.1 h1:6PQlHDyf9PSTN69NmwUir5+0is3tU0vRJj8zLlgK8Mc= github.com/ipfs/go-dsqueue v0.1.1/go.mod h1:Xxg353WSwwzYn3FGSzZ+taSQII3pIZ+EJC8/oWRDM10= github.com/ipfs/go-fs-lock v0.1.1 h1:TecsP/Uc7WqYYatasreZQiP9EGRy4ZnKoG4yXxR33nw= diff --git a/go.mod b/go.mod index 3c9c892a3..6270a4802 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/cenkalti/backoff/v4 v4.3.0 github.com/ceramicnetwork/go-dag-jose v0.1.1 github.com/cheggaaa/pb v1.0.29 - github.com/cockroachdb/pebble/v2 v2.1.3 + github.com/cockroachdb/pebble/v2 v2.1.4 github.com/coreos/go-systemd/v22 v22.5.0 github.com/dustin/go-humanize v1.0.1 github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 @@ -31,7 +31,7 @@ require ( github.com/ipfs/go-ds-flatfs v0.6.0 github.com/ipfs/go-ds-leveldb v0.5.2 github.com/ipfs/go-ds-measure v0.2.2 - github.com/ipfs/go-ds-pebble v0.5.8 + github.com/ipfs/go-ds-pebble v0.5.9 github.com/ipfs/go-fs-lock v0.1.1 github.com/ipfs/go-ipfs-cmds v0.15.0 github.com/ipfs/go-ipld-cbor v0.2.1 @@ -99,7 +99,7 @@ require ( github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/DataDog/zstd v1.5.7 // indirect github.com/Jorropo/jsync v1.0.1 // indirect - github.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657 // indirect + github.com/RaduBerinde/axisds v0.1.0 // indirect github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54 // indirect github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect github.com/alexbrainman/goissue34681 v0.0.0-20191006012335-3fc7a47baff5 // indirect diff --git a/go.sum b/go.sum index 896ed6617..cba1d9ac9 100644 --- a/go.sum +++ b/go.sum @@ -46,8 +46,8 @@ github.com/Jorropo/jsync v1.0.1 h1:6HgRolFZnsdfzRUj+ImB9og1JYOxQoReSywkHOGSaUU= github.com/Jorropo/jsync v1.0.1/go.mod h1:jCOZj3vrBCri3bSU3ErUYvevKlnbssrXeCivybS5ABQ= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657 h1:8XBWWQD+vFF+JqOsm16t0Kab1a7YWV8+GISVEP8AuZ8= -github.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657/go.mod h1:UHGJonU9z4YYGKJxSaC6/TNcLOBptpmM5m2Cksbnw0Y= +github.com/RaduBerinde/axisds v0.1.0 h1:YItk/RmU5nvlsv/awo2Fjx97Mfpt4JfgtEVAGPrLdz8= +github.com/RaduBerinde/axisds v0.1.0/go.mod h1:UHGJonU9z4YYGKJxSaC6/TNcLOBptpmM5m2Cksbnw0Y= github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54 h1:bsU8Tzxr/PNz75ayvCnxKZWEYdLMPDkUgticP4a4Bvk= github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54/go.mod h1:0tr7FllbE9gJkHq7CVeeDDFAFKQVy5RnCSSNBOvdqbc= github.com/aclements/go-perfevent v0.0.0-20240301234650-f7843625020f h1:JjxwchlOepwsUWcQwD2mLUAGE9aCp0/ehy6yCHFBOvo= @@ -117,8 +117,8 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895 h1:XANOgPYtvELQ/h4IrmPAohXqe2pWA8Bwhejr3VQoZsA= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895/go.mod h1:aPd7gM9ov9M8v32Yy5NJrDyOcD8z642dqs+F0CeNXfA= -github.com/cockroachdb/pebble/v2 v2.1.3 h1:irU503OnjRoJBrkZQIJvwv9c4WvpUeOJxhRApojB8D8= -github.com/cockroachdb/pebble/v2 v2.1.3/go.mod h1:B1UgWsyR+L+UvZXNgpxw+WqsUKA8VQ/bb//FXOHghB8= +github.com/cockroachdb/pebble/v2 v2.1.4 h1:j9wPgMDbkErFdAKYFGhsoCcvzcjR+6zrJ4jhKtJ6bOk= +github.com/cockroachdb/pebble/v2 v2.1.4/go.mod h1:Reo1RTniv1UjVTAu/Fv74y5i3kJ5gmVrPhO9UtFiKn8= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b h1:VXvSNzmr8hMj8XTuY0PT9Ane9qZGul/p67vGYwl9BFI= @@ -366,8 +366,8 @@ github.com/ipfs/go-ds-leveldb v0.5.2 h1:6nmxlQ2zbp4LCNdJVsmHfs9GP0eylfBNxpmY1csp github.com/ipfs/go-ds-leveldb v0.5.2/go.mod h1:2fAwmcvD3WoRT72PzEekHBkQmBDhc39DJGoREiuGmYo= github.com/ipfs/go-ds-measure v0.2.2 h1:4kwvBGbbSXNYe4ANlg7qTIYoZU6mNlqzQHdVqICkqGI= github.com/ipfs/go-ds-measure v0.2.2/go.mod h1:b/87ak0jMgH9Ylt7oH0+XGy4P8jHx9KG09Qz+pOeTIs= -github.com/ipfs/go-ds-pebble v0.5.8 h1:NbAfKQo+m39Nka6gt8PARAyH+VoHtRInB6CFCmT+wqo= -github.com/ipfs/go-ds-pebble v0.5.8/go.mod h1:AJjJTHgads/Fn5+tuWmaDGjGEbks7Wgx82NQ/pwmEhc= +github.com/ipfs/go-ds-pebble v0.5.9 h1:D1FEuMxjbEmDADNqsyT74n9QHVAn12nv9i9Qa15AFYc= +github.com/ipfs/go-ds-pebble v0.5.9/go.mod h1:XmUBN05l6B+tMg7mpMS75ZcKW/CX01uZMhhWw85imQA= github.com/ipfs/go-dsqueue v0.1.1 h1:6PQlHDyf9PSTN69NmwUir5+0is3tU0vRJj8zLlgK8Mc= github.com/ipfs/go-dsqueue v0.1.1/go.mod h1:Xxg353WSwwzYn3FGSzZ+taSQII3pIZ+EJC8/oWRDM10= github.com/ipfs/go-fs-lock v0.1.1 h1:TecsP/Uc7WqYYatasreZQiP9EGRy4ZnKoG4yXxR33nw= diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index 5ae573c55..e58fd174d 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -34,7 +34,7 @@ require ( github.com/Jorropo/jsync v1.0.1 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect - github.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657 // indirect + github.com/RaduBerinde/axisds v0.1.0 // indirect github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54 // indirect github.com/alecthomas/go-check-sumtype v0.3.1 // indirect github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect @@ -65,7 +65,7 @@ require ( github.com/cockroachdb/crlib v0.0.0-20241112164430-1264a2edc35b // indirect github.com/cockroachdb/errors v1.11.3 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect - github.com/cockroachdb/pebble/v2 v2.1.3 // indirect + github.com/cockroachdb/pebble/v2 v2.1.4 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index d2b06032c..f0d1bf408 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -32,8 +32,8 @@ github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+ github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4= github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= -github.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657 h1:8XBWWQD+vFF+JqOsm16t0Kab1a7YWV8+GISVEP8AuZ8= -github.com/RaduBerinde/axisds v0.0.0-20250419182453-5135a0650657/go.mod h1:UHGJonU9z4YYGKJxSaC6/TNcLOBptpmM5m2Cksbnw0Y= +github.com/RaduBerinde/axisds v0.1.0 h1:YItk/RmU5nvlsv/awo2Fjx97Mfpt4JfgtEVAGPrLdz8= +github.com/RaduBerinde/axisds v0.1.0/go.mod h1:UHGJonU9z4YYGKJxSaC6/TNcLOBptpmM5m2Cksbnw0Y= github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54 h1:bsU8Tzxr/PNz75ayvCnxKZWEYdLMPDkUgticP4a4Bvk= github.com/RaduBerinde/btreemap v0.0.0-20250419174037-3d62b7205d54/go.mod h1:0tr7FllbE9gJkHq7CVeeDDFAFKQVy5RnCSSNBOvdqbc= github.com/aclements/go-perfevent v0.0.0-20240301234650-f7843625020f h1:JjxwchlOepwsUWcQwD2mLUAGE9aCp0/ehy6yCHFBOvo= @@ -104,8 +104,8 @@ github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZe github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895 h1:XANOgPYtvELQ/h4IrmPAohXqe2pWA8Bwhejr3VQoZsA= github.com/cockroachdb/metamorphic v0.0.0-20231108215700-4ba948b56895/go.mod h1:aPd7gM9ov9M8v32Yy5NJrDyOcD8z642dqs+F0CeNXfA= -github.com/cockroachdb/pebble/v2 v2.1.3 h1:irU503OnjRoJBrkZQIJvwv9c4WvpUeOJxhRApojB8D8= -github.com/cockroachdb/pebble/v2 v2.1.3/go.mod h1:B1UgWsyR+L+UvZXNgpxw+WqsUKA8VQ/bb//FXOHghB8= +github.com/cockroachdb/pebble/v2 v2.1.4 h1:j9wPgMDbkErFdAKYFGhsoCcvzcjR+6zrJ4jhKtJ6bOk= +github.com/cockroachdb/pebble/v2 v2.1.4/go.mod h1:Reo1RTniv1UjVTAu/Fv74y5i3kJ5gmVrPhO9UtFiKn8= github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= github.com/cockroachdb/swiss v0.0.0-20251224182025-b0f6560f979b h1:VXvSNzmr8hMj8XTuY0PT9Ane9qZGul/p67vGYwl9BFI= From 5ccdcdd4fca6654374e28d4ef4a16746c67e1938 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Sat, 24 Jan 2026 01:32:56 +0100 Subject: [PATCH 38/49] chore(deps): ignore datastore backends in dependabot (#11169) pebble, leveldb, and badger should be updated via go-ds-* wrappers to ensure compatibility --- .github/dependabot.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 4f285be44..cde712383 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,6 +13,11 @@ updates: open-pull-requests-limit: 10 labels: - "dependencies" + ignore: + # Updated via go-ds-* wrappers in ipfs-ecosystem group + - dependency-name: "github.com/cockroachdb/pebble*" + - dependency-name: "github.com/syndtr/goleveldb" + - dependency-name: "github.com/dgraph-io/badger*" groups: ipfs-ecosystem: patterns: From a13740127224eb77a46d83ede9e5cfcb8f7ba9de Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Tue, 27 Jan 2026 15:51:01 -0800 Subject: [PATCH 39/49] Upgrade to Boxo v0.36.0 (#11175) * Upgrade to Boxo v0.36.0 * sharness: add missing metrics --- docs/changelogs/v0.40.md | 4 +- docs/examples/kubo-as-a-library/go.mod | 35 +++++---- docs/examples/kubo-as-a-library/go.sum | 74 +++++++++--------- go.mod | 37 ++++----- go.sum | 78 ++++++++++--------- test/dependencies/go.mod | 28 +++---- test/dependencies/go.sum | 74 +++++++++--------- ...prometheus_metrics_added_by_enabling_rcmgr | 1 + ...rometheus_metrics_added_by_measure_profile | 1 + 9 files changed, 172 insertions(+), 160 deletions(-) diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index ca7fede84..fd8c41b5b 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -149,11 +149,13 @@ The `ipfs ls` command now supports `--long` (`-l`) flag for displaying Unix-styl - update `go-libp2p` to [v0.46.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.46.0) - Reduced WebRTC log noise by using debug level for pion errors ([go-libp2p#3426](https://github.com/libp2p/go-libp2p/pull/3426)). - Fixed mDNS discovery on Windows and macOS by filtering addresses to reduce packet size ([go-libp2p#3434](https://github.com/libp2p/go-libp2p/pull/3434)). -- update `quic-go` to [v0.57.1](https://github.com/quic-go/quic-go/releases/tag/v0.57.1) (incl. [v0.56.0](https://github.com/quic-go/quic-go/releases/tag/v0.56.0) + [v0.57.0](https://github.com/quic-go/quic-go/releases/tag/v0.57.0)) +- update `quic-go` to [v0.59.0](https://github.com/quic-go/quic-go/releases/tag/v0.59.0) (incl. [v0.58.0](https://github.com/quic-go/quic-go/releases/tag/v0.58.0) + [v0.57.0](https://github.com/quic-go/quic-go/releases/tag/v0.57.0)) - update `p2p-forge` to [v0.7.0](https://github.com/ipshipyard/p2p-forge/releases/tag/v0.7.0) - update `go-ds-pebble` to [v0.5.9](https://github.com/ipfs/go-ds-pebble/releases/tag/v0.5.9) - updates `github.com/cockroachdb/pebble` to [v2.1.4](https://github.com/cockroachdb/pebble/releases/tag/v2.1.4) to enable Go 1.26 support - update `go-libp2p-pubsub` to [v0.15.0](https://github.com/libp2p/go-libp2p-pubsub/releases/tag/v0.15.0) +- update `boxo` to [v0.36.0](https://github.com/ipfs/boxo/releases/tag/v0.36.0) +- update `go-libp2p-kad-dht` to [v0.37.1](https://github.com/libp2p/go-libp2p-kad-dht/releases/tag/v0.37.1) (includes [v0.37.0](https://github.com/libp2p/go-libp2p-kad-dht/releases/tag/v0.37.0)) ### ๐Ÿ“ Changelog diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 80682672a..c109c37f6 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -7,9 +7,9 @@ go 1.25 replace github.com/ipfs/kubo => ./../../.. require ( - github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 + github.com/ipfs/boxo v0.36.0 github.com/ipfs/kubo v0.0.0-00010101000000-000000000000 - github.com/libp2p/go-libp2p v0.46.0 + github.com/libp2p/go-libp2p v0.47.0 github.com/multiformats/go-multiaddr v0.16.1 ) @@ -44,13 +44,14 @@ require ( github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/dgraph-io/badger v1.6.2 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect + github.com/dunglas/httpsfv v1.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/filecoin-project/go-clock v0.1.0 // indirect github.com/flynn/noise v1.1.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.10 // indirect + github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/gammazero/chanqueue v1.1.1 // indirect github.com/gammazero/deque v1.2.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect @@ -82,19 +83,19 @@ require ( github.com/ipfs/go-ds-leveldb v0.5.2 // indirect github.com/ipfs/go-ds-measure v0.2.2 // indirect github.com/ipfs/go-ds-pebble v0.5.9 // indirect - github.com/ipfs/go-dsqueue v0.1.1 // indirect + github.com/ipfs/go-dsqueue v0.1.2 // indirect github.com/ipfs/go-fs-lock v0.1.1 // indirect github.com/ipfs/go-ipfs-cmds v0.15.0 // indirect github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect - github.com/ipfs/go-ipfs-pq v0.0.3 // indirect + github.com/ipfs/go-ipfs-pq v0.0.4 // indirect github.com/ipfs/go-ipfs-redirects-file v0.1.2 // indirect github.com/ipfs/go-ipld-cbor v0.2.1 // indirect github.com/ipfs/go-ipld-format v0.6.3 // indirect github.com/ipfs/go-ipld-git v0.1.1 // indirect github.com/ipfs/go-ipld-legacy v0.2.2 // indirect - github.com/ipfs/go-log/v2 v2.9.0 // indirect + github.com/ipfs/go-log/v2 v2.9.1 // indirect github.com/ipfs/go-metrics-interface v0.3.0 // indirect - github.com/ipfs/go-peertaskqueue v0.8.2 // indirect + github.com/ipfs/go-peertaskqueue v0.8.3 // indirect github.com/ipfs/go-test v0.2.3 // indirect github.com/ipfs/go-unixfsnode v1.10.2 // indirect github.com/ipld/go-car/v2 v2.16.0 // indirect @@ -114,7 +115,7 @@ require ( github.com/libp2p/go-doh-resolver v0.5.0 // indirect github.com/libp2p/go-flow-metrics v0.3.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect - github.com/libp2p/go-libp2p-kad-dht v0.37.0 // indirect + github.com/libp2p/go-libp2p-kad-dht v0.37.1 // indirect github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect github.com/libp2p/go-libp2p-pubsub v0.15.0 // indirect github.com/libp2p/go-libp2p-pubsub-router v0.6.0 // indirect @@ -122,14 +123,14 @@ require ( github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect github.com/libp2p/go-libp2p-xor v0.1.0 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect - github.com/libp2p/go-netroute v0.3.0 // indirect + github.com/libp2p/go-netroute v0.4.0 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v5 v5.0.1 // indirect github.com/libp2p/zeroconf/v2 v2.2.0 // indirect github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mholt/acmez/v3 v3.1.2 // indirect - github.com/miekg/dns v1.1.68 // indirect + github.com/miekg/dns v1.1.72 // indirect github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/minlz v1.0.1-0.20250507153514-87eb42fe8882 // indirect @@ -176,8 +177,8 @@ require ( github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.17.0 // indirect github.com/quic-go/qpack v0.6.0 // indirect - github.com/quic-go/quic-go v0.57.1 // indirect - github.com/quic-go/webtransport-go v0.9.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect + github.com/quic-go/webtransport-go v0.10.0 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect @@ -193,21 +194,21 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 // indirect go.opentelemetry.io/otel/exporters/zipkin v1.38.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect go.opentelemetry.io/otel/sdk v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/fx v1.24.0 // indirect go.uber.org/mock v0.5.2 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.uber.org/zap/exp v0.3.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect @@ -222,7 +223,7 @@ require ( golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.41.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - gonum.org/v1/gonum v0.16.0 // indirect + gonum.org/v1/gonum v0.17.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/grpc v1.75.0 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index c9c490305..1a50e42b4 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -123,6 +123,8 @@ github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkz github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dunglas/httpsfv v1.1.0 h1:Jw76nAyKWKZKFrpMMcL76y35tOpYHqQPzHQiwDvpe54= +github.com/dunglas/httpsfv v1.1.0/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= @@ -146,8 +148,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= -github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= +github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gammazero/chanqueue v1.1.1 h1:n9Y+zbBxw2f7uUE9wpgs0rOSkP/I/yhDLiNuhyVjojQ= github.com/gammazero/chanqueue v1.1.1/go.mod h1:fMwpwEiuUgpab0sH4VHiVcEoji1pSi+EIzeG4TPeKPc= github.com/gammazero/deque v1.2.0 h1:scEFO8Uidhw6KDU5qg1HA5fYwM0+us2qdeJqm43bitU= @@ -265,8 +267,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 h1:OqNqsGZPX8zh3eFMO8Lf8EHRRnSGBMqcd github.com/ipfs-shipyard/nopfs/ipfs v0.25.0/go.mod h1:BxhUdtBgOXg1B+gAPEplkg/GpyTZY+kCMSfsJvvydqU= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 h1:pRQYSSGnGQa921d8v0uhXg2BGzoSf9ndTWTlR7ImVoo= -github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2/go.mod h1:Abmp1if6bMQG87/0SQPIB9fkxJnZMLCt2nQw3yUZHH0= +github.com/ipfs/boxo v0.36.0 h1:DarrMBM46xCs6GU6Vz+AL8VUyXykqHAqZYx8mR0Oics= +github.com/ipfs/boxo v0.36.0/go.mod h1:92hnRXfP5ScKEIqlq9Ns7LR1dFXEVADKWVGH0fjk83k= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= @@ -297,8 +299,8 @@ github.com/ipfs/go-ds-measure v0.2.2 h1:4kwvBGbbSXNYe4ANlg7qTIYoZU6mNlqzQHdVqICk github.com/ipfs/go-ds-measure v0.2.2/go.mod h1:b/87ak0jMgH9Ylt7oH0+XGy4P8jHx9KG09Qz+pOeTIs= github.com/ipfs/go-ds-pebble v0.5.9 h1:D1FEuMxjbEmDADNqsyT74n9QHVAn12nv9i9Qa15AFYc= github.com/ipfs/go-ds-pebble v0.5.9/go.mod h1:XmUBN05l6B+tMg7mpMS75ZcKW/CX01uZMhhWw85imQA= -github.com/ipfs/go-dsqueue v0.1.1 h1:6PQlHDyf9PSTN69NmwUir5+0is3tU0vRJj8zLlgK8Mc= -github.com/ipfs/go-dsqueue v0.1.1/go.mod h1:Xxg353WSwwzYn3FGSzZ+taSQII3pIZ+EJC8/oWRDM10= +github.com/ipfs/go-dsqueue v0.1.2 h1:jBMsgvT9Pj9l3cqI0m5jYpW/aWDYkW4Us6EuzrcSGbs= +github.com/ipfs/go-dsqueue v0.1.2/go.mod h1:OU94YuMVUIF/ctR7Ysov9PI4gOa2XjPGN9nd8imSv78= github.com/ipfs/go-fs-lock v0.1.1 h1:TecsP/Uc7WqYYatasreZQiP9EGRy4ZnKoG4yXxR33nw= github.com/ipfs/go-fs-lock v0.1.1/go.mod h1:2goSXMCw7QfscHmSe09oXiR34DQeUdm+ei+dhonqly0= github.com/ipfs/go-ipfs-cmds v0.15.0 h1:nQDgKadrzyiFyYoZMARMIoVoSwe3gGTAfGvrWLeAQbQ= @@ -308,8 +310,8 @@ github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1I github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw= github.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo= -github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE= -github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4= +github.com/ipfs/go-ipfs-pq v0.0.4 h1:U7jjENWJd1jhcrR8X/xHTaph14PTAK9O+yaLJbjqgOw= +github.com/ipfs/go-ipfs-pq v0.0.4/go.mod h1:9UdLOIIb99IFrgT0Fc53pvbvlJBhpUb4GJuAQf3+O2A= github.com/ipfs/go-ipfs-redirects-file v0.1.2 h1:QCK7VtL91FH17KROVVy5KrzDx2hu68QvB2FTWk08ZQk= github.com/ipfs/go-ipfs-redirects-file v0.1.2/go.mod h1:yIiTlLcDEM/8lS6T3FlCEXZktPPqSOyuY6dEzVqw7Fw= github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= @@ -323,12 +325,12 @@ github.com/ipfs/go-ipld-git v0.1.1/go.mod h1:+VyMqF5lMcJh4rwEppV0e6g4nCCHXThLYYD github.com/ipfs/go-ipld-legacy v0.2.2 h1:DThbqCPVLpWBcGtU23KDLiY2YRZZnTkXQyfz8aOfBkQ= github.com/ipfs/go-ipld-legacy v0.2.2/go.mod h1:hhkj+b3kG9b2BcUNw8IFYAsfeNo8E3U7eYlWeAOPyDU= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log/v2 v2.9.0 h1:l4b06AwVXwldIzbVPZy5z7sKp9lHFTX0KWfTBCtHaOk= -github.com/ipfs/go-log/v2 v2.9.0/go.mod h1:UhIYAwMV7Nb4ZmihUxfIRM2Istw/y9cAk3xaK+4Zs2c= +github.com/ipfs/go-log/v2 v2.9.1 h1:3JXwHWU31dsCpvQ+7asz6/QsFJHqFr4gLgQ0FWteujk= +github.com/ipfs/go-log/v2 v2.9.1/go.mod h1:evFx7sBiohUN3AG12mXlZBw5hacBQld3ZPHrowlJYoo= github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU= github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY= -github.com/ipfs/go-peertaskqueue v0.8.2 h1:PaHFRaVFdxQk1Qo3OKiHPYjmmusQy7gKQUaL8JDszAU= -github.com/ipfs/go-peertaskqueue v0.8.2/go.mod h1:L6QPvou0346c2qPJNiJa6BvOibxDfaiPlqHInmzg0FA= +github.com/ipfs/go-peertaskqueue v0.8.3 h1:tBPpGJy+A92RqtRFq5amJn0Uuj8Pw8tXi0X3eHfHM8w= +github.com/ipfs/go-peertaskqueue v0.8.3/go.mod h1:OqVync4kPOcXEGdj/LKvox9DCB5mkSBeXsPczCxLtYA= github.com/ipfs/go-test v0.2.3 h1:Z/jXNAReQFtCYyn7bsv/ZqUwS6E7iIcSpJ2CuzCvnrc= github.com/ipfs/go-test v0.2.3/go.mod h1:QW8vSKkwYvWFwIZQLGQXdkt9Ud76eQXRQ9Ao2H+cA1o= github.com/ipfs/go-unixfsnode v1.10.2 h1:TREegX1J4X+k1w4AhoDuxxFvVcS9SegMRvrmxF6Tca8= @@ -395,14 +397,14 @@ github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZ github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784= github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo= -github.com/libp2p/go-libp2p v0.46.0 h1:0T2yvIKpZ3DVYCuPOFxPD1layhRU486pj9rSlGWYnDM= -github.com/libp2p/go-libp2p v0.46.0/go.mod h1:TbIDnpDjBLa7isdgYpbxozIVPBTmM/7qKOJP4SFySrQ= +github.com/libp2p/go-libp2p v0.47.0 h1:qQpBjSCWNQFF0hjBbKirMXE9RHLtSuzTDkTfr1rw0yc= +github.com/libp2p/go-libp2p v0.47.0/go.mod h1:s8HPh7mMV933OtXzONaGFseCg/BE//m1V34p3x4EUOY= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= github.com/libp2p/go-libp2p-core v0.3.0/go.mod h1:ACp3DmS3/N64c2jDzcV429ukDpicbL6+TrrxANBjPGw= -github.com/libp2p/go-libp2p-kad-dht v0.37.0 h1:V1IkFzK9taNS1UNAx260foulcBPH+watAUFjNo2qMUY= -github.com/libp2p/go-libp2p-kad-dht v0.37.0/go.mod h1:o4FPa1ea++UVAMJ1c+kyjUmj3CKm9+ZCyzQb4uutCFM= +github.com/libp2p/go-libp2p-kad-dht v0.37.1 h1:jtX8bQIXVCs6/allskNB4m5n95Xvwav7wHAhopGZfS0= +github.com/libp2p/go-libp2p-kad-dht v0.37.1/go.mod h1:Uwokdh232k9Y1uMy2yJOK5zb7hpMHn4P8uWS4s9i05Q= github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s= github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4= @@ -422,8 +424,8 @@ github.com/libp2p/go-libp2p-xor v0.1.0/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQ github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= -github.com/libp2p/go-netroute v0.3.0 h1:nqPCXHmeNmgTJnktosJ/sIef9hvwYCrsLxXmfNks/oc= -github.com/libp2p/go-netroute v0.3.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= +github.com/libp2p/go-netroute v0.4.0 h1:sZZx9hyANYUx9PZyqcgE/E1GUG3iEtTZHUEvdtXT7/Q= +github.com/libp2p/go-netroute v0.4.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= @@ -434,8 +436,8 @@ github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxfg= -github.com/marcopolo/simnet v0.0.1/go.mod h1:WDaQkgLAjqDUEBAOXz22+1j6wXKfGlC5sD5XWt3ddOs= +github.com/marcopolo/simnet v0.0.4 h1:50Kx4hS9kFGSRIbrt9xUS3NJX33EyPqHVmpXvaKLqrY= +github.com/marcopolo/simnet v0.0.4/go.mod h1:tfQF1u2DmaB6WHODMtQaLtClEf3a296CKQLq5gAsIS0= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -448,8 +450,8 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -605,10 +607,10 @@ github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7D github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= -github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= -github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= -github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/quic-go/webtransport-go v0.10.0 h1:LqXXPOXuETY5Xe8ITdGisBzTYmUOy5eSj+9n4hLTjHI= +github.com/quic-go/webtransport-go v0.10.0/go.mod h1:LeGIXr5BQKE3UsynwVBeQrU1TPrbh73MGoC6jd+V7ow= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -710,8 +712,8 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= @@ -722,14 +724,14 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE= go.opentelemetry.io/otel/exporters/zipkin v1.38.0 h1:0rJ2TmzpHDG+Ib9gPmu3J3cE0zXirumQcKS4wCoZUa0= go.opentelemetry.io/otel/exporters/zipkin v1.38.0/go.mod h1:Su/nq/K5zRjDKKC3Il0xbViE3juWgG3JDoqLumFx5G0= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= @@ -742,8 +744,8 @@ go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -968,8 +970,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/go.mod b/go.mod index 6270a4802..7c4be05fa 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/hashicorp/go-version v1.7.0 github.com/ipfs-shipyard/nopfs v0.0.14 github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 - github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 + github.com/ipfs/boxo v0.36.0 github.com/ipfs/go-block-format v0.2.3 github.com/ipfs/go-cid v0.6.0 github.com/ipfs/go-cidutil v0.1.0 @@ -38,7 +38,7 @@ require ( github.com/ipfs/go-ipld-format v0.6.3 github.com/ipfs/go-ipld-git v0.1.1 github.com/ipfs/go-ipld-legacy v0.2.2 - github.com/ipfs/go-log/v2 v2.9.0 + github.com/ipfs/go-log/v2 v2.9.1 github.com/ipfs/go-metrics-interface v0.3.0 github.com/ipfs/go-metrics-prometheus v0.1.0 github.com/ipfs/go-test v0.2.3 @@ -50,9 +50,9 @@ require ( github.com/jbenet/go-temp-err-catcher v0.1.0 github.com/julienschmidt/httprouter v1.3.0 github.com/libp2p/go-doh-resolver v0.5.0 - github.com/libp2p/go-libp2p v0.46.0 + github.com/libp2p/go-libp2p v0.47.0 github.com/libp2p/go-libp2p-http v0.5.0 - github.com/libp2p/go-libp2p-kad-dht v0.37.0 + github.com/libp2p/go-libp2p-kad-dht v0.37.1 github.com/libp2p/go-libp2p-kbucket v0.8.0 github.com/libp2p/go-libp2p-pubsub v0.15.0 github.com/libp2p/go-libp2p-pubsub-router v0.6.0 @@ -60,7 +60,7 @@ require ( github.com/libp2p/go-libp2p-routing-helpers v0.7.5 github.com/libp2p/go-libp2p-testing v0.12.0 github.com/libp2p/go-socket-activation v0.1.1 - github.com/miekg/dns v1.1.68 + github.com/miekg/dns v1.1.72 github.com/multiformats/go-multiaddr v0.16.1 github.com/multiformats/go-multiaddr-dns v0.4.1 github.com/multiformats/go-multibase v0.2.0 @@ -79,14 +79,14 @@ require ( go.opencensus.io v0.24.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 go.opentelemetry.io/contrib/propagators/autoprop v0.46.1 - go.opentelemetry.io/otel v1.38.0 + go.opentelemetry.io/otel v1.39.0 go.opentelemetry.io/otel/exporters/prometheus v0.56.0 go.opentelemetry.io/otel/sdk v1.38.0 go.opentelemetry.io/otel/sdk/metric v1.38.0 - go.opentelemetry.io/otel/trace v1.38.0 + go.opentelemetry.io/otel/trace v1.39.0 go.uber.org/dig v1.19.0 go.uber.org/fx v1.24.0 - go.uber.org/zap v1.27.0 + go.uber.org/zap v1.27.1 golang.org/x/crypto v0.47.0 golang.org/x/exp v0.0.0-20260112195511-716be5621a96 golang.org/x/mod v0.32.0 @@ -123,11 +123,12 @@ require ( github.com/dgraph-io/badger v1.6.2 // indirect github.com/dgraph-io/ristretto v0.0.2 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect + github.com/dunglas/httpsfv v1.1.0 // indirect github.com/fatih/color v1.15.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/filecoin-project/go-clock v0.1.0 // indirect github.com/flynn/noise v1.1.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.10 // indirect + github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/gammazero/chanqueue v1.1.1 // indirect github.com/gammazero/deque v1.2.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect @@ -150,11 +151,11 @@ require ( github.com/huin/goupnp v1.3.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect github.com/ipfs/go-bitfield v1.1.0 // indirect - github.com/ipfs/go-dsqueue v0.1.1 // indirect + github.com/ipfs/go-dsqueue v0.1.2 // indirect github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect - github.com/ipfs/go-ipfs-pq v0.0.3 // indirect + github.com/ipfs/go-ipfs-pq v0.0.4 // indirect github.com/ipfs/go-ipfs-redirects-file v0.1.2 // indirect - github.com/ipfs/go-peertaskqueue v0.8.2 // indirect + github.com/ipfs/go-peertaskqueue v0.8.3 // indirect github.com/jackpal/go-nat-pmp v1.0.2 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect @@ -169,7 +170,7 @@ require ( github.com/libp2p/go-libp2p-gostream v0.6.0 // indirect github.com/libp2p/go-libp2p-xor v0.1.0 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect - github.com/libp2p/go-netroute v0.3.0 // indirect + github.com/libp2p/go-netroute v0.4.0 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/libp2p/go-yamux/v5 v5.0.1 // indirect github.com/libp2p/zeroconf/v2 v2.2.0 // indirect @@ -220,8 +221,8 @@ require ( github.com/prometheus/procfs v0.17.0 // indirect github.com/prometheus/statsd_exporter v0.27.1 // indirect github.com/quic-go/qpack v0.6.0 // indirect - github.com/quic-go/quic-go v0.57.1 // indirect - github.com/quic-go/webtransport-go v0.9.0 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect + github.com/quic-go/webtransport-go v0.10.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rs/cors v1.11.1 // indirect @@ -248,7 +249,7 @@ require ( go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 // indirect go.opentelemetry.io/otel/exporters/zipkin v1.38.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect go.opentelemetry.io/proto/otlp v1.7.1 // indirect go.uber.org/mock v0.5.2 // indirect go.uber.org/multierr v1.11.0 // indirect @@ -256,14 +257,14 @@ require ( go.yaml.in/yaml/v2 v2.4.3 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/net v0.49.0 // indirect - golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect golang.org/x/telemetry v0.0.0-20260109210033-bd525da824e2 // indirect golang.org/x/term v0.39.0 // indirect golang.org/x/text v0.33.0 // indirect golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.41.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - gonum.org/v1/gonum v0.16.0 // indirect + gonum.org/v1/gonum v0.17.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect google.golang.org/grpc v1.75.0 // indirect diff --git a/go.sum b/go.sum index cba1d9ac9..1e4be0486 100644 --- a/go.sum +++ b/go.sum @@ -157,6 +157,8 @@ github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70d github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dunglas/httpsfv v1.1.0 h1:Jw76nAyKWKZKFrpMMcL76y35tOpYHqQPzHQiwDvpe54= +github.com/dunglas/httpsfv v1.1.0/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= @@ -187,8 +189,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= -github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= +github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gammazero/chanqueue v1.1.1 h1:n9Y+zbBxw2f7uUE9wpgs0rOSkP/I/yhDLiNuhyVjojQ= github.com/gammazero/chanqueue v1.1.1/go.mod h1:fMwpwEiuUgpab0sH4VHiVcEoji1pSi+EIzeG4TPeKPc= github.com/gammazero/deque v1.2.0 h1:scEFO8Uidhw6KDU5qg1HA5fYwM0+us2qdeJqm43bitU= @@ -336,8 +338,8 @@ github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 h1:OqNqsGZPX8zh3eFMO8Lf8EHRRnSGBMqcd github.com/ipfs-shipyard/nopfs/ipfs v0.25.0/go.mod h1:BxhUdtBgOXg1B+gAPEplkg/GpyTZY+kCMSfsJvvydqU= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 h1:pRQYSSGnGQa921d8v0uhXg2BGzoSf9ndTWTlR7ImVoo= -github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2/go.mod h1:Abmp1if6bMQG87/0SQPIB9fkxJnZMLCt2nQw3yUZHH0= +github.com/ipfs/boxo v0.36.0 h1:DarrMBM46xCs6GU6Vz+AL8VUyXykqHAqZYx8mR0Oics= +github.com/ipfs/boxo v0.36.0/go.mod h1:92hnRXfP5ScKEIqlq9Ns7LR1dFXEVADKWVGH0fjk83k= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-block-format v0.0.3/go.mod h1:4LmD4ZUw0mhO+JSKdpWwrzATiEfM7WWgQ8H5l6P8MVk= @@ -368,8 +370,8 @@ github.com/ipfs/go-ds-measure v0.2.2 h1:4kwvBGbbSXNYe4ANlg7qTIYoZU6mNlqzQHdVqICk github.com/ipfs/go-ds-measure v0.2.2/go.mod h1:b/87ak0jMgH9Ylt7oH0+XGy4P8jHx9KG09Qz+pOeTIs= github.com/ipfs/go-ds-pebble v0.5.9 h1:D1FEuMxjbEmDADNqsyT74n9QHVAn12nv9i9Qa15AFYc= github.com/ipfs/go-ds-pebble v0.5.9/go.mod h1:XmUBN05l6B+tMg7mpMS75ZcKW/CX01uZMhhWw85imQA= -github.com/ipfs/go-dsqueue v0.1.1 h1:6PQlHDyf9PSTN69NmwUir5+0is3tU0vRJj8zLlgK8Mc= -github.com/ipfs/go-dsqueue v0.1.1/go.mod h1:Xxg353WSwwzYn3FGSzZ+taSQII3pIZ+EJC8/oWRDM10= +github.com/ipfs/go-dsqueue v0.1.2 h1:jBMsgvT9Pj9l3cqI0m5jYpW/aWDYkW4Us6EuzrcSGbs= +github.com/ipfs/go-dsqueue v0.1.2/go.mod h1:OU94YuMVUIF/ctR7Ysov9PI4gOa2XjPGN9nd8imSv78= github.com/ipfs/go-fs-lock v0.1.1 h1:TecsP/Uc7WqYYatasreZQiP9EGRy4ZnKoG4yXxR33nw= github.com/ipfs/go-fs-lock v0.1.1/go.mod h1:2goSXMCw7QfscHmSe09oXiR34DQeUdm+ei+dhonqly0= github.com/ipfs/go-ipfs-cmds v0.15.0 h1:nQDgKadrzyiFyYoZMARMIoVoSwe3gGTAfGvrWLeAQbQ= @@ -379,8 +381,8 @@ github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1I github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw= github.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo= -github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE= -github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4= +github.com/ipfs/go-ipfs-pq v0.0.4 h1:U7jjENWJd1jhcrR8X/xHTaph14PTAK9O+yaLJbjqgOw= +github.com/ipfs/go-ipfs-pq v0.0.4/go.mod h1:9UdLOIIb99IFrgT0Fc53pvbvlJBhpUb4GJuAQf3+O2A= github.com/ipfs/go-ipfs-redirects-file v0.1.2 h1:QCK7VtL91FH17KROVVy5KrzDx2hu68QvB2FTWk08ZQk= github.com/ipfs/go-ipfs-redirects-file v0.1.2/go.mod h1:yIiTlLcDEM/8lS6T3FlCEXZktPPqSOyuY6dEzVqw7Fw= github.com/ipfs/go-ipfs-util v0.0.1/go.mod h1:spsl5z8KUnrve+73pOhSVZND1SIxPW5RyBCNzQxlJBc= @@ -394,14 +396,14 @@ github.com/ipfs/go-ipld-git v0.1.1/go.mod h1:+VyMqF5lMcJh4rwEppV0e6g4nCCHXThLYYD github.com/ipfs/go-ipld-legacy v0.2.2 h1:DThbqCPVLpWBcGtU23KDLiY2YRZZnTkXQyfz8aOfBkQ= github.com/ipfs/go-ipld-legacy v0.2.2/go.mod h1:hhkj+b3kG9b2BcUNw8IFYAsfeNo8E3U7eYlWeAOPyDU= github.com/ipfs/go-log v0.0.1/go.mod h1:kL1d2/hzSpI0thNYjiKfjanbVNU+IIGA/WnNESY9leM= -github.com/ipfs/go-log/v2 v2.9.0 h1:l4b06AwVXwldIzbVPZy5z7sKp9lHFTX0KWfTBCtHaOk= -github.com/ipfs/go-log/v2 v2.9.0/go.mod h1:UhIYAwMV7Nb4ZmihUxfIRM2Istw/y9cAk3xaK+4Zs2c= +github.com/ipfs/go-log/v2 v2.9.1 h1:3JXwHWU31dsCpvQ+7asz6/QsFJHqFr4gLgQ0FWteujk= +github.com/ipfs/go-log/v2 v2.9.1/go.mod h1:evFx7sBiohUN3AG12mXlZBw5hacBQld3ZPHrowlJYoo= github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU= github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY= github.com/ipfs/go-metrics-prometheus v0.1.0 h1:bApWOHkrH3VTBHzTHrZSfq4n4weOZDzZFxUXv+HyKcA= github.com/ipfs/go-metrics-prometheus v0.1.0/go.mod h1:2GtL525C/4yxtvSXpRJ4dnE45mCX9AS0XRa03vHx7G0= -github.com/ipfs/go-peertaskqueue v0.8.2 h1:PaHFRaVFdxQk1Qo3OKiHPYjmmusQy7gKQUaL8JDszAU= -github.com/ipfs/go-peertaskqueue v0.8.2/go.mod h1:L6QPvou0346c2qPJNiJa6BvOibxDfaiPlqHInmzg0FA= +github.com/ipfs/go-peertaskqueue v0.8.3 h1:tBPpGJy+A92RqtRFq5amJn0Uuj8Pw8tXi0X3eHfHM8w= +github.com/ipfs/go-peertaskqueue v0.8.3/go.mod h1:OqVync4kPOcXEGdj/LKvox9DCB5mkSBeXsPczCxLtYA= github.com/ipfs/go-test v0.2.3 h1:Z/jXNAReQFtCYyn7bsv/ZqUwS6E7iIcSpJ2CuzCvnrc= github.com/ipfs/go-test v0.2.3/go.mod h1:QW8vSKkwYvWFwIZQLGQXdkt9Ud76eQXRQ9Ao2H+cA1o= github.com/ipfs/go-unixfsnode v1.10.2 h1:TREegX1J4X+k1w4AhoDuxxFvVcS9SegMRvrmxF6Tca8= @@ -480,8 +482,8 @@ github.com/libp2p/go-flow-metrics v0.0.1/go.mod h1:Iv1GH0sG8DtYN3SVJ2eG221wMiNpZ github.com/libp2p/go-flow-metrics v0.0.3/go.mod h1:HeoSNUrOJVK1jEpDqVEiUOIXqhbnS27omG0uWU5slZs= github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784= github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo= -github.com/libp2p/go-libp2p v0.46.0 h1:0T2yvIKpZ3DVYCuPOFxPD1layhRU486pj9rSlGWYnDM= -github.com/libp2p/go-libp2p v0.46.0/go.mod h1:TbIDnpDjBLa7isdgYpbxozIVPBTmM/7qKOJP4SFySrQ= +github.com/libp2p/go-libp2p v0.47.0 h1:qQpBjSCWNQFF0hjBbKirMXE9RHLtSuzTDkTfr1rw0yc= +github.com/libp2p/go-libp2p v0.47.0/go.mod h1:s8HPh7mMV933OtXzONaGFseCg/BE//m1V34p3x4EUOY= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= github.com/libp2p/go-libp2p-core v0.2.4/go.mod h1:STh4fdfa5vDYr0/SzYYeqnt+E6KfEV5VxfIrm0bcI0g= @@ -490,8 +492,8 @@ github.com/libp2p/go-libp2p-gostream v0.6.0 h1:QfAiWeQRce6pqnYfmIVWJFXNdDyfiR/qk github.com/libp2p/go-libp2p-gostream v0.6.0/go.mod h1:Nywu0gYZwfj7Jc91PQvbGU8dIpqbQQkjWgDuOrFaRdA= github.com/libp2p/go-libp2p-http v0.5.0 h1:+x0AbLaUuLBArHubbbNRTsgWz0RjNTy6DJLOxQ3/QBc= github.com/libp2p/go-libp2p-http v0.5.0/go.mod h1:glh87nZ35XCQyFsdzZps6+F4HYI6DctVFY5u1fehwSg= -github.com/libp2p/go-libp2p-kad-dht v0.37.0 h1:V1IkFzK9taNS1UNAx260foulcBPH+watAUFjNo2qMUY= -github.com/libp2p/go-libp2p-kad-dht v0.37.0/go.mod h1:o4FPa1ea++UVAMJ1c+kyjUmj3CKm9+ZCyzQb4uutCFM= +github.com/libp2p/go-libp2p-kad-dht v0.37.1 h1:jtX8bQIXVCs6/allskNB4m5n95Xvwav7wHAhopGZfS0= +github.com/libp2p/go-libp2p-kad-dht v0.37.1/go.mod h1:Uwokdh232k9Y1uMy2yJOK5zb7hpMHn4P8uWS4s9i05Q= github.com/libp2p/go-libp2p-kbucket v0.3.1/go.mod h1:oyjT5O7tS9CQurok++ERgc46YLwEpuGoFq9ubvoUOio= github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s= github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4= @@ -511,8 +513,8 @@ github.com/libp2p/go-libp2p-xor v0.1.0/go.mod h1:LSTM5yRnjGZbWNTA/hRwq2gGFrvRIbQ github.com/libp2p/go-msgio v0.0.4/go.mod h1:63lBBgOTDKQL6EWazRMCwXsEeEeK9O2Cd+0+6OOuipQ= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= -github.com/libp2p/go-netroute v0.3.0 h1:nqPCXHmeNmgTJnktosJ/sIef9hvwYCrsLxXmfNks/oc= -github.com/libp2p/go-netroute v0.3.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= +github.com/libp2p/go-netroute v0.4.0 h1:sZZx9hyANYUx9PZyqcgE/E1GUG3iEtTZHUEvdtXT7/Q= +github.com/libp2p/go-netroute v0.4.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc= github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= @@ -525,8 +527,8 @@ github.com/libp2p/zeroconf/v2 v2.2.0 h1:Cup06Jv6u81HLhIj1KasuNM/RHHrJ8T7wOTS4+Tv github.com/libp2p/zeroconf/v2 v2.2.0/go.mod h1:fuJqLnUwZTshS3U/bMRJ3+ow/v9oid1n0DmyYyNO1Xs= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxfg= -github.com/marcopolo/simnet v0.0.1/go.mod h1:WDaQkgLAjqDUEBAOXz22+1j6wXKfGlC5sD5XWt3ddOs= +github.com/marcopolo/simnet v0.0.4 h1:50Kx4hS9kFGSRIbrt9xUS3NJX33EyPqHVmpXvaKLqrY= +github.com/marcopolo/simnet v0.0.4/go.mod h1:tfQF1u2DmaB6WHODMtQaLtClEf3a296CKQLq5gAsIS0= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -550,8 +552,8 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyex github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= -github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= @@ -740,10 +742,10 @@ github.com/prometheus/statsd_exporter v0.27.1 h1:tcRJOmwlA83HPfWzosAgr2+zEN5XDFv github.com/prometheus/statsd_exporter v0.27.1/go.mod h1:vA6ryDfsN7py/3JApEst6nLTJboq66XsNcJGNmC88NQ= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= -github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= -github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= -github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/quic-go/webtransport-go v0.10.0 h1:LqXXPOXuETY5Xe8ITdGisBzTYmUOy5eSj+9n4hLTjHI= +github.com/quic-go/webtransport-go v0.10.0/go.mod h1:LeGIXr5BQKE3UsynwVBeQrU1TPrbh73MGoC6jd+V7ow= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -882,8 +884,8 @@ go.opentelemetry.io/contrib/propagators/jaeger v1.21.1 h1:f4beMGDKiVzg9IcX7/VuWV go.opentelemetry.io/contrib/propagators/jaeger v1.21.1/go.mod h1:U9jhkEl8d1LL+QXY7q3kneJWJugiN3kZJV2OWz3hkBY= go.opentelemetry.io/contrib/propagators/ot v1.21.1 h1:3TN5vkXjKYWp0YdMcnUEC/A+pBPvqz9V3nCS2xmcurk= go.opentelemetry.io/contrib/propagators/ot v1.21.1/go.mod h1:oy0MYCbS/b3cqUDW37wBWtlwBIsutngS++Lklpgh+fc= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= @@ -896,14 +898,14 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/ go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE= go.opentelemetry.io/otel/exporters/zipkin v1.38.0 h1:0rJ2TmzpHDG+Ib9gPmu3J3cE0zXirumQcKS4wCoZUa0= go.opentelemetry.io/otel/exporters/zipkin v1.38.0/go.mod h1:Su/nq/K5zRjDKKC3Il0xbViE3juWgG3JDoqLumFx5G0= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= @@ -916,8 +918,8 @@ go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -1036,8 +1038,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1209,8 +1211,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index e58fd174d..1326c72b4 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -8,7 +8,7 @@ require ( github.com/Kubuxu/gocovmerge v0.0.0-20161216165753-7ecaa51963cd github.com/golangci/golangci-lint v1.64.8 github.com/ipfs/go-cidutil v0.1.0 - github.com/ipfs/go-log/v2 v2.9.0 + github.com/ipfs/go-log/v2 v2.9.1 github.com/ipfs/go-test v0.2.3 github.com/ipfs/hang-fds v0.1.0 github.com/ipfs/iptb v1.4.1 @@ -89,7 +89,7 @@ require ( github.com/flynn/noise v1.1.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.10 // indirect + github.com/gabriel-vasile/mimetype v1.4.12 // indirect github.com/gammazero/chanqueue v1.1.1 // indirect github.com/gammazero/deque v1.2.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect @@ -135,12 +135,12 @@ require ( github.com/huin/goupnp v1.3.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/ipfs/bbloom v0.0.4 // indirect - github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 // indirect + github.com/ipfs/boxo v0.36.0 // indirect github.com/ipfs/go-bitfield v1.1.0 // indirect github.com/ipfs/go-block-format v0.2.3 // indirect github.com/ipfs/go-cid v0.6.0 // indirect github.com/ipfs/go-datastore v0.9.0 // indirect - github.com/ipfs/go-dsqueue v0.1.1 // indirect + github.com/ipfs/go-dsqueue v0.1.2 // indirect github.com/ipfs/go-ipfs-cmds v0.15.0 // indirect github.com/ipfs/go-ipfs-redirects-file v0.1.2 // indirect github.com/ipfs/go-ipld-cbor v0.2.1 // indirect @@ -181,14 +181,14 @@ require ( github.com/libp2p/go-cidranger v1.1.0 // indirect github.com/libp2p/go-doh-resolver v0.5.0 // indirect github.com/libp2p/go-flow-metrics v0.3.0 // indirect - github.com/libp2p/go-libp2p v0.46.0 // indirect + github.com/libp2p/go-libp2p v0.47.0 // indirect github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect - github.com/libp2p/go-libp2p-kad-dht v0.37.0 // indirect + github.com/libp2p/go-libp2p-kad-dht v0.37.1 // indirect github.com/libp2p/go-libp2p-kbucket v0.8.0 // indirect github.com/libp2p/go-libp2p-record v0.3.1 // indirect github.com/libp2p/go-libp2p-routing-helpers v0.7.5 // indirect github.com/libp2p/go-msgio v0.3.0 // indirect - github.com/libp2p/go-netroute v0.3.0 // indirect + github.com/libp2p/go-netroute v0.4.0 // indirect github.com/libp2p/go-reuseport v0.4.0 // indirect github.com/macabu/inamedparam v0.1.3 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -201,7 +201,7 @@ require ( github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mgechev/revive v1.7.0 // indirect github.com/mholt/acmez/v3 v3.1.2 // indirect - github.com/miekg/dns v1.1.68 // indirect + github.com/miekg/dns v1.1.72 // indirect github.com/minio/minlz v1.0.1-0.20250507153514-87eb42fe8882 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -257,7 +257,7 @@ require ( github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect - github.com/quic-go/quic-go v0.57.1 // indirect + github.com/quic-go/quic-go v0.59.0 // indirect github.com/raeperd/recvcheck v0.2.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect @@ -316,14 +316,14 @@ require ( go-simpler.org/sloglint v0.9.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/dig v1.19.0 // indirect go.uber.org/fx v1.24.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.uber.org/zap/exp v0.3.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/crypto v0.47.0 // indirect @@ -338,7 +338,7 @@ require ( golang.org/x/time v0.12.0 // indirect golang.org/x/tools v0.41.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - gonum.org/v1/gonum v0.16.0 // indirect + gonum.org/v1/gonum v0.17.0 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index f0d1bf408..8351caa9d 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -141,6 +141,8 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk= github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= +github.com/dunglas/httpsfv v1.1.0 h1:Jw76nAyKWKZKFrpMMcL76y35tOpYHqQPzHQiwDvpe54= +github.com/dunglas/httpsfv v1.1.0/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= @@ -165,8 +167,8 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= -github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw= +github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gammazero/chanqueue v1.1.1 h1:n9Y+zbBxw2f7uUE9wpgs0rOSkP/I/yhDLiNuhyVjojQ= github.com/gammazero/chanqueue v1.1.1/go.mod h1:fMwpwEiuUgpab0sH4VHiVcEoji1pSi+EIzeG4TPeKPc= github.com/gammazero/deque v1.2.0 h1:scEFO8Uidhw6KDU5qg1HA5fYwM0+us2qdeJqm43bitU= @@ -294,8 +296,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= -github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2 h1:pRQYSSGnGQa921d8v0uhXg2BGzoSf9ndTWTlR7ImVoo= -github.com/ipfs/boxo v0.35.3-0.20260109213916-89dc184784f2/go.mod h1:Abmp1if6bMQG87/0SQPIB9fkxJnZMLCt2nQw3yUZHH0= +github.com/ipfs/boxo v0.36.0 h1:DarrMBM46xCs6GU6Vz+AL8VUyXykqHAqZYx8mR0Oics= +github.com/ipfs/boxo v0.36.0/go.mod h1:92hnRXfP5ScKEIqlq9Ns7LR1dFXEVADKWVGH0fjk83k= github.com/ipfs/go-bitfield v1.1.0 h1:fh7FIo8bSwaJEh6DdTWbCeZ1eqOaOkKFI74SCnsWbGA= github.com/ipfs/go-bitfield v1.1.0/go.mod h1:paqf1wjq/D2BBmzfTVFlJQ9IlFOZpg422HL0HqsGWHU= github.com/ipfs/go-block-format v0.2.3 h1:mpCuDaNXJ4wrBJLrtEaGFGXkferrw5eqVvzaHhtFKQk= @@ -310,14 +312,14 @@ github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= github.com/ipfs/go-ds-leveldb v0.5.2 h1:6nmxlQ2zbp4LCNdJVsmHfs9GP0eylfBNxpmY1csp0x0= github.com/ipfs/go-ds-leveldb v0.5.2/go.mod h1:2fAwmcvD3WoRT72PzEekHBkQmBDhc39DJGoREiuGmYo= -github.com/ipfs/go-dsqueue v0.1.1 h1:6PQlHDyf9PSTN69NmwUir5+0is3tU0vRJj8zLlgK8Mc= -github.com/ipfs/go-dsqueue v0.1.1/go.mod h1:Xxg353WSwwzYn3FGSzZ+taSQII3pIZ+EJC8/oWRDM10= +github.com/ipfs/go-dsqueue v0.1.2 h1:jBMsgvT9Pj9l3cqI0m5jYpW/aWDYkW4Us6EuzrcSGbs= +github.com/ipfs/go-dsqueue v0.1.2/go.mod h1:OU94YuMVUIF/ctR7Ysov9PI4gOa2XjPGN9nd8imSv78= github.com/ipfs/go-ipfs-cmds v0.15.0 h1:nQDgKadrzyiFyYoZMARMIoVoSwe3gGTAfGvrWLeAQbQ= github.com/ipfs/go-ipfs-cmds v0.15.0/go.mod h1:VABf/mv/wqvYX6hLG6Z+40eNAEw3FQO0bSm370Or3Wk= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= -github.com/ipfs/go-ipfs-pq v0.0.3 h1:YpoHVJB+jzK15mr/xsWC574tyDLkezVrDNeaalQBsTE= -github.com/ipfs/go-ipfs-pq v0.0.3/go.mod h1:btNw5hsHBpRcSSgZtiNm/SLj5gYIZ18AKtv3kERkRb4= +github.com/ipfs/go-ipfs-pq v0.0.4 h1:U7jjENWJd1jhcrR8X/xHTaph14PTAK9O+yaLJbjqgOw= +github.com/ipfs/go-ipfs-pq v0.0.4/go.mod h1:9UdLOIIb99IFrgT0Fc53pvbvlJBhpUb4GJuAQf3+O2A= github.com/ipfs/go-ipfs-redirects-file v0.1.2 h1:QCK7VtL91FH17KROVVy5KrzDx2hu68QvB2FTWk08ZQk= github.com/ipfs/go-ipfs-redirects-file v0.1.2/go.mod h1:yIiTlLcDEM/8lS6T3FlCEXZktPPqSOyuY6dEzVqw7Fw= github.com/ipfs/go-ipld-cbor v0.2.1 h1:H05yEJbK/hxg0uf2AJhyerBDbjOuHX4yi+1U/ogRa7E= @@ -326,12 +328,12 @@ github.com/ipfs/go-ipld-format v0.6.3 h1:9/lurLDTotJpZSuL++gh3sTdmcFhVkCwsgx2+rA github.com/ipfs/go-ipld-format v0.6.3/go.mod h1:74ilVN12NXVMIV+SrBAyC05UJRk0jVvGqdmrcYZvCBk= github.com/ipfs/go-ipld-legacy v0.2.2 h1:DThbqCPVLpWBcGtU23KDLiY2YRZZnTkXQyfz8aOfBkQ= github.com/ipfs/go-ipld-legacy v0.2.2/go.mod h1:hhkj+b3kG9b2BcUNw8IFYAsfeNo8E3U7eYlWeAOPyDU= -github.com/ipfs/go-log/v2 v2.9.0 h1:l4b06AwVXwldIzbVPZy5z7sKp9lHFTX0KWfTBCtHaOk= -github.com/ipfs/go-log/v2 v2.9.0/go.mod h1:UhIYAwMV7Nb4ZmihUxfIRM2Istw/y9cAk3xaK+4Zs2c= +github.com/ipfs/go-log/v2 v2.9.1 h1:3JXwHWU31dsCpvQ+7asz6/QsFJHqFr4gLgQ0FWteujk= +github.com/ipfs/go-log/v2 v2.9.1/go.mod h1:evFx7sBiohUN3AG12mXlZBw5hacBQld3ZPHrowlJYoo= github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU= github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY= -github.com/ipfs/go-peertaskqueue v0.8.2 h1:PaHFRaVFdxQk1Qo3OKiHPYjmmusQy7gKQUaL8JDszAU= -github.com/ipfs/go-peertaskqueue v0.8.2/go.mod h1:L6QPvou0346c2qPJNiJa6BvOibxDfaiPlqHInmzg0FA= +github.com/ipfs/go-peertaskqueue v0.8.3 h1:tBPpGJy+A92RqtRFq5amJn0Uuj8Pw8tXi0X3eHfHM8w= +github.com/ipfs/go-peertaskqueue v0.8.3/go.mod h1:OqVync4kPOcXEGdj/LKvox9DCB5mkSBeXsPczCxLtYA= github.com/ipfs/go-test v0.2.3 h1:Z/jXNAReQFtCYyn7bsv/ZqUwS6E7iIcSpJ2CuzCvnrc= github.com/ipfs/go-test v0.2.3/go.mod h1:QW8vSKkwYvWFwIZQLGQXdkt9Ud76eQXRQ9Ao2H+cA1o= github.com/ipfs/go-unixfsnode v1.10.2 h1:TREegX1J4X+k1w4AhoDuxxFvVcS9SegMRvrmxF6Tca8= @@ -415,12 +417,12 @@ github.com/libp2p/go-doh-resolver v0.5.0 h1:4h7plVVW+XTS+oUBw2+8KfoM1jF6w8XmO7+s github.com/libp2p/go-doh-resolver v0.5.0/go.mod h1:aPDxfiD2hNURgd13+hfo29z9IC22fv30ee5iM31RzxU= github.com/libp2p/go-flow-metrics v0.3.0 h1:q31zcHUvHnwDO0SHaukewPYgwOBSxtt830uJtUx6784= github.com/libp2p/go-flow-metrics v0.3.0/go.mod h1:nuhlreIwEguM1IvHAew3ij7A8BMlyHQJ279ao24eZZo= -github.com/libp2p/go-libp2p v0.46.0 h1:0T2yvIKpZ3DVYCuPOFxPD1layhRU486pj9rSlGWYnDM= -github.com/libp2p/go-libp2p v0.46.0/go.mod h1:TbIDnpDjBLa7isdgYpbxozIVPBTmM/7qKOJP4SFySrQ= +github.com/libp2p/go-libp2p v0.47.0 h1:qQpBjSCWNQFF0hjBbKirMXE9RHLtSuzTDkTfr1rw0yc= +github.com/libp2p/go-libp2p v0.47.0/go.mod h1:s8HPh7mMV933OtXzONaGFseCg/BE//m1V34p3x4EUOY= github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94= github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8= -github.com/libp2p/go-libp2p-kad-dht v0.37.0 h1:V1IkFzK9taNS1UNAx260foulcBPH+watAUFjNo2qMUY= -github.com/libp2p/go-libp2p-kad-dht v0.37.0/go.mod h1:o4FPa1ea++UVAMJ1c+kyjUmj3CKm9+ZCyzQb4uutCFM= +github.com/libp2p/go-libp2p-kad-dht v0.37.1 h1:jtX8bQIXVCs6/allskNB4m5n95Xvwav7wHAhopGZfS0= +github.com/libp2p/go-libp2p-kad-dht v0.37.1/go.mod h1:Uwokdh232k9Y1uMy2yJOK5zb7hpMHn4P8uWS4s9i05Q= github.com/libp2p/go-libp2p-kbucket v0.8.0 h1:QAK7RzKJpYe+EuSEATAaaHYMYLkPDGC18m9jxPLnU8s= github.com/libp2p/go-libp2p-kbucket v0.8.0/go.mod h1:JMlxqcEyKwO6ox716eyC0hmiduSWZZl6JY93mGaaqc4= github.com/libp2p/go-libp2p-record v0.3.1 h1:cly48Xi5GjNw5Wq+7gmjfBiG9HCzQVkiZOUZ8kUl+Fg= @@ -431,8 +433,8 @@ github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUI github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg= github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0= github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM= -github.com/libp2p/go-netroute v0.3.0 h1:nqPCXHmeNmgTJnktosJ/sIef9hvwYCrsLxXmfNks/oc= -github.com/libp2p/go-netroute v0.3.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= +github.com/libp2p/go-netroute v0.4.0 h1:sZZx9hyANYUx9PZyqcgE/E1GUG3iEtTZHUEvdtXT7/Q= +github.com/libp2p/go-netroute v0.4.0/go.mod h1:Nkd5ShYgSMS5MUKy/MU2T57xFoOKvvLR92Lic48LEyA= github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s= github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU= github.com/libp2p/go-yamux/v5 v5.0.1 h1:f0WoX/bEF2E8SbE4c/k1Mo+/9z0O4oC/hWEA+nfYRSg= @@ -445,8 +447,8 @@ github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc= -github.com/marcopolo/simnet v0.0.1 h1:rSMslhPz6q9IvJeFWDoMGxMIrlsbXau3NkuIXHGJxfg= -github.com/marcopolo/simnet v0.0.1/go.mod h1:WDaQkgLAjqDUEBAOXz22+1j6wXKfGlC5sD5XWt3ddOs= +github.com/marcopolo/simnet v0.0.4 h1:50Kx4hS9kFGSRIbrt9xUS3NJX33EyPqHVmpXvaKLqrY= +github.com/marcopolo/simnet v0.0.4/go.mod h1:tfQF1u2DmaB6WHODMtQaLtClEf3a296CKQLq5gAsIS0= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4= @@ -466,8 +468,8 @@ github.com/mgechev/revive v1.7.0 h1:JyeQ4yO5K8aZhIKf5rec56u0376h8AlKNQEmjfkjKlY= github.com/mgechev/revive v1.7.0/go.mod h1:qZnwcNhoguE58dfi96IJeSTPeZQejNeoMQLUZGi4SW4= github.com/mholt/acmez/v3 v3.1.2 h1:auob8J/0FhmdClQicvJvuDavgd5ezwLBfKuYmynhYzc= github.com/mholt/acmez/v3 v3.1.2/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= -github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= +github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= @@ -615,10 +617,10 @@ github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4l github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= -github.com/quic-go/quic-go v0.57.1 h1:25KAAR9QR8KZrCZRThWMKVAwGoiHIrNbT72ULHTuI10= -github.com/quic-go/quic-go v0.57.1/go.mod h1:ly4QBAjHA2VhdnxhojRsCUOeJwKYg+taDlos92xb1+s= -github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70= -github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao= +github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= +github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= +github.com/quic-go/webtransport-go v0.10.0 h1:LqXXPOXuETY5Xe8ITdGisBzTYmUOy5eSj+9n4hLTjHI= +github.com/quic-go/webtransport-go v0.10.0/go.mod h1:LeGIXr5BQKE3UsynwVBeQrU1TPrbh73MGoC6jd+V7ow= github.com/raeperd/recvcheck v0.2.0 h1:GnU+NsbiCqdC2XX5+vMZzP+jAJC5fht7rcVTAhX74UI= github.com/raeperd/recvcheck v0.2.0/go.mod h1:n04eYkwIR0JbgD73wT8wL4JjPC3wm0nFtzBnWNocnYU= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= @@ -783,16 +785,16 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/dig v1.19.0 h1:BACLhebsYdpQ7IROQ1AGPjrXcP5dF80U3gKoFzbaq/4= @@ -805,8 +807,8 @@ go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= @@ -970,8 +972,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/test/sharness/t0119-prometheus-data/prometheus_metrics_added_by_enabling_rcmgr b/test/sharness/t0119-prometheus-data/prometheus_metrics_added_by_enabling_rcmgr index e69de29bb..9829c7db8 100644 --- a/test/sharness/t0119-prometheus-data/prometheus_metrics_added_by_enabling_rcmgr +++ b/test/sharness/t0119-prometheus-data/prometheus_metrics_added_by_enabling_rcmgr @@ -0,0 +1 @@ +libp2p_rcmgr_limit diff --git a/test/sharness/t0119-prometheus-data/prometheus_metrics_added_by_measure_profile b/test/sharness/t0119-prometheus-data/prometheus_metrics_added_by_measure_profile index 03f132701..d7e13422a 100644 --- a/test/sharness/t0119-prometheus-data/prometheus_metrics_added_by_measure_profile +++ b/test/sharness/t0119-prometheus-data/prometheus_metrics_added_by_measure_profile @@ -156,3 +156,4 @@ leveldb_datastore_sync_latency_seconds_bucket leveldb_datastore_sync_latency_seconds_count leveldb_datastore_sync_latency_seconds_sum leveldb_datastore_sync_total +libp2p_rcmgr_limit From b57278017a5d91144ed354c4fa314d60c1556802 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:43:52 -0800 Subject: [PATCH 40/49] chore(deps): bump github.com/tidwall/gjson from 1.16.0 to 1.18.0 (#11168) Bumps [github.com/tidwall/gjson](https://github.com/tidwall/gjson) from 1.16.0 to 1.18.0. - [Commits](https://github.com/tidwall/gjson/compare/v1.16.0...v1.18.0) --- updated-dependencies: - dependency-name: github.com/tidwall/gjson dependency-version: 1.18.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7c4be05fa..a4a5c4ecb 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( github.com/prometheus/client_golang v1.23.2 github.com/stretchr/testify v1.11.1 github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d - github.com/tidwall/gjson v1.16.0 + github.com/tidwall/gjson v1.18.0 github.com/tidwall/sjson v1.2.5 github.com/whyrusleeping/go-sysinfo v0.0.0-20190219211824-4a357d4b90b1 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 diff --git a/go.sum b/go.sum index 1e4be0486..475c95728 100644 --- a/go.sum +++ b/go.sum @@ -809,8 +809,8 @@ github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJ github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U= github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= -github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= From ef99e0a0f70b97b713e6e1debf1d3d30806eb92e Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Thu, 29 Jan 2026 02:56:42 +0100 Subject: [PATCH 41/49] refactor(ci): add caching to ipfs-webui interop tests (#11173) * ci: add caching to ipfs-webui interop tests cache node_modules, Playwright browsers, and test build output to speed up repeated CI runs. also use node version from ipfs-webui/.tool-versions instead of hardcoding, and upload test artifacts on failure. * docs(ci): add header comment to interop workflow explain what helia-interop and ipfs-webui jobs do --- .github/workflows/interop.yml | 92 ++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 22 deletions(-) diff --git a/.github/workflows/interop.yml b/.github/workflows/interop.yml index 25bdba4f2..f78fd40df 100644 --- a/.github/workflows/interop.yml +++ b/.github/workflows/interop.yml @@ -1,3 +1,17 @@ +# Interoperability Tests +# +# This workflow ensures Kubo remains compatible with the broader IPFS ecosystem. +# It builds Kubo from source, then runs: +# +# 1. helia-interop: Tests compatibility with Helia (JavaScript IPFS implementation) +# using Playwright-based tests from @helia/interop package. +# +# 2. ipfs-webui: Runs E2E tests from ipfs/ipfs-webui repository to verify +# the web interface works correctly with the locally built Kubo binary. +# +# Both jobs use caching to speed up repeated runs (npm dependencies, Playwright +# browsers, and webui build artifacts). + name: Interop on: @@ -84,9 +98,6 @@ jobs: run: shell: bash steps: - - uses: actions/setup-node@v6 - with: - node-version: 20.x - uses: actions/download-artifact@v7 with: name: kubo @@ -96,36 +107,73 @@ jobs: with: repository: ipfs/ipfs-webui path: ipfs-webui - - run: | - echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - id: npm-cache-dir - - uses: actions/cache@v5 + - uses: actions/setup-node@v6 with: - path: ${{ steps.npm-cache-dir.outputs.dir }} - key: ${{ runner.os }}-${{ github.job }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-${{ github.job }}- - - env: - NPM_CACHE_DIR: ${{ steps.npm-cache-dir.outputs.dir }} - run: | - npm ci --prefer-offline --no-audit --progress=false --cache "$NPM_CACHE_DIR" - npx playwright install --with-deps - working-directory: ipfs-webui - - id: ref + node-version-file: 'ipfs-webui/.tool-versions' + - id: webui-ref run: echo "ref=$(git rev-parse --short HEAD)" | tee -a $GITHUB_OUTPUT working-directory: ipfs-webui - - id: state + - id: webui-state env: GITHUB_TOKEN: ${{ github.token }} - ENDPOINT: repos/ipfs/ipfs-webui/commits/${{ steps.ref.outputs.ref }}/status + ENDPOINT: repos/ipfs/ipfs-webui/commits/${{ steps.webui-ref.outputs.ref }}/status SELECTOR: .state KEY: state run: gh api "$ENDPOINT" --jq "$SELECTOR" | xargs -I{} echo "$KEY={}" | tee -a $GITHUB_OUTPUT - - name: Build ipfs-webui@main (state=${{ steps.state.outputs.state }}) + # Cache node_modules based on package-lock.json + - name: Cache node_modules + uses: actions/cache@v5 + id: node-modules-cache + with: + path: ipfs-webui/node_modules + key: ${{ runner.os }}-webui-node-modules-${{ hashFiles('ipfs-webui/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-webui-node-modules- + - name: Install dependencies + if: steps.node-modules-cache.outputs.cache-hit != 'true' + run: npm ci --prefer-offline --no-audit --progress=false + working-directory: ipfs-webui + # Cache Playwright browsers + - name: Cache Playwright browsers + uses: actions/cache@v5 + id: playwright-cache + with: + path: ~/.cache/ms-playwright + key: ${{ runner.os }}-playwright-${{ hashFiles('ipfs-webui/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-playwright- + # On cache miss: download browsers and install OS dependencies + - name: Install Playwright with dependencies + if: steps.playwright-cache.outputs.cache-hit != 'true' + run: npx playwright install --with-deps + working-directory: ipfs-webui + # On cache hit: only ensure OS dependencies are present (fast, idempotent) + - name: Install Playwright OS dependencies + if: steps.playwright-cache.outputs.cache-hit == 'true' + run: npx playwright install-deps + working-directory: ipfs-webui + # Cache test build output + - name: Cache test build + uses: actions/cache@v5 + id: test-build-cache + with: + path: ipfs-webui/build + key: ${{ runner.os }}-webui-build-${{ hashFiles('ipfs-webui/package-lock.json', 'ipfs-webui/src/**', 'ipfs-webui/public/**') }} + restore-keys: | + ${{ runner.os }}-webui-build- + - name: Build ipfs-webui@${{ steps.webui-ref.outputs.ref }} (state=${{ steps.webui-state.outputs.state }}) + if: steps.test-build-cache.outputs.cache-hit != 'true' run: npm run test:build working-directory: ipfs-webui - - name: Test ipfs-webui@main (state=${{ steps.state.outputs.state }}) E2E against the locally built Kubo binary + - name: Test ipfs-webui@${{ steps.webui-ref.outputs.ref }} (state=${{ steps.webui-state.outputs.state }}) E2E against the locally built Kubo binary run: npm run test:e2e env: IPFS_GO_EXEC: ${{ github.workspace }}/cmd/ipfs/ipfs working-directory: ipfs-webui + - name: Upload test artifacts on failure + if: failure() + uses: actions/upload-artifact@v6 + with: + name: webui-test-results + path: ipfs-webui/test-results/ + retention-days: 7 From 7de7af082076313705691bda7fc178dc7415f0c7 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 30 Jan 2026 17:20:56 +0100 Subject: [PATCH 42/49] feat(dns): skip DNS lookups for AutoTLS hostnames (#11140) * feat(dns): resolve libp2p.direct addresses locally without network I/O p2p-forge hostnames encode IP addresses directly (e.g., 1-2-3-4.peerID.libp2p.direct -> 1.2.3.4), so DNS queries are wasteful. kubo now parses these IPs in-memory. - applies to both default libp2p.direct and custom AutoTLS.DomainSuffix - TXT queries still delegate to network for ACME DNS-01 compatibility - https://github.com/ipfs/kubo/pull/11140#discussion_r2683477754 use fallback to network DNS instead of returning errors when local parsing fails, ensuring forward compatibility with future DNS records - https://github.com/ipfs/kubo/pull/11140#discussion_r2683512408 add peerID validation using peer.Decode(), matching libp2p.direct server behavior, with fallback on invalid peerID - https://github.com/ipfs/kubo/pull/11140#discussion_r2683521930 document interaction with DNS.Resolvers in config.md - https://github.com/ipfs/kubo/pull/11140#discussion_r2683526647 add AutoTLS.SkipDNSLookup config flag to disable local resolution (useful for debugging or custom DNS override scenarios) - https://github.com/ipfs/kubo/pull/11140#discussion_r2683533462 add E2E test verifying libp2p.direct resolves locally even when DNS.Resolvers points to a broken server additional improvements: - use madns.BasicResolver interface instead of custom basicResolver - add compile-time interface checks for p2pForgeResolver and madns.Resolver - refactor tests: merge IPv4/IPv6, add helpers, use config.DefaultDomainSuffix - improve changelog to explain public good benefit (reducing DNS load) Fixes #11136 --- config/autotls.go | 8 ++ core/node/dns.go | 35 ++++- core/node/p2pforge_resolver.go | 120 ++++++++++++++++ core/node/p2pforge_resolver_test.go | 172 +++++++++++++++++++++++ docs/changelogs/v0.40.md | 7 + docs/config.md | 19 ++- test/cli/dns_resolvers_multiaddr_test.go | 143 +++++++++++++++++++ 7 files changed, 502 insertions(+), 2 deletions(-) create mode 100644 core/node/p2pforge_resolver.go create mode 100644 core/node/p2pforge_resolver_test.go create mode 100644 test/cli/dns_resolvers_multiaddr_test.go diff --git a/config/autotls.go b/config/autotls.go index 805a9ded6..4d90b7171 100644 --- a/config/autotls.go +++ b/config/autotls.go @@ -16,6 +16,13 @@ type AutoTLS struct { // Optional, controls if Kubo should add /tls/sni/.../ws listener to every /tcp port if no explicit /ws is defined in Addresses.Swarm AutoWSS Flag `json:",omitempty"` + // Optional, controls whether to skip network DNS lookups for p2p-forge domains. + // Applies to resolution via DNS.Resolvers, including /dns* multiaddrs in go-libp2p. + // When enabled (default), A/AAAA queries for *.libp2p.direct are resolved + // locally by parsing the IP directly from the hostname, avoiding network I/O. + // Set to false to always use network DNS (useful for debugging). + SkipDNSLookup Flag `json:",omitempty"` + // Optional override of the parent domain that will be used DomainSuffix *OptionalString `json:",omitempty"` @@ -42,5 +49,6 @@ const ( DefaultCAEndpoint = p2pforge.DefaultCAEndpoint DefaultAutoWSS = true // requires AutoTLS.Enabled DefaultAutoTLSShortAddrs = true // requires AutoTLS.Enabled + DefaultAutoTLSSkipDNSLookup = true // skip network DNS for p2p-forge domains DefaultAutoTLSRegistrationDelay = 1 * time.Hour ) diff --git a/core/node/dns.go b/core/node/dns.go index 3f0875afb..ba4e00784 100644 --- a/core/node/dns.go +++ b/core/node/dns.go @@ -10,6 +10,10 @@ import ( madns "github.com/multiformats/go-multiaddr-dns" ) +// Compile-time interface check: *madns.Resolver (returned by gateway.NewDNSResolver +// and madns.NewResolver) must implement madns.BasicResolver for p2pForgeResolver fallback. +var _ madns.BasicResolver = (*madns.Resolver)(nil) + func DNSResolver(cfg *config.Config) (*madns.Resolver, error) { var dohOpts []doh.Option if !cfg.DNS.MaxCacheTTL.IsDefault() { @@ -19,5 +23,34 @@ func DNSResolver(cfg *config.Config) (*madns.Resolver, error) { // Replace "auto" DNS resolver placeholders with autoconf values resolvers := cfg.DNSResolversWithAutoConf() - return gateway.NewDNSResolver(resolvers, dohOpts...) + // Get base resolver from boxo (handles custom DoH resolvers per eTLD) + baseResolver, err := gateway.NewDNSResolver(resolvers, dohOpts...) + if err != nil { + return nil, err + } + + // Check if we should skip network DNS lookups for p2p-forge domains + skipAutoTLSDNS := cfg.AutoTLS.SkipDNSLookup.WithDefault(config.DefaultAutoTLSSkipDNSLookup) + if !skipAutoTLSDNS { + // Local resolution disabled, use network DNS for everything + return baseResolver, nil + } + + // Build list of p2p-forge domains to resolve locally without network I/O. + // AutoTLS hostnames encode IP addresses directly (e.g., 1-2-3-4.peerID.libp2p.direct), + // so DNS lookups are wasteful. We resolve these in-memory when possible. + forgeDomains := []string{config.DefaultDomainSuffix} + customDomain := cfg.AutoTLS.DomainSuffix.WithDefault(config.DefaultDomainSuffix) + if customDomain != config.DefaultDomainSuffix { + forgeDomains = append(forgeDomains, customDomain) + } + forgeResolver := NewP2PForgeResolver(forgeDomains, baseResolver) + + // Register p2p-forge resolver for each domain, fallback to baseResolver for others + opts := []madns.Option{madns.WithDefaultResolver(baseResolver)} + for _, domain := range forgeDomains { + opts = append(opts, madns.WithDomainResolver(domain+".", forgeResolver)) + } + + return madns.NewResolver(opts...) } diff --git a/core/node/p2pforge_resolver.go b/core/node/p2pforge_resolver.go new file mode 100644 index 000000000..6ddbb1904 --- /dev/null +++ b/core/node/p2pforge_resolver.go @@ -0,0 +1,120 @@ +package node + +import ( + "context" + "net" + "net/netip" + "strings" + + "github.com/libp2p/go-libp2p/core/peer" + madns "github.com/multiformats/go-multiaddr-dns" +) + +// p2pForgeResolver implements madns.BasicResolver for deterministic resolution +// of p2p-forge domains (e.g., *.libp2p.direct) without network I/O for A/AAAA queries. +// +// p2p-forge encodes IP addresses in DNS hostnames: +// - IPv4: 1-2-3-4.peerID.libp2p.direct -> 1.2.3.4 +// - IPv6: 2001-db8--1.peerID.libp2p.direct -> 2001:db8::1 +// +// When local parsing fails (invalid format, invalid peerID, etc.), the resolver +// falls back to network DNS. This ensures future .libp2p.direct records +// can still resolve if the authoritative DNS adds support for them. +// +// TXT queries always delegate to the fallback resolver. This is important for +// p2p-forge/client ACME DNS-01 challenges to work correctly, as Let's Encrypt +// needs to verify TXT records at _acme-challenge.peerID.libp2p.direct. +// +// See: https://github.com/ipshipyard/p2p-forge +type p2pForgeResolver struct { + suffixes []string + fallback madns.BasicResolver +} + +// Compile-time check that p2pForgeResolver implements madns.BasicResolver. +var _ madns.BasicResolver = (*p2pForgeResolver)(nil) + +// NewP2PForgeResolver creates a resolver for the given p2p-forge domain suffixes. +// Each suffix should be a bare domain like "libp2p.direct" (without leading dot). +// When local IP parsing fails, queries fall back to the provided resolver. +// TXT queries always delegate to the fallback resolver for ACME compatibility. +func NewP2PForgeResolver(suffixes []string, fallback madns.BasicResolver) *p2pForgeResolver { + normalized := make([]string, len(suffixes)) + for i, s := range suffixes { + normalized[i] = strings.ToLower(strings.TrimSuffix(s, ".")) + } + return &p2pForgeResolver{suffixes: normalized, fallback: fallback} +} + +// LookupIPAddr parses IP addresses encoded in the hostname. +// +// Format: .. +// - IPv4: 192-168-1-1.peerID.libp2p.direct -> [192.168.1.1] +// - IPv6: 2001-db8--1.peerID.libp2p.direct -> [2001:db8::1] +// +// If the hostname doesn't match the expected format (wrong suffix, invalid peerID, +// invalid IP encoding, or peerID-only), the lookup falls back to network DNS. +// This allows future DNS records like .libp2p.direct to resolve normally. +func (r *p2pForgeResolver) LookupIPAddr(ctx context.Context, hostname string) ([]net.IPAddr, error) { + // DNS is case-insensitive, normalize to lowercase + hostname = strings.ToLower(strings.TrimSuffix(hostname, ".")) + + // find matching suffix and extract subdomain + var subdomain string + for _, suffix := range r.suffixes { + if sub, found := strings.CutSuffix(hostname, "."+suffix); found { + subdomain = sub + break + } + } + if subdomain == "" { + // not a p2p-forge domain, fallback to network + return r.fallback.LookupIPAddr(ctx, hostname) + } + + // split subdomain into parts: should be [ip-prefix, peerID] + parts := strings.Split(subdomain, ".") + if len(parts) != 2 { + // not the expected . format, fallback to network + return r.fallback.LookupIPAddr(ctx, hostname) + } + + encodedIP := parts[0] + peerIDStr := parts[1] + + // validate peerID (same validation as libp2p.direct DNS server) + if _, err := peer.Decode(peerIDStr); err != nil { + // invalid peerID, fallback to network + return r.fallback.LookupIPAddr(ctx, hostname) + } + + // RFC 1123: hostname labels cannot start or end with hyphen + if len(encodedIP) == 0 || encodedIP[0] == '-' || encodedIP[len(encodedIP)-1] == '-' { + // invalid hostname label, fallback to network + return r.fallback.LookupIPAddr(ctx, hostname) + } + + // try parsing as IPv4 first: segments joined by "-" become "." + segments := strings.Split(encodedIP, "-") + if len(segments) == 4 { + ipv4Str := strings.Join(segments, ".") + if ip, err := netip.ParseAddr(ipv4Str); err == nil && ip.Is4() { + return []net.IPAddr{{IP: ip.AsSlice()}}, nil + } + } + + // try parsing as IPv6: segments joined by "-" become ":" + ipv6Str := strings.Join(segments, ":") + if ip, err := netip.ParseAddr(ipv6Str); err == nil && ip.Is6() { + return []net.IPAddr{{IP: ip.AsSlice()}}, nil + } + + // IP parsing failed, fallback to network + return r.fallback.LookupIPAddr(ctx, hostname) +} + +// LookupTXT delegates to the fallback resolver to support ACME DNS-01 challenges +// and any other TXT record lookups on p2p-forge domains. +func (r *p2pForgeResolver) LookupTXT(ctx context.Context, hostname string) ([]string, error) { + return r.fallback.LookupTXT(ctx, hostname) +} diff --git a/core/node/p2pforge_resolver_test.go b/core/node/p2pforge_resolver_test.go new file mode 100644 index 000000000..caa1b6409 --- /dev/null +++ b/core/node/p2pforge_resolver_test.go @@ -0,0 +1,172 @@ +package node + +import ( + "context" + "errors" + "net" + "testing" + + "github.com/ipfs/kubo/config" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// Test constants matching p2p-forge production format +const ( + // testPeerID is a valid peerID in CIDv1 base36 format as used by p2p-forge. + // Base36 is lowercase-only, making it safe for case-insensitive DNS. + // Corresponds to 12D3KooWDpJ7As7BWAwRMfu1VU2WCqNjvq387JEYKDBj4kx6nXTN in base58btc. + testPeerID = "k51qzi5uqu5dhnwe629wdlncpql6frppdpwnz4wtlcw816aysd5wwlk63g4wmh" + + // domainSuffix is the default p2p-forge domain used in tests. + domainSuffix = config.DefaultDomainSuffix +) + +// mockResolver implements madns.BasicResolver for testing +type mockResolver struct { + txtRecords map[string][]string + ipRecords map[string][]net.IPAddr + ipErr error +} + +func (m *mockResolver) LookupIPAddr(_ context.Context, hostname string) ([]net.IPAddr, error) { + if m.ipErr != nil { + return nil, m.ipErr + } + if m.ipRecords != nil { + return m.ipRecords[hostname], nil + } + return nil, nil +} + +func (m *mockResolver) LookupTXT(_ context.Context, name string) ([]string, error) { + if m.txtRecords != nil { + return m.txtRecords[name], nil + } + return nil, nil +} + +// newTestResolver creates a p2pForgeResolver with default suffix. +func newTestResolver(t *testing.T) *p2pForgeResolver { + t.Helper() + return NewP2PForgeResolver([]string{domainSuffix}, &mockResolver{}) +} + +// assertLookupIP verifies that hostname resolves to wantIP. +func assertLookupIP(t *testing.T, r *p2pForgeResolver, hostname, wantIP string) { + t.Helper() + addrs, err := r.LookupIPAddr(t.Context(), hostname) + require.NoError(t, err) + require.Len(t, addrs, 1) + assert.Equal(t, wantIP, addrs[0].IP.String()) +} + +func TestP2PForgeResolver_LookupIPAddr(t *testing.T) { + r := newTestResolver(t) + + tests := []struct { + name string + hostname string + wantIP string + }{ + // IPv4 + {"ipv4/basic", "192-168-1-1." + testPeerID + "." + domainSuffix, "192.168.1.1"}, + {"ipv4/zeros", "0-0-0-0." + testPeerID + "." + domainSuffix, "0.0.0.0"}, + {"ipv4/max", "255-255-255-255." + testPeerID + "." + domainSuffix, "255.255.255.255"}, + {"ipv4/trailing dot", "10-0-0-1." + testPeerID + "." + domainSuffix + ".", "10.0.0.1"}, + {"ipv4/uppercase suffix", "192-168-1-1." + testPeerID + ".LIBP2P.DIRECT", "192.168.1.1"}, + // IPv6 + {"ipv6/full", "2001-db8-0-0-0-0-0-1." + testPeerID + "." + domainSuffix, "2001:db8::1"}, + {"ipv6/compressed", "2001-db8--1." + testPeerID + "." + domainSuffix, "2001:db8::1"}, + {"ipv6/loopback", "0--1." + testPeerID + "." + domainSuffix, "::1"}, + {"ipv6/all zeros", "0--0." + testPeerID + "." + domainSuffix, "::"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assertLookupIP(t, r, tt.hostname, tt.wantIP) + }) + } +} + +func TestP2PForgeResolver_LookupIPAddr_MultipleSuffixes(t *testing.T) { + r := NewP2PForgeResolver([]string{domainSuffix, "custom.example.com"}, &mockResolver{}) + + tests := []struct { + hostname string + wantIP string + }{ + {"192-168-1-1." + testPeerID + "." + domainSuffix, "192.168.1.1"}, + {"10-0-0-1." + testPeerID + ".custom.example.com", "10.0.0.1"}, + } + + for _, tt := range tests { + t.Run(tt.hostname, func(t *testing.T) { + assertLookupIP(t, r, tt.hostname, tt.wantIP) + }) + } +} + +func TestP2PForgeResolver_LookupIPAddr_FallbackToNetwork(t *testing.T) { + fallbackIP := []net.IPAddr{{IP: net.ParseIP("93.184.216.34")}} + + tests := []struct { + name string + hostname string + }{ + {"peerID only", testPeerID + "." + domainSuffix}, + {"invalid peerID", "192-168-1-1.invalid-peer-id." + domainSuffix}, + {"invalid IP encoding", "not-an-ip." + testPeerID + "." + domainSuffix}, + {"leading hyphen", "-192-168-1-1." + testPeerID + "." + domainSuffix}, + {"too many parts", "extra.192-168-1-1." + testPeerID + "." + domainSuffix}, + {"wrong suffix", "192-168-1-1." + testPeerID + ".example.com"}, + } + + // Build fallback records from test cases + ipRecords := make(map[string][]net.IPAddr, len(tests)) + for _, tt := range tests { + ipRecords[tt.hostname] = fallbackIP + } + fallback := &mockResolver{ipRecords: ipRecords} + r := NewP2PForgeResolver([]string{domainSuffix}, fallback) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + addrs, err := r.LookupIPAddr(t.Context(), tt.hostname) + require.NoError(t, err) + require.Len(t, addrs, 1, "should fallback to network") + assert.Equal(t, "93.184.216.34", addrs[0].IP.String()) + }) + } +} + +func TestP2PForgeResolver_LookupIPAddr_FallbackError(t *testing.T) { + expectedErr := errors.New("network error") + r := NewP2PForgeResolver([]string{domainSuffix}, &mockResolver{ipErr: expectedErr}) + + // peerID-only triggers fallback, which returns error + _, err := r.LookupIPAddr(t.Context(), testPeerID+"."+domainSuffix) + require.ErrorIs(t, err, expectedErr) +} + +func TestP2PForgeResolver_LookupTXT(t *testing.T) { + t.Run("delegates to fallback for ACME DNS-01", func(t *testing.T) { + acmeHost := "_acme-challenge." + testPeerID + "." + domainSuffix + fallback := &mockResolver{ + txtRecords: map[string][]string{acmeHost: {"acme-token-value"}}, + } + r := NewP2PForgeResolver([]string{domainSuffix}, fallback) + + records, err := r.LookupTXT(t.Context(), acmeHost) + require.NoError(t, err) + assert.Equal(t, []string{"acme-token-value"}, records) + }) + + t.Run("returns empty when fallback has no records", func(t *testing.T) { + r := NewP2PForgeResolver([]string{domainSuffix}, &mockResolver{}) + + records, err := r.LookupTXT(t.Context(), "anything."+domainSuffix) + require.NoError(t, err) + assert.Empty(t, records) + }) +} diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index fd8c41b5b..bcdb6f0e2 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -19,6 +19,7 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [Improved `ipfs dag stat` output](#improved-ipfs-dag-stat-output) - [Skip bad keys when listing](#skip_bad_keys_when_listing) - [Accelerated DHT Client and Provide Sweep now work together](#accelerated-dht-client-and-provide-sweep-now-work-together) + - [๐ŸŒ No unnecessary DNS lookups for AutoTLS addresses](#-no-unnecessary-dns-lookups-for-autotls-addresses) - [โฑ๏ธ Configurable gateway request duration limit](#๏ธ-configurable-gateway-request-duration-limit) - [๐Ÿ”ง Recovery from corrupted MFS root](#-recovery-from-corrupted-mfs-root) - [๐Ÿ“‹ Long listing format for `ipfs ls`](#-long-listing-format-for-ipfs-ls) @@ -118,6 +119,12 @@ Change the `ipfs key list` behavior to log an error and continue listing keys wh Previously, provide operations could start before the Accelerated DHT Client discovered enough peers, causing sweep mode to lose its efficiency benefits. Now, providing waits for the initial network crawl (about 10 minutes). Your content will be properly distributed across DHT regions after initial DHT map is created. Check `ipfs provide stat` to see when providing begins. +#### ๐ŸŒ No unnecessary DNS lookups for AutoTLS addresses + +Kubo no longer makes DNS queries for [AutoTLS](https://blog.libp2p.io/autotls/) addresses like `1-2-3-4.peerid.libp2p.direct`. Since the IP is encoded in the hostname (`1-2-3-4` means `1.2.3.4`), Kubo extracts it locally. This reduces load on the public good DNS servers at `libp2p.direct` run by [Shipyard](https://ipshipyard.com), reserving them for web browsers which lack direct DNS access and must rely on the browser's resolver. + +To disable, set [`AutoTLS.SkipDNSLookup`](https://github.com/ipfs/kubo/blob/master/docs/config.md#autotlsskipdnslookup) to `false`. + #### โฑ๏ธ Configurable gateway request duration limit [`Gateway.MaxRequestDuration`](https://github.com/ipfs/kubo/blob/master/docs/config.md#gatewaymaxrequestduration) sets an absolute deadline for gateway requests. Unlike `RetrievalTimeout` (which resets on each data write and catches stalled transfers), this is a hard limit on the total time a request can take. diff --git a/docs/config.md b/docs/config.md index e6ab44d04..8c160f062 100644 --- a/docs/config.md +++ b/docs/config.md @@ -779,6 +779,22 @@ Default: `true` Type: `flag` +### `AutoTLS.SkipDNSLookup` + +Optional. Controls whether to skip network DNS lookups for [p2p-forge] domains like `*.libp2p.direct`. + +This applies to DNS resolution performed via [`DNS.Resolvers`](#dnsresolvers), including `/dns*` multiaddrs resolved by go-libp2p (e.g., peer addresses from DHT or delegated routing). + +When enabled (default), A/AAAA queries for hostnames matching [`AutoTLS.DomainSuffix`](#autotlsdomainsuffix) are resolved locally by parsing the IP address directly from the hostname (e.g., `1-2-3-4.peerID.libp2p.direct` resolves to `1.2.3.4` without network I/O). This avoids unnecessary DNS queries since the IP is already encoded in the hostname. + +If the hostname format is invalid (wrong peerID, malformed IP encoding), the resolver falls back to network DNS, ensuring forward compatibility with potential future DNS record types. + +Set to `false` to always use network DNS for these domains. This is primarily useful for debugging or if you need to override resolution behavior via [`DNS.Resolvers`](#dnsresolvers). + +Default: `true` + +Type: `flag` + ### `AutoTLS.DomainSuffix` Optional override of the parent domain suffix that will be used in DNS+TLS+WebSockets multiaddrs generated by [p2p-forge] client. @@ -3489,7 +3505,7 @@ Please remove this option from your config. ## `DNS` -Options for configuring DNS resolution for [DNSLink](https://docs.ipfs.tech/concepts/dnslink/) and `/dns*` [Multiaddrs][libp2p-multiaddrs]. +Options for configuring DNS resolution for [DNSLink](https://docs.ipfs.tech/concepts/dnslink/) and `/dns*` [Multiaddrs][libp2p-multiaddrs] (including peer addresses discovered via DHT or delegated routing). ### `DNS.Resolvers` @@ -3519,6 +3535,7 @@ Be mindful that: - The default catch-all resolver is the cleartext one provided by your operating system. It can be overridden by adding a DoH entry for the DNS root indicated by `.` as illustrated above. - Out-of-the-box support for selected non-ICANN TLDs relies on third-party centralized services provided by respective communities on best-effort basis. - The special value `"auto"` uses DNS resolvers from [AutoConf](#autoconf) when enabled. For example: `{".": "auto"}` uses any custom DoH resolver (global or per TLD) provided by AutoConf system. +- When [`AutoTLS.SkipDNSLookup`](#autotlsskipdnslookup) is enabled (default), domains matching [`AutoTLS.DomainSuffix`](#autotlsdomainsuffix) (default: `libp2p.direct`) are resolved locally by parsing the IP directly from the hostname. Set `AutoTLS.SkipDNSLookup=false` to force network DNS lookups for these domains. Default: `{".": "auto"}` diff --git a/test/cli/dns_resolvers_multiaddr_test.go b/test/cli/dns_resolvers_multiaddr_test.go new file mode 100644 index 000000000..b330004ea --- /dev/null +++ b/test/cli/dns_resolvers_multiaddr_test.go @@ -0,0 +1,143 @@ +package cli + +import ( + "strings" + "testing" + "time" + + "github.com/ipfs/kubo/config" + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// testDomainSuffix is the default p2p-forge domain used in tests +const testDomainSuffix = config.DefaultDomainSuffix // libp2p.direct + +// TestDNSResolversApplyToMultiaddr is a regression test for: +// https://github.com/ipfs/kubo/issues/9199 +// +// It verifies that DNS.Resolvers config is used when resolving /dnsaddr, +// /dns, /dns4, /dns6 multiaddrs during peer connections, not just for +// DNSLink resolution. +func TestDNSResolversApplyToMultiaddr(t *testing.T) { + t.Parallel() + + t.Run("invalid DoH resolver causes multiaddr resolution to fail", func(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init("--profile=test") + + // Set an invalid DoH resolver that will fail when used. + // If DNS.Resolvers is properly wired to multiaddr resolution, + // swarm connect to a /dnsaddr will fail with an error mentioning + // the invalid resolver URL. + invalidResolver := "https://invalid.broken.resolver.test/dns-query" + node.SetIPFSConfig("DNS.Resolvers", map[string]string{ + ".": invalidResolver, + }) + + // Clear bootstrap peers to prevent background connection attempts + node.SetIPFSConfig("Bootstrap", []string{}) + + node.StartDaemon() + defer node.StopDaemon() + + // Give daemon time to fully start + time.Sleep(2 * time.Second) + + // Verify daemon is responsive + result := node.RunIPFS("id") + require.Equal(t, 0, result.ExitCode(), "daemon should be responsive") + + // Try to connect to a /dnsaddr peer - this should fail because + // the DNS.Resolvers config points to an invalid DoH server + result = node.RunIPFS("swarm", "connect", "/dnsaddr/bootstrap.libp2p.io") + + // The connection should fail + require.NotEqual(t, 0, result.ExitCode(), + "swarm connect should fail when DNS.Resolvers points to invalid DoH server") + + // The error should mention the invalid resolver, proving DNS.Resolvers + // is being used for multiaddr resolution + stderr := result.Stderr.String() + assert.True(t, + strings.Contains(stderr, "invalid.broken.resolver.test") || + strings.Contains(stderr, "no such host") || + strings.Contains(stderr, "lookup") || + strings.Contains(stderr, "dial"), + "error should indicate DNS resolution failure using custom resolver. got: %s", stderr) + }) + + t.Run("libp2p.direct resolves locally even with broken DNS.Resolvers", func(t *testing.T) { + t.Parallel() + + h := harness.NewT(t) + nodes := h.NewNodes(2).Init("--profile=test") + + // Configure node0 with a broken DNS resolver + // This would break all DNS resolution if libp2p.direct wasn't resolved locally + invalidResolver := "https://invalid.broken.resolver.test/dns-query" + nodes[0].SetIPFSConfig("DNS.Resolvers", map[string]string{ + ".": invalidResolver, + }) + + // Clear bootstrap peers on both nodes + for _, n := range nodes { + n.SetIPFSConfig("Bootstrap", []string{}) + } + + nodes.StartDaemons() + defer nodes.StopDaemons() + + // Get node1's peer ID in base36 format (what p2p-forge uses in DNS hostnames) + // DNS is case-insensitive, and base36 is lowercase-only, making it ideal for DNS + idResult := nodes[1].RunIPFS("id", "--peerid-base", "base36", "-f", "") + require.Equal(t, 0, idResult.ExitCode()) + node1IDBase36 := strings.TrimSpace(idResult.Stdout.String()) + node1ID := nodes[1].PeerID().String() + node1Addrs := nodes[1].SwarmAddrs() + + // Find a TCP address we can use + var tcpAddr string + for _, addr := range node1Addrs { + addrStr := addr.String() + if strings.Contains(addrStr, "/tcp/") && strings.Contains(addrStr, "/ip4/127.0.0.1") { + tcpAddr = addrStr + break + } + } + require.NotEmpty(t, tcpAddr, "node1 should have a local TCP address") + + // Extract port from address like /ip4/127.0.0.1/tcp/12345/... + parts := strings.Split(tcpAddr, "/") + var port string + for i, p := range parts { + if p == "tcp" && i+1 < len(parts) { + port = parts[i+1] + break + } + } + require.NotEmpty(t, port, "should find TCP port in address") + + // Construct a libp2p.direct hostname that encodes 127.0.0.1 + // Format: /dns4/..libp2p.direct/tcp//p2p/ + // p2p-forge uses base36 peerIDs in DNS hostnames (lowercase, DNS-safe) + libp2pDirectAddr := "/dns4/127-0-0-1." + node1IDBase36 + "." + testDomainSuffix + "/tcp/" + port + "/p2p/" + node1ID + + // This connection should succeed because libp2p.direct is resolved locally + // even though DNS.Resolvers points to a broken server + result := nodes[0].RunIPFS("swarm", "connect", libp2pDirectAddr) + + // The connection should succeed - local resolution bypasses broken DNS + assert.Equal(t, 0, result.ExitCode(), + "swarm connect to libp2p.direct should succeed with local resolution. stderr: %s", + result.Stderr.String()) + + // Verify the connection was actually established + result = nodes[0].RunIPFS("swarm", "peers") + require.Equal(t, 0, result.ExitCode()) + assert.Contains(t, result.Stdout.String(), node1ID, + "node0 should be connected to node1") + }) +} From 9539b4d8b8509031a45c243fecbec6a4234aef17 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 30 Jan 2026 19:38:42 +0100 Subject: [PATCH 43/49] docs: cleanup broken links and outdated content (#11100) - docs/README.md: restructure to surface 20+ previously undiscoverable docs - docs/README.md: fix broken github-issue-guide.md link (file was removed) - docs/add-code-flow.md: rewrite with current code flow and mermaid diagrams - docs/customizing.md, docs/gateway.md: use specs.ipfs.tech URLs - README.md: fix orphan #nix anchor, use go.dev links, link to contributors graph - remove stale docs/AUTHORS and docs/generate-authors.sh (last updated 2016) --- docs/AUTHORS | 112 ------------------ docs/README.md | 63 +++++++---- docs/add-code-flow.md | 239 ++++++++++++++++++++++++++++----------- docs/customizing.md | 2 +- docs/generate-authors.sh | 12 -- 5 files changed, 215 insertions(+), 213 deletions(-) delete mode 100644 docs/AUTHORS delete mode 100755 docs/generate-authors.sh diff --git a/docs/AUTHORS b/docs/AUTHORS deleted file mode 100644 index 85a6e160c..000000000 --- a/docs/AUTHORS +++ /dev/null @@ -1,112 +0,0 @@ -# This file lists all individuals having contributed content to the repository. -# For how it is generated, see `docs/generate-authors.sh`. - -Aaron Hill -Adam Gashlin -Adrian Ulrich -Alex -anarcat -Andres Buritica -Andrew Chin -Andy Leap -Artem Andreenko -Baptiste Jonglez -Brendan Benshoof -Brendan Mc -Brian Tiger Chow -Caio Alonso -Carlos Cobo -Cayman Nava -Chas Leichner -Chris Grimmett -Chris P -Chris Sasarak -Christian Couder -Christian Kniep -Christopher Sasarak -David -David Braun -David Dias -David Wagner -dignifiedquire -Dominic Della Valle -Dominic Tarr -drathir -Dylan Powers -Emery Hemingway -epitron -Ethan Buchman -Etienne Laurin -Forrest Weston -Francesco Canessa -gatesvp -Giuseppe Bertone -Harlan T Wood -Hector Sanjuan -Henry -Ho-Sheng Hsiao -Jakub Sztandera -Jason Carver -Jonathan Dahan -Juan Batiz-Benet -Karthik Bala -Kevin Atkinson -Kevin Wallace -klauspost -Knut Ahlers -Konstantin Koroviev -kpcyrd -Kristoffer Strรถm -Lars Gierth -llSourcell -Marcin Janczyk -Marcin Rataj -Markus Amalthea Magnuson -michael -Michael Lovci -Michael Murรฉ -Michael Pfister -Mildred Ki'Lya -Muneeb Ali -Nick Hamann -palkeo -Patrick Connolly -Pavol Rusnak -Peter Borzov -Philip Nelson -Quinn Slack -ReadmeCritic -rht -Richard Littauer -Robert Carlsen -Roerick Sweeney -Sean Lang -SH -Shanti Bouchez-Mongardรฉ -Shaun Bruce -Simon Kirkby -Siraj Ravel -Siva Chandran -slothbag -sroerick -Stephan Seidt -Stephen Sugden -Stephen Whitmore -Steven Allen -Tarnay Kรกlmรกn -theswitch -Thomas Gardner -Tim Groeneveld -Tommi Virtanen -Tonis Tiigi -Tor Arne Vestbรธ -Travis Person -verokarhu -Vijayee Kulkaa -Vitor Baptista -vitzli -W. Trevor King -Whyrusleeping -wzhd -Yuval Langer -แดแด€แด›แด› ส™แด‡สŸสŸ diff --git a/docs/README.md b/docs/README.md index 244aa4846..a3777546d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,39 +1,58 @@ # Developer Documentation and Guides -If you are looking for User Documentation & Guides, please visit [docs.ipfs.tech](https://docs.ipfs.tech/) or check [General Documentation](#general-documentation). +If you're looking for User Documentation & Guides, visit [docs.ipfs.tech](https://docs.ipfs.tech/). -If youโ€™re experiencing an issue with IPFS, **please follow [our issue guide](github-issue-guide.md) when filing an issue!** +If you're experiencing an issue with IPFS, please [file an issue](https://github.com/ipfs/kubo/issues/new/choose) in this repository. -Otherwise, check out the following guides to using and developing IPFS: - -## General Documentation +## Configuration - [Configuration reference](config.md) - - [Datastore configuration](datastores.md) - - [Experimental features](experimental-features.md) + - [Datastore configuration](datastores.md) + - [Experimental features](experimental-features.md) +- [Environment variables](environment-variables.md) -## Developing `kubo` +## Running Kubo + +- [Gateway configuration](gateway.md) +- [Delegated routing](delegated-routing.md) +- [Content blocking](content-blocking.md) (for public node operators) +- [libp2p resource management](libp2p-resource-management.md) +- [Mounting IPFS with FUSE](fuse.md) + +## Metrics & Monitoring + +- [Prometheus metrics](metrics.md) +- [Telemetry plugin](telemetry.md) +- [Provider statistics](provide-stats.md) +- [Performance debugging](debug-guide.md) + +## Development - **[Developer Guide](developer-guide.md)** - prerequisites, build, test, and contribute - Contributing Guidelines [for IPFS projects](https://github.com/ipfs/community/blob/master/CONTRIBUTING.md) and for [Go code specifically](https://github.com/ipfs/community/blob/master/CONTRIBUTING_GO.md) -- Building on [Windows](windows.md) -- [Performance Debugging Guidelines](debug-guide.md) -- [Release Checklist](releases.md) +- [Building on Windows](windows.md) +- [Customizing Kubo](customizing.md) +- [Installing plugins](plugins.md) +- [Release checklist](releases.md) ## Guides -- [How to Implement an API Client](implement-api-bindings.md) -- [Connecting with Websockets](transports.md) โ€” if you want `js-ipfs` nodes in web browsers to connect to your `kubo` node, you will need to turn on websocket support in your `kubo` node. +- [Transferring files over IPFS](file-transfer.md) +- [How to implement an API client](implement-api-bindings.md) +- [HTTP/RPC clients](http-rpc-clients.md) +- [Websocket transports](transports.md) +- [Command completion](command-completion.md) -## Advanced User Guides +## Production -- [Transferring a File Over IPFS](file-transfer.md) -- [Installing command completion](command-completion.md) -- [Mounting IPFS with FUSE](fuse.md) -- [Installing plugins](plugins.md) -- [Setting up an IPFS Gateway](https://github.com/ipfs/kubo/blob/master/docs/gateway.md) +- [Reverse proxy setup](production/reverse-proxy.md) -## Other +## Specifications -- [Thanks to all our contributors โค๏ธ](AUTHORS) (We use the `generate-authors.sh` script to regenerate this list.) -- [How to file a GitHub Issue](github-issue-guide.md) +- [Repository structure](specifications/repository.md) +- [Filesystem datastore](specifications/repository_fs.md) +- [Keystore](specifications/keystore.md) + +## Examples + +- [Kubo as a library](examples/kubo-as-a-library/README.md) diff --git a/docs/add-code-flow.md b/docs/add-code-flow.md index 353d47166..0cdba3e8f 100644 --- a/docs/add-code-flow.md +++ b/docs/add-code-flow.md @@ -1,102 +1,209 @@ -# IPFS : The `Add` command demystified +# How `ipfs add` Works -The goal of this document is to capture the code flow for adding a file (see the `coreapi` package) using the IPFS CLI, in the process exploring some data structures and packages like `ipld.Node` (aka `dagnode`), `FSNode`, `MFS`, etc. +This document explains what happens when you run `ipfs add` to import files into IPFS. Understanding this flow helps when debugging, optimizing imports, or building applications on top of IPFS. -## Concepts -- [Files](https://github.com/ipfs/docs/issues/133) +- [The Big Picture](#the-big-picture) +- [Try It Yourself](#try-it-yourself) +- [Step by Step](#step-by-step) + - [Step 1: Chunking](#step-1-chunking) + - [Step 2: Building the DAG](#step-2-building-the-dag) + - [Step 3: Storing Blocks](#step-3-storing-blocks) + - [Step 4: Pinning](#step-4-pinning) + - [Alternative: Organizing with MFS](#alternative-organizing-with-mfs) +- [Options](#options) +- [UnixFS Format](#unixfs-format) +- [Code Architecture](#code-architecture) + - [Key Files](#key-files) + - [The Adder](#the-adder) +- [Further Reading](#further-reading) ---- +## The Big Picture -**Try this yourself** -> -> ``` -> # Convert a file to the IPFS format. -> echo "Hello World" > new-file -> ipfs add new-file -> added QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u new-file -> 12 B / 12 B [=========================================================] 100.00% -> -> # Add a file to the MFS. -> NEW_FILE_HASH=$(ipfs add new-file -Q) -> ipfs files cp /ipfs/$NEW_FILE_HASH /new-file -> -> # Get information from the file in MFS. -> ipfs files stat /new-file -> # QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u -> # Size: 12 -> # CumulativeSize: 20 -> # ChildBlocks: 0 -> # Type: file -> -> # Retrieve the contents. -> ipfs files read /new-file -> # Hello World -> ``` +When you add a file to IPFS, three main things happen: -## Code Flow +1. **Chunking** - The file is split into smaller pieces +2. **DAG Building** - Those pieces are organized into a tree structure (a [Merkle DAG](https://docs.ipfs.tech/concepts/merkle-dag/)) +3. **Pinning** - The root of the tree is pinned so it persists in your local node -**[`UnixfsAPI.Add()`](https://github.com/ipfs/go-ipfs/blob/v0.4.18/core/coreapi/unixfs.go#L31)** - *Entrypoint into the `Unixfs` package* +The result is a Content Identifier (CID) - a hash that uniquely identifies your content and can be used to retrieve it from anywhere in the IPFS network. -The `UnixfsAPI.Add()` acts on the input data or files, to build a _merkledag_ node (in essence it is the entire tree represented by the root node) and adds it to the _blockstore_. -Within the function, a new `Adder` is created with the configured `Blockstore` and __DAG service__`. +```mermaid +flowchart LR + A["Your File
(bytes)"] --> B["Chunker
(split data)"] + B --> C["DAG Builder
(tree)"] + C --> D["CID
(hash)"] +``` -- **[`adder.AddAllAndPin(files)`](https://github.com/ipfs/go-ipfs/blob/v0.4.18/core/coreunix/add.go#L403)** - *Entrypoint to the `Add` logic* - encapsulates a lot of the underlying functionality that will be investigated in the following sections. +## Try It Yourself - Our focus will be on the simplest case, a single file, handled by `Adder.addFile(file files.File)`. +```bash +# Add a simple file +echo "Hello World" > hello.txt +ipfs add hello.txt +# added QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u hello.txt - - **[`adder.addFile(file files.File)`](https://github.com/ipfs/go-ipfs/blob/v0.4.18/core/coreunix/add.go#L450)** - *Create the _DAG_ and add to `MFS`* +# See what's inside +ipfs cat QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u +# Hello World - The `addFile(file)` method takes the data and converts it into a __DAG__ tree and adds the root of the tree into the `MFS`. +# View the DAG structure +ipfs dag get QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u +``` - https://github.com/ipfs/go-ipfs/blob/v0.4.18/core/coreunix/add.go#L508-L521 +## Step by Step - There are two main methods to focus on - +### Step 1: Chunking - 1. **[`adder.add(io.Reader)`](https://github.com/ipfs/go-ipfs/blob/v0.4.18/core/coreunix/add.go#L115)** - *Create and return the **root** __DAG__ node* +Big files are split into chunks because: - This method converts the input data (`io.Reader`) to a __DAG__ tree, by splitting the data into _chunks_ using the `Chunker` and organizing them into a __DAG__ (with a *trickle* or *balanced* layout. See [balanced](https://github.com/ipfs/go-unixfs/blob/6b769632e7eb8fe8f302e3f96bf5569232e7a3ee/importer/balanced/builder.go) for more info). +- Large files need to be broken down for efficient transfer +- Identical chunks across files are stored only once (deduplication) +- You can fetch parts of a file without downloading the whole thing - The method returns the **root** `ipld.Node` of the __DAG__. +**Chunking strategies** (set with `--chunker`): - 2. **[`adder.addNode(ipld.Node, path)`](https://github.com/ipfs/go-ipfs/blob/v0.4.18/core/coreunix/add.go#L366)** - *Add **root** __DAG__ node to the `MFS`* +| Strategy | Description | Best For | +|----------|-------------|----------| +| `size-N` | Fixed size chunks | General use | +| `rabin` | Content-defined chunks using rolling hash | Deduplication across similar files | +| `buzhash` | Alternative content-defined chunking | Similar to rabin | - Now that we have the **root** node of the `DAG`, this needs to be added to the `MFS` file system. - Fetch (or create, if doesn't already exist) the `MFS` **root** using `mfsRoot()`. +See `ipfs add --help` for current defaults, or [Import](config.md#import) for making them permanent. - > NOTE: The `MFS` **root** is an ephemeral root, created and destroyed solely for the `add` functionality. +Content-defined chunking (rabin/buzhash) finds natural boundaries in the data. This means if you edit the middle of a file, only the changed chunks need to be re-stored - the rest can be deduplicated. - Assuming the directory already exists in the MFS file system, (if it doesn't exist it will be created using `mfs.Mkdir()`), the **root** __DAG__ node is added to the `MFS` File system using the `mfs.PutNode()` function. +### Step 2: Building the DAG - - **[MFS] [`PutNode(mfs.Root, path, ipld.Node)`](https://github.com/ipfs/go-mfs/blob/v0.1.18/ops.go#L86)** - *Insert node at path into given `MFS`* +Each chunk becomes a leaf node in a tree. If a file has many chunks, intermediate nodes group them together. This creates a Merkle DAG (Directed Acyclic Graph) where: - The `path` param is used to determine the `MFS Directory`, which is first looked up in the `MFS` using `lookupDir()` function. This is followed by adding the **root** __DAG__ node (`ipld.Node`) into this `Directory` using `directory.AddChild()` method. +- Each node is identified by a hash of its contents +- Parent nodes contain links (hashes) to their children +- The root node's hash becomes the file's CID - - **[MFS] Add Child To `UnixFS`** - - **[`directory.AddChild(filename, ipld.Node)`](https://github.com/ipfs/go-mfs/blob/v0.1.18/dir.go#L350)** - *Add **root** __DAG__ node under this directory* +**Layout strategies**: - Within this method the node is added to the `Directory`'s __DAG service__ using the `dserv.Add()` method, followed by adding the **root** __DAG__ node with the given name, in the `directory.addUnixFSChild(directory.child{name, ipld.Node})` method. +**Balanced layout** (default): - - **[MFS] [`directory.addUnixFSChild(child)`](https://github.com/ipfs/go-mfs/blob/v0.1.18/dir.go#L375)** - *Add child to inner UnixFS Directory* +```mermaid +graph TD + Root --> Node1[Node] + Root --> Node2[Node] + Node1 --> Leaf1[Leaf] + Node1 --> Leaf2[Leaf] + Node2 --> Leaf3[Leaf] +``` - The node is then added as a child to the inner `UnixFS` directory using the `(BasicDirectory).AddChild()` method. +All leaves at similar depth. Good for random access - you can jump to any part of the file efficiently. - > NOTE: This is not to be confused with the `directory.AddChild(filename, ipld.Node)`, as this operates on the `UnixFS` `BasicDirectory` object. +**Trickle layout** (`--trickle`): - - **[UnixFS] [`(BasicDirectory).AddChild(ctx, name, ipld.Node)`](https://github.com/ipfs/go-unixfs/blob/v1.1.16/io/directory.go#L137)** - *Add child to `BasicDirectory`* +```mermaid +graph TD + Root --> Leaf1[Leaf] + Root --> Node1[Node] + Root --> Node2[Node] + Node1 --> Leaf2[Leaf] + Node2 --> Leaf3[Leaf] +``` - > IMPORTANT: It should be noted that the `BasicDirectory` object uses the `ProtoNode` type object which is an implementation of the `ipld.Node` interface, seen and used throughout this document. Ideally the `ipld.Node` should always be used, unless we need access to specific functions from `ProtoNode` (like `Copy()`) that are not available in the interface. +Leaves added progressively. Good for streaming - you can start reading before the whole file is added. - This method first attempts to remove any old links (`ProtoNode.RemoveNodeLink(name)`) to the `ProtoNode` prior to adding a link to the newly added `ipld.Node`, using `ProtoNode.AddNodeLink(name, ipld.Node)`. +### Step 3: Storing Blocks - - **[Merkledag] [`AddNodeLink()`](https://github.com/ipfs/go-merkledag/blob/v1.1.15/node.go#L99)** +As the DAG is built, each node is stored in the blockstore: - The `AddNodeLink()` method is where an `ipld.Link` is created with the `ipld.Node`'s `CID` and size in the `ipld.MakeLink(ipld.Node)` method, and is then appended to the `ProtoNode`'s links in the `ProtoNode.AddRawLink(name)` method. +- **Normal mode**: Data is copied into IPFS's internal storage (`~/.ipfs/blocks/`) +- **Filestore mode** (`--nocopy`): Only references to the original file are stored (saves disk space but the original file must remain in place) - - **[`adder.Finalize()`](https://github.com/ipfs/go-ipfs/blob/v0.4.18/core/coreunix/add.go#L200)** - *Fetch and return the __DAG__ **root** from the `MFS` and `UnixFS` directory* +### Step 4: Pinning - The `Finalize` method returns the `ipld.Node` from the `UnixFS` `Directory`. +By default, added content is pinned (`ipfs add --pin=true`). This tells your IPFS node to keep this data - without pinning, content may eventually be removed to free up space. - - **[`adder.PinRoot()`](https://github.com/ipfs/go-ipfs/blob/v0.4.18/core/coreunix/add.go#L171)** - *Pin all files under the `MFS` **root*** +### Alternative: Organizing with MFS - The whole process ends with `PinRoot` recursively pinning all the files under the `MFS` **root** +Instead of pinning, you can use the [Mutable File System (MFS)](https://docs.ipfs.tech/concepts/file-systems/#mutable-file-system-mfs) to organize content using familiar paths like `/photos/vacation.jpg` instead of raw CIDs: + +```bash +# Add directly to MFS path +ipfs add --to-files=/backups/ myfile.txt + +# Or copy an existing CID into MFS +ipfs files cp /ipfs/QmWATWQ7fVPP2EFGu71UkfnqhYXDYH566qy47CnJDgvs8u /docs/hello.txt +``` + +Content in MFS is implicitly pinned and stays organized across node restarts. + +## Options + +Run `ipfs add --help` to see all available options for controlling chunking, DAG layout, CID format, pinning behavior, and more. + +## UnixFS Format + +IPFS uses [UnixFS](https://specs.ipfs.tech/unixfs/) to represent files and directories. UnixFS is an abstraction layer that: + +- Gives names to raw data blobs (so you can have `/foo/bar.txt` instead of just hashes) +- Represents directories as lists of named links to other nodes +- Organizes large files as trees of smaller chunks +- Makes these structures cryptographically verifiable - any tampering is detectable because it would change the hashes + +With `--raw-leaves`, leaf nodes store raw data without the UnixFS wrapper. This is more efficient and is the default when using CIDv1. + +## Code Architecture + +The add flow spans several layers: + +```mermaid +flowchart TD + subgraph CLI ["CLI Layer (kubo)"] + A["core/commands/add.go
parses flags, shows progress"] + end + subgraph API ["CoreAPI Layer (kubo)"] + B["core/coreapi/unixfs.go
UnixfsAPI.Add() entry point"] + end + subgraph Adder ["Adder (kubo)"] + C["core/coreunix/add.go
orchestrates chunking, DAG building, MFS, pinning"] + end + subgraph Boxo ["boxo libraries"] + D["chunker/ - splits data into chunks"] + E["ipld/unixfs/ - DAG layout and UnixFS format"] + F["mfs/ - mutable filesystem abstraction"] + G["pinning/ - pin management"] + H["blockstore/ - block storage"] + end + A --> B --> C --> Boxo +``` + +### Key Files + +| Component | Location | +|-----------|----------| +| CLI command | `core/commands/add.go` | +| API implementation | `core/coreapi/unixfs.go` | +| Adder logic | `core/coreunix/add.go` | +| Chunking | [boxo/chunker](https://github.com/ipfs/boxo/tree/main/chunker) | +| DAG layouts | [boxo/ipld/unixfs/importer](https://github.com/ipfs/boxo/tree/main/ipld/unixfs/importer) | +| MFS | [boxo/mfs](https://github.com/ipfs/boxo/tree/main/mfs) | +| Pinning | [boxo/pinning/pinner](https://github.com/ipfs/boxo/tree/main/pinning/pinner) | + +### The Adder + +The `Adder` type in `core/coreunix/add.go` is the workhorse. It: + +1. **Creates an MFS root** - temporary in-memory filesystem for building the DAG +2. **Processes files recursively** - chunks each file and builds DAG nodes +3. **Commits to blockstore** - persists all blocks +4. **Pins the result** - keeps content from being removed +5. **Returns the root CID** + +Key methods: + +- `AddAllAndPin()` - main entry point +- `addFileNode()` - handles a single file or directory +- `add()` - chunks data and builds the DAG using boxo's layout builders + +## Further Reading + +- [UnixFS specification](https://specs.ipfs.tech/unixfs/) +- [IPLD and Merkle DAGs](https://docs.ipfs.tech/concepts/merkle-dag/) +- [Pinning](https://docs.ipfs.tech/concepts/persistence/) +- [MFS (Mutable File System)](https://docs.ipfs.tech/concepts/file-systems/#mutable-file-system-mfs) diff --git a/docs/customizing.md b/docs/customizing.md index 0f078999f..f1e726763 100644 --- a/docs/customizing.md +++ b/docs/customizing.md @@ -45,7 +45,7 @@ This gives a more Go-centric dependency updating flow to building a new binary w ## Bespoke Extension Points Certain Kubo functionality may have their own extension points. For example: -* Kubo supports the [Routing v1](https://github.com/ipfs/specs/blob/main/routing/ROUTING_V1_HTTP.md) API for delegating content routing to external processes +* Kubo supports the [Routing v1](https://specs.ipfs.tech/routing/http-routing-v1/) API for delegating content routing to external processes * Kubo supports the [Pinning Service API](https://github.com/ipfs/pinning-services-api-spec) for delegating pinning to external processes * Kubo supports [DNSLink](https://dnslink.dev/) for delegating name->CID mappings to DNS diff --git a/docs/generate-authors.sh b/docs/generate-authors.sh deleted file mode 100755 index 75b33b7e0..000000000 --- a/docs/generate-authors.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -set -e - -# see also ".mailmap" for how email addresses and names are deduplicated - - -cat >AUTHORS <<-'EOF' -# This file lists all individuals having contributed content to the repository. -# For how it is generated, see `docs/generate-authors.sh`. - -EOF -git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf >>AUTHORS From 59b5d6ab4da8d7a2bebaa2e1eb7b82cbce476503 Mon Sep 17 00:00:00 2001 From: Vedant Madane <6527493+VedantMadane@users.noreply.github.com> Date: Sat, 31 Jan 2026 00:09:26 +0530 Subject: [PATCH 44/49] feat(key): add 'ipfs key ls' as alias for 'ipfs key list' (#11147) * feat(key): add 'ipfs key ls' as alias for 'ipfs key list' Add 'ls' as an alias for the 'list' subcommand in 'ipfs key' to be consistent with other ipfs commands like 'ipfs repo ls' and 'ipfs pin ls' which use 'ls' instead of 'list'. Fixes #10976 Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com> * feat(key): make 'ipfs key ls' canonical, deprecate 'list' aligns with other commands like 'ipfs pin ls' and 'ipfs files ls'. 'ipfs key list' still works but shows deprecation warning. * fix(key): correct --key option description in verify command was copy-pasted from sign command and said "signing" instead of "verifying" --------- Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com> Co-authored-by: Marcin Rataj --- client/rpc/key.go | 2 +- core/commands/commands_test.go | 1 + core/commands/keystore.go | 20 ++++++++++++++++---- docs/changelogs/v0.40.md | 8 +++++--- 4 files changed, 23 insertions(+), 8 deletions(-) diff --git a/client/rpc/key.go b/client/rpc/key.go index 710d9fb06..a38c0962a 100644 --- a/client/rpc/key.go +++ b/client/rpc/key.go @@ -101,7 +101,7 @@ func (api *KeyAPI) List(ctx context.Context) ([]iface.Key, error) { var out struct { Keys []keyOutput } - if err := api.core().Request("key/list").Exec(ctx, &out); err != nil { + if err := api.core().Request("key/ls").Exec(ctx, &out); err != nil { return nil, err } diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index 49e359e24..d4bad9601 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -106,6 +106,7 @@ func TestCommands(t *testing.T) { "/key/gen", "/key/import", "/key/list", + "/key/ls", "/key/rename", "/key/rm", "/key/rotate", diff --git a/core/commands/keystore.go b/core/commands/keystore.go index 6ce1b5a0d..afcdb62db 100644 --- a/core/commands/keystore.go +++ b/core/commands/keystore.go @@ -38,9 +38,9 @@ publish'. > ipfs key gen --type=rsa --size=2048 mykey > ipfs name publish --key=mykey QmSomeHash -'ipfs key list' lists the available keys. +'ipfs key ls' lists the available keys. - > ipfs key list + > ipfs key ls self mykey `, @@ -49,7 +49,8 @@ publish'. "gen": keyGenCmd, "export": keyExportCmd, "import": keyImportCmd, - "list": keyListCmd, + "list": keyListDeprecatedCmd, + "ls": keyListCmd, "rename": keyRenameCmd, "rm": keyRmCmd, "rotate": keyRotateCmd, @@ -488,6 +489,17 @@ var keyListCmd = &cmds.Command{ Type: KeyOutputList{}, } +var keyListDeprecatedCmd = &cmds.Command{ + Status: cmds.Deprecated, + Helptext: cmds.HelpText{ + Tagline: "Deprecated: use 'ipfs key ls' instead.", + }, + Options: keyListCmd.Options, + Run: keyListCmd.Run, + Encoders: keyListCmd.Encoders, + Type: keyListCmd.Type, +} + const ( keyStoreForceOptionName = "force" ) @@ -773,7 +785,7 @@ the signed payload is always prefixed with "libp2p-key signed message:". `, }, Options: []cmds.Option{ - cmds.StringOption("key", "k", "The name of the key to use for signing."), + cmds.StringOption("key", "k", "The name of the key to use for verifying."), cmds.StringOption("signature", "s", "Multibase-encoded signature to verify."), ke.OptionIPNSBase, }, diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index bcdb6f0e2..127e93c73 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -17,7 +17,7 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [New `ipfs diag datastore` commands](#new-ipfs-diag-datastore-commands) - [๐Ÿš‡ Improved `ipfs p2p` tunnels with foreground mode](#-improved-ipfs-p2p-tunnels-with-foreground-mode) - [Improved `ipfs dag stat` output](#improved-ipfs-dag-stat-output) - - [Skip bad keys when listing](#skip_bad_keys_when_listing) + - [๐Ÿ”‘ `ipfs key` improvements](#-ipfs-key-improvements) - [Accelerated DHT Client and Provide Sweep now work together](#accelerated-dht-client-and-provide-sweep-now-work-together) - [๐ŸŒ No unnecessary DNS lookups for AutoTLS addresses](#-no-unnecessary-dns-lookups-for-autotls-addresses) - [โฑ๏ธ Configurable gateway request duration limit](#๏ธ-configurable-gateway-request-duration-limit) @@ -111,9 +111,11 @@ Ratio: 1.500000 Use `--progress=true` to force progress even when piped, or `--progress=false` to disable it. -#### Skip bad keys when listing +#### ๐Ÿ”‘ `ipfs key` improvements -Change the `ipfs key list` behavior to log an error and continue listing keys when a key cannot be read from the keystore or decoded. +`ipfs key ls` is now the canonical command for listing keys, matching `ipfs pin ls` and `ipfs files ls`. The old `ipfs key list` still works but is deprecated. + +Listing also became more resilient: bad keys are now skipped with an error log instead of failing the entire operation. #### Accelerated DHT Client and Provide Sweep now work together From 77ed3dd0ef485bc421f6975c43f6d745717a48f1 Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Fri, 30 Jan 2026 14:41:55 -0800 Subject: [PATCH 45/49] feat(rpc): Content-Type headers and IPNS record get/put (#11067) * fix http header when compress enabled for get command Closes #2376 * fix(rpc): set Content-Type for ipfs get based on output format - set application/x-tar when outputting tar (default and --archive) - set application/gzip when compression is enabled (--compress) - update go-ipfs-cmds with Tar encoding type and RFC 6713 compliant MIME types (application/gzip instead of application/x-gzip) * test(rpc): add Content-Type header tests for ipfs get * feat(rpc): add Content-Type headers for binary responses set proper Content-Type headers for RPC endpoints that return binary data: - `dag export`: application/vnd.ipld.car - `block get`: application/vnd.ipld.raw - `diag profile`: application/zip - `get`: application/x-tar or application/gzip (already worked, migrated to new API) uses the new OctetStream encoding type and SetContentType() method from go-ipfs-cmds to specify custom MIME types for binary responses. refs: https://github.com/ipfs/kubo/issues/2376 * feat(rpc): add `ipfs name get` command for IPNS record retrieval add dedicated command to retrieve raw signed IPNS records from the routing system. returns protobuf-encoded IPNS record with Content-Type `application/vnd.ipfs.ipns-record`. this provides a more convenient alternative to `ipfs routing get /ipns/` which returns JSON with base64-encoded data. the raw output can be piped directly to `ipfs name inspect`: ipfs name get | ipfs name inspect spec: https://specs.ipfs.tech/ipns/ipns-record/ * feat(rpc): add `ipfs name put` command for IPNS record storage adds `ipfs name put` to complement `ipfs name get`, allowing users to store IPNS records obtained from external sources without needing the private key. useful for backup, restore, and debugging workflows. the command validates records by default (signature, sequence number). use `--force` to bypass validation for testing how routing handles malformed or outdated records. also reorganizes test/cli files: - rename http_rpc_* -> rpc_* to match existing convention - merge name_get_put_test.go into name_test.go - add file header comments documenting test purposes * chore(deps): update go-ipfs-cmds to latest master includes SetContentType() for dynamic Content-Type headers --------- Co-authored-by: Marcin Rataj --- core/commands/block.go | 5 + core/commands/commands_test.go | 2 + core/commands/dag/dag.go | 3 + core/commands/dag/export.go | 2 + core/commands/extra.go | 4 +- core/commands/get.go | 13 + core/commands/name/name.go | 294 ++++++++++++- core/commands/profile.go | 5 + docs/changelogs/v0.40.md | 28 ++ docs/examples/kubo-as-a-library/go.mod | 2 +- docs/examples/kubo-as-a-library/go.sum | 4 +- go.mod | 2 +- go.sum | 4 +- test/cli/name_test.go | 550 +++++++++++++++++++++++++ test/cli/rpc_content_type_test.go | 167 ++++++++ test/cli/rpc_get_output_test.go | 74 ++++ test/dependencies/go.mod | 2 +- test/dependencies/go.sum | 4 +- 18 files changed, 1144 insertions(+), 21 deletions(-) create mode 100644 test/cli/rpc_content_type_test.go create mode 100644 test/cli/rpc_get_output_test.go diff --git a/core/commands/block.go b/core/commands/block.go index b4b0fd204..1402a8531 100644 --- a/core/commands/block.go +++ b/core/commands/block.go @@ -98,6 +98,9 @@ var blockGetCmd = &cmds.Command{ 'ipfs block get' is a plumbing command for retrieving raw IPFS blocks. It takes a , and outputs the block to stdout. `, + HTTP: &cmds.HTTPHelpText{ + ResponseContentType: "application/vnd.ipld.raw", + }, }, Arguments: []cmds.Argument{ @@ -119,6 +122,8 @@ It takes a , and outputs the block to stdout. return err } + res.SetEncodingType(cmds.OctetStream) + res.SetContentType("application/vnd.ipld.raw") return res.Emit(r) }, } diff --git a/core/commands/commands_test.go b/core/commands/commands_test.go index d4bad9601..2573d4cd5 100644 --- a/core/commands/commands_test.go +++ b/core/commands/commands_test.go @@ -124,12 +124,14 @@ func TestCommands(t *testing.T) { "/multibase/transcode", "/multibase/list", "/name", + "/name/get", "/name/inspect", "/name/publish", "/name/pubsub", "/name/pubsub/cancel", "/name/pubsub/state", "/name/pubsub/subs", + "/name/put", "/name/resolve", "/object", "/object/data", diff --git a/core/commands/dag/dag.go b/core/commands/dag/dag.go index caf7a5474..73eeddd94 100644 --- a/core/commands/dag/dag.go +++ b/core/commands/dag/dag.go @@ -276,6 +276,9 @@ Note that at present only single root selections / .car files are supported. The output of blocks happens in strict DAG-traversal, first-seen, order. CAR file follows the CARv1 format: https://ipld.io/specs/transport/car/carv1/ `, + HTTP: &cmds.HTTPHelpText{ + ResponseContentType: "application/vnd.ipld.car", + }, }, Arguments: []cmds.Argument{ cmds.StringArg("root", true, false, "CID of a root to recursively export").EnableStdin(), diff --git a/core/commands/dag/export.go b/core/commands/dag/export.go index 9f11c43de..48223f860 100644 --- a/core/commands/dag/export.go +++ b/core/commands/dag/export.go @@ -73,6 +73,8 @@ func dagExport(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment }() + res.SetEncodingType(cmds.OctetStream) + res.SetContentType("application/vnd.ipld.car") if err := res.Emit(pipeR); err != nil { pipeR.Close() // ignore the error if any return err diff --git a/core/commands/extra.go b/core/commands/extra.go index e70fb029a..12b02eabc 100644 --- a/core/commands/extra.go +++ b/core/commands/extra.go @@ -1,6 +1,8 @@ package commands -import cmds "github.com/ipfs/go-ipfs-cmds" +import ( + cmds "github.com/ipfs/go-ipfs-cmds" +) func CreateCmdExtras(opts ...func(e *cmds.Extra)) *cmds.Extra { e := new(cmds.Extra) diff --git a/core/commands/get.go b/core/commands/get.go index 12a3ea8ca..804836b9a 100644 --- a/core/commands/get.go +++ b/core/commands/get.go @@ -45,6 +45,9 @@ To output a TAR archive instead of unpacked files, use '--archive' or '-a'. To compress the output with GZIP compression, use '--compress' or '-C'. You may also specify the level of compression by specifying '-l=<1-9>'. `, + HTTP: &cmds.HTTPHelpText{ + ResponseContentType: "application/x-tar, or application/gzip when compress=true", + }, }, Arguments: []cmds.Argument{ @@ -103,6 +106,16 @@ may also specify the level of compression by specifying '-l=<1-9>'. reader.Close() }() + // Set Content-Type based on output format. + // When compression is enabled, output is gzip (or tar.gz for directories). + // Otherwise, tar is used as the transport format. + res.SetEncodingType(cmds.OctetStream) + if cmplvl != gzip.NoCompression { + res.SetContentType("application/gzip") + } else { + res.SetContentType("application/x-tar") + } + return res.Emit(reader) }, PostRun: cmds.PostRunMap{ diff --git a/core/commands/name/name.go b/core/commands/name/name.go index 912629d68..73d540f62 100644 --- a/core/commands/name/name.go +++ b/core/commands/name/name.go @@ -3,15 +3,18 @@ package name import ( "bytes" "encoding/hex" + "errors" "fmt" "io" + "strings" "text/tabwriter" "time" "github.com/ipfs/boxo/ipns" ipns_pb "github.com/ipfs/boxo/ipns/pb" cmds "github.com/ipfs/go-ipfs-cmds" - cmdenv "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/commands/cmdenv" + "github.com/ipfs/kubo/core/coreiface/options" "google.golang.org/protobuf/proto" ) @@ -42,29 +45,30 @@ Examples: Publish an with your default name: - > ipfs name publish /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - Published to QmbCMUZw6JFeZ7Wp9jkzbye3Fzp2GGcPgC3nmeUjfVF87n: /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + > ipfs name publish /ipfs/bafkreifjjcie6lypi6ny7amxnfftagclbuxndqonfipmb64f2km2devei4 + Published to k51qzi5uqu5dgklc20hksmmzhoy5lfrn5xcnryq6xp4r50b5yc0vnivpywfu9p: /ipfs/bafk... Publish an with another name, added by an 'ipfs key' command: - > ipfs key gen --type=rsa --size=2048 mykey - > ipfs name publish --key=mykey /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy - Published to QmSrPmbaUKA3ZodhzPWZnpFgcPMFWF4QsxXbkWfEptTBJd: /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + > ipfs key gen --type=ed25519 mykey + k51qzi5uqu5dlz49qkb657myg6f1buu6rauv8c6b489a9i1e4dkt7a3yo9j2wr + > ipfs name publish --key=mykey /ipfs/bafkreifjjcie6lypi6ny7amxnfftagclbuxndqonfipmb64f2km2devei4 + Published to k51qzi5uqu5dlz49qkb657myg6f1buu6rauv8c6b489a9i1e4dkt7a3yo9j2wr: /ipfs/bafk... Resolve the value of your name: > ipfs name resolve - /ipfs/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy + /ipfs/bafk... Resolve the value of another name: - > ipfs name resolve QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ - /ipfs/QmSiTko9JZyabH56y2fussEt1A5oDqsFXB3CkvAqraFryz + > ipfs name resolve k51qzi5uqu5dlz49qkb657myg6f1buu6rauv8c6b489a9i1e4dkt7a3yo9j2wr + /ipfs/bafk... Resolve the value of a dnslink: - > ipfs name resolve ipfs.io - /ipfs/QmaBvfZooxWkrv7D3r8LS9moNjzD2o525XMZze69hhoxf5 + > ipfs name resolve specs.ipfs.tech + /ipfs/bafy... `, }, @@ -74,6 +78,8 @@ Resolve the value of a dnslink: "resolve": IpnsCmd, "pubsub": IpnsPubsubCmd, "inspect": IpnsInspectCmd, + "get": IpnsGetCmd, + "put": IpnsPutCmd, }, } @@ -123,6 +129,9 @@ in Multibase. The Data field is DAG-CBOR represented as DAG-JSON. Passing --verify will verify signature against provided public key. `, + HTTP: &cmds.HTTPHelpText{ + Description: "Request body should be `multipart/form-data` with the IPNS record bytes.", + }, }, Arguments: []cmds.Argument{ cmds.FileArg("record", true, false, "The IPNS record payload to be verified.").EnableStdin(), @@ -267,3 +276,266 @@ Passing --verify will verify signature against provided public key. }), }, } + +var IpnsGetCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Retrieve a signed IPNS record.", + ShortDescription: ` +Retrieves the signed IPNS record for a given name from the routing system. + +The output is the raw IPNS record (protobuf) as defined in the IPNS spec: +https://specs.ipfs.tech/ipns/ipns-record/ + +The record can be inspected with 'ipfs name inspect': + + ipfs name get | ipfs name inspect + +This is equivalent to 'ipfs routing get /ipns/' but only accepts +IPNS names (not arbitrary routing keys). + +Note: The routing system returns the "best" IPNS record it knows about. +For IPNS, "best" means the record with the highest sequence number. +If multiple records exist (e.g., after using 'ipfs name put'), this command +returns the one the routing system considers most current. +`, + HTTP: &cmds.HTTPHelpText{ + ResponseContentType: "application/vnd.ipfs.ipns-record", + }, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("name", true, false, "The IPNS name to look up."), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + api, err := cmdenv.GetApi(env, req) + if err != nil { + return err + } + + // Normalize the argument: accept both "k51..." and "/ipns/k51..." + name := req.Arguments[0] + if !strings.HasPrefix(name, "/ipns/") { + name = "/ipns/" + name + } + + data, err := api.Routing().Get(req.Context, name) + if err != nil { + return err + } + + res.SetEncodingType(cmds.OctetStream) + res.SetContentType("application/vnd.ipfs.ipns-record") + return res.Emit(bytes.NewReader(data)) + }, +} + +const ( + forceOptionName = "force" + putAllowOfflineOption = "allow-offline" + allowDelegatedOption = "allow-delegated" + maxIPNSRecordSize = 10 << 10 // 10 KiB per IPNS spec +) + +var errPutAllowOffline = errors.New("can't put while offline: pass `--allow-offline` to store locally or `--allow-delegated` if Ipns.DelegatedPublishers are set up") + +var IpnsPutCmd = &cmds.Command{ + Status: cmds.Experimental, + Helptext: cmds.HelpText{ + Tagline: "Store a pre-signed IPNS record in the routing system.", + ShortDescription: ` +Stores a pre-signed IPNS record in the routing system. + +This command accepts a raw IPNS record (protobuf) as defined in the IPNS spec: +https://specs.ipfs.tech/ipns/ipns-record/ + +The record must be signed by the private key corresponding to the IPNS name. +Use 'ipfs name get' to retrieve records and 'ipfs name inspect' to examine. +`, + LongDescription: ` +Stores a pre-signed IPNS record in the routing system. + +This command accepts a raw IPNS record (protobuf) as defined in the IPNS spec: +https://specs.ipfs.tech/ipns/ipns-record/ + +The record must be signed by the private key corresponding to the IPNS name. +Use 'ipfs name get' to retrieve records and 'ipfs name inspect' to examine. + +Use Cases: + + - Re-publishing third-party records: store someone else's signed record + - Cross-node sync: import records exported from another node + - Backup/restore: export with 'name get', restore with 'name put' + +Validation: + +By default, the command validates that: + + - The record is a valid IPNS record (protobuf) + - The record size is within 10 KiB limit + - The signature matches the provided IPNS name + - The record's sequence number is higher than any existing record + +The --force flag skips this command's validation and passes the record +directly to the routing system. Note that --force only affects this command; +it does not control how the routing system handles the record. The routing +system may still reject invalid records or prefer records with higher sequence +numbers. Use --force primarily for testing (e.g., to observe how the routing +system reacts to incorrectly signed or malformed records). + +Important: Even after a successful 'name put', a subsequent 'name get' may +return a different record if one with a higher sequence number exists. +This is expected IPNS behavior, not a bug. + +Publishing Modes: + +By default, IPNS records are published to both the DHT and any configured +HTTP delegated publishers. You can control this behavior with: + + --allow-offline Store locally without requiring network connectivity + --allow-delegated Publish via HTTP delegated publishers only (no DHT) + +Examples: + +Export and re-import a record: + + > ipfs name get k51... > record.bin + > ipfs name put k51... record.bin + +Store a record received from someone else: + + > ipfs name put k51... third-party-record.bin + +Force store a record to test routing validation: + + > ipfs name put --force k51... possibly-invalid-record.bin +`, + HTTP: &cmds.HTTPHelpText{ + Description: "Request body should be `multipart/form-data` with the IPNS record bytes.", + }, + }, + Arguments: []cmds.Argument{ + cmds.StringArg("name", true, false, "The IPNS name to store the record for (e.g., k51... or /ipns/k51...)."), + cmds.FileArg("record", true, false, "Path to file containing the signed IPNS record.").EnableStdin(), + }, + Options: []cmds.Option{ + cmds.BoolOption(forceOptionName, "f", "Skip validation (signature, sequence, size)."), + cmds.BoolOption(putAllowOfflineOption, "Store locally without broadcasting to the network."), + cmds.BoolOption(allowDelegatedOption, "Publish via HTTP delegated publishers only (no DHT)."), + }, + Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error { + nd, err := cmdenv.GetNode(env) + if err != nil { + return err + } + + api, err := cmdenv.GetApi(env, req) + if err != nil { + return err + } + + // Parse options + force, _ := req.Options[forceOptionName].(bool) + allowOffline, _ := req.Options[putAllowOfflineOption].(bool) + allowDelegated, _ := req.Options[allowDelegatedOption].(bool) + + // Validate flag combinations + if allowOffline && allowDelegated { + return errors.New("cannot use both --allow-offline and --allow-delegated flags") + } + + // Handle different publishing modes + if allowDelegated { + // AllowDelegated mode: check if delegated publishers are configured + cfg, err := nd.Repo.Config() + if err != nil { + return fmt.Errorf("failed to read config: %w", err) + } + delegatedPublishers := cfg.DelegatedPublishersWithAutoConf() + if len(delegatedPublishers) == 0 { + return errors.New("no delegated publishers configured: add Ipns.DelegatedPublishers or use --allow-offline for local-only publishing") + } + // For allow-delegated mode, we proceed even if offline + // since we're using HTTP publishing via delegated publishers + } + + // Parse the IPNS name argument + nameArg := req.Arguments[0] + if !strings.HasPrefix(nameArg, "/ipns/") { + nameArg = "/ipns/" + nameArg + } + // Extract the name part after /ipns/ + namePart := strings.TrimPrefix(nameArg, "/ipns/") + name, err := ipns.NameFromString(namePart) + if err != nil { + return fmt.Errorf("invalid IPNS name: %w", err) + } + + // Read raw record bytes from file/stdin + file, err := cmdenv.GetFileArg(req.Files.Entries()) + if err != nil { + return err + } + defer file.Close() + + // Read record data (limit to 1 MiB for memory safety) + data, err := io.ReadAll(io.LimitReader(file, 1<<20)) + if err != nil { + return fmt.Errorf("failed to read record: %w", err) + } + if len(data) == 0 { + return errors.New("record is empty") + } + + // Validate unless --force + if !force { + // Check size limit per IPNS spec + if len(data) > maxIPNSRecordSize { + return fmt.Errorf("record exceeds maximum size of %d bytes, use --force to skip size check", maxIPNSRecordSize) + } + rec, err := ipns.UnmarshalRecord(data) + if err != nil { + return fmt.Errorf("invalid IPNS record: %w", err) + } + + // Validate signature against provided name + err = ipns.ValidateWithName(rec, name) + if err != nil { + return fmt.Errorf("record validation failed: %w", err) + } + + // Check for sequence conflicts with existing record + existingData, err := api.Routing().Get(req.Context, nameArg) + if err == nil { + // We have an existing record, check sequence + existingRec, parseErr := ipns.UnmarshalRecord(existingData) + if parseErr == nil { + existingSeq, seqErr := existingRec.Sequence() + newSeq, newSeqErr := rec.Sequence() + if seqErr == nil && newSeqErr == nil { + if existingSeq >= newSeq { + return fmt.Errorf("existing record has sequence %d >= new record sequence %d, use --force to overwrite", existingSeq, newSeq) + } + } + } + } + // If Get fails (no existing record), that's fine - proceed with put + } + + // Publish the original bytes as-is + // When allowDelegated is true, we set allowOffline to allow the operation + // even without DHT connectivity (delegated publishers use HTTP) + opts := []options.RoutingPutOption{ + options.Routing.AllowOffline(allowOffline || allowDelegated), + } + + err = api.Routing().Put(req.Context, nameArg, data, opts...) + if err != nil { + if err.Error() == "can't put while offline" { + return errPutAllowOffline + } + return err + } + + return nil + }, +} diff --git a/core/commands/profile.go b/core/commands/profile.go index 9f54e0612..b230c873e 100644 --- a/core/commands/profile.go +++ b/core/commands/profile.go @@ -70,6 +70,9 @@ However, it could reveal: - Memory offsets of various data structures. - Any modifications you've made to go-ipfs. `, + HTTP: &cmds.HTTPHelpText{ + ResponseContentType: "application/zip", + }, }, NoLocal: true, Options: []cmds.Option{ @@ -121,6 +124,8 @@ However, it could reveal: archive.Close() _ = w.CloseWithError(err) }() + res.SetEncodingType(cmds.OctetStream) + res.SetContentType("application/zip") return res.Emit(r) }, PostRun: cmds.PostRunMap{ diff --git a/docs/changelogs/v0.40.md b/docs/changelogs/v0.40.md index 127e93c73..d3538aaac 100644 --- a/docs/changelogs/v0.40.md +++ b/docs/changelogs/v0.40.md @@ -22,6 +22,8 @@ This release was brought to you by the [Shipyard](https://ipshipyard.com/) team. - [๐ŸŒ No unnecessary DNS lookups for AutoTLS addresses](#-no-unnecessary-dns-lookups-for-autotls-addresses) - [โฑ๏ธ Configurable gateway request duration limit](#๏ธ-configurable-gateway-request-duration-limit) - [๐Ÿ”ง Recovery from corrupted MFS root](#-recovery-from-corrupted-mfs-root) + - [๐Ÿ“ก RPC `Content-Type` headers for binary responses](#-rpc-content-type-headers-for-binary-responses) + - [๐Ÿ”– New `ipfs name get|put` commands](#-new-ipfs-name-getput-commands) - [๐Ÿ“‹ Long listing format for `ipfs ls`](#-long-listing-format-for-ipfs-ls) - [๐Ÿ“ฆ๏ธ Dependency updates](#-dependency-updates) - [๐Ÿ“ Changelog](#-changelog) @@ -149,6 +151,32 @@ $ ipfs files chroot --confirm QmYourBackupCID See `ipfs files chroot --help` for details. +#### ๐Ÿ“ก RPC `Content-Type` headers for binary responses + +HTTP RPC endpoints that return binary data now set appropriate `Content-Type` headers, making it easier to integrate with HTTP clients and tooling that rely on MIME types. On CLI these commands behave the same as before, but over HTTP RPC you now get proper headers: + +| Endpoint | Content-Type | +|------------------------|-------------------------------------------| +| `/api/v0/get` | `application/x-tar` or `application/gzip` | +| `/api/v0/dag/export` | `application/vnd.ipld.car` | +| `/api/v0/block/get` | `application/vnd.ipld.raw` | +| `/api/v0/name/get` | `application/vnd.ipfs.ipns-record` | +| `/api/v0/diag/profile` | `application/zip` | + +#### ๐Ÿ”– New `ipfs name get|put` commands + +You can now backup, restore, and share IPNS records without needing the private key. + +```console +$ ipfs name get /ipns/k51... > record.bin +$ ipfs name get /ipns/k51... | ipfs name inspect +$ ipfs name put k51... record.bin +``` + +These are low-level tools primarily for debugging and testing IPNS. + +The `put` command validates records by default. Use `--force` to skip validation and test how routing systems handle malformed or outdated records. Note that `--force` only bypasses this command's checks; the routing system may still reject invalid records. + #### ๐Ÿ“‹ Long listing format for `ipfs ls` The `ipfs ls` command now supports `--long` (`-l`) flag for displaying Unix-style file permissions and modification times. This works with files added using `--preserve-mode` and `--preserve-mtime`. See `ipfs ls --help` for format details and examples. diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index c109c37f6..1a3d9aeb5 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -85,7 +85,7 @@ require ( github.com/ipfs/go-ds-pebble v0.5.9 // indirect github.com/ipfs/go-dsqueue v0.1.2 // indirect github.com/ipfs/go-fs-lock v0.1.1 // indirect - github.com/ipfs/go-ipfs-cmds v0.15.0 // indirect + github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 // indirect github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect github.com/ipfs/go-ipfs-pq v0.0.4 // indirect github.com/ipfs/go-ipfs-redirects-file v0.1.2 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 1a50e42b4..ca60866cc 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -303,8 +303,8 @@ github.com/ipfs/go-dsqueue v0.1.2 h1:jBMsgvT9Pj9l3cqI0m5jYpW/aWDYkW4Us6EuzrcSGbs github.com/ipfs/go-dsqueue v0.1.2/go.mod h1:OU94YuMVUIF/ctR7Ysov9PI4gOa2XjPGN9nd8imSv78= github.com/ipfs/go-fs-lock v0.1.1 h1:TecsP/Uc7WqYYatasreZQiP9EGRy4ZnKoG4yXxR33nw= github.com/ipfs/go-fs-lock v0.1.1/go.mod h1:2goSXMCw7QfscHmSe09oXiR34DQeUdm+ei+dhonqly0= -github.com/ipfs/go-ipfs-cmds v0.15.0 h1:nQDgKadrzyiFyYoZMARMIoVoSwe3gGTAfGvrWLeAQbQ= -github.com/ipfs/go-ipfs-cmds v0.15.0/go.mod h1:VABf/mv/wqvYX6hLG6Z+40eNAEw3FQO0bSm370Or3Wk= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 h1:l1DaJI5/+uOKdmvYrXwN3j/zOApLr8EBB0IGMTB7UaM= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1/go.mod h1:YmhRbpaLKg40i9Ogj2+L41tJ+8x50fF8u1FJJD/WNhc= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= diff --git a/go.mod b/go.mod index a4a5c4ecb..cb38a63ce 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/ipfs/go-ds-measure v0.2.2 github.com/ipfs/go-ds-pebble v0.5.9 github.com/ipfs/go-fs-lock v0.1.1 - github.com/ipfs/go-ipfs-cmds v0.15.0 + github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 github.com/ipfs/go-ipld-cbor v0.2.1 github.com/ipfs/go-ipld-format v0.6.3 github.com/ipfs/go-ipld-git v0.1.1 diff --git a/go.sum b/go.sum index 475c95728..562bd85a6 100644 --- a/go.sum +++ b/go.sum @@ -374,8 +374,8 @@ github.com/ipfs/go-dsqueue v0.1.2 h1:jBMsgvT9Pj9l3cqI0m5jYpW/aWDYkW4Us6EuzrcSGbs github.com/ipfs/go-dsqueue v0.1.2/go.mod h1:OU94YuMVUIF/ctR7Ysov9PI4gOa2XjPGN9nd8imSv78= github.com/ipfs/go-fs-lock v0.1.1 h1:TecsP/Uc7WqYYatasreZQiP9EGRy4ZnKoG4yXxR33nw= github.com/ipfs/go-fs-lock v0.1.1/go.mod h1:2goSXMCw7QfscHmSe09oXiR34DQeUdm+ei+dhonqly0= -github.com/ipfs/go-ipfs-cmds v0.15.0 h1:nQDgKadrzyiFyYoZMARMIoVoSwe3gGTAfGvrWLeAQbQ= -github.com/ipfs/go-ipfs-cmds v0.15.0/go.mod h1:VABf/mv/wqvYX6hLG6Z+40eNAEw3FQO0bSm370Or3Wk= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 h1:l1DaJI5/+uOKdmvYrXwN3j/zOApLr8EBB0IGMTB7UaM= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1/go.mod h1:YmhRbpaLKg40i9Ogj2+L41tJ+8x50fF8u1FJJD/WNhc= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= diff --git a/test/cli/name_test.go b/test/cli/name_test.go index cf5df2bb0..c9ce0ac26 100644 --- a/test/cli/name_test.go +++ b/test/cli/name_test.go @@ -1,3 +1,7 @@ +// Tests for `ipfs name` CLI commands. +// - TestName: tests name publish, resolve, and inspect +// - TestNameGetPut: tests name get and put for raw IPNS record handling + package cli import ( @@ -5,6 +9,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "strings" "testing" @@ -337,3 +342,548 @@ func TestName(t *testing.T) { require.Contains(t, res.Stdout.String(), publishPath2, "New content should now be published") }) } + +func TestNameGetPut(t *testing.T) { + t.Parallel() + + const ( + fixturePath = "fixtures/TestName.car" + fixtureCid = "bafybeidg3uxibfrt7uqh7zd5yaodetik7wjwi4u7rwv2ndbgj6ec7lsv2a" + ) + + makeDaemon := func(t *testing.T, daemonArgs ...string) *harness.Node { + node := harness.NewT(t).NewNode().Init("--profile=test") + r, err := os.Open(fixturePath) + require.NoError(t, err) + defer r.Close() + err = node.IPFSDagImport(r, fixtureCid) + require.NoError(t, err) + return node.StartDaemon(daemonArgs...) + } + + // makeKey creates a unique IPNS key for a test and returns the IPNS name + makeKey := func(t *testing.T, node *harness.Node, keyName string) ipns.Name { + res := node.IPFS("key", "gen", "--type=ed25519", keyName) + keyID := strings.TrimSpace(res.Stdout.String()) + name, err := ipns.NameFromString(keyID) + require.NoError(t, err) + return name + } + + // makeExternalRecord creates an IPNS record on an ephemeral node that is + // shut down before returning. This ensures the test node has no local + // knowledge of the record, properly testing put/get functionality. + // We use short --lifetime so if IPNS records from tests get published on + // the public DHT, they won't waste storage for long. + makeExternalRecord := func(t *testing.T, h *harness.Harness, publishPath string, publishArgs ...string) (ipns.Name, []byte) { + node := h.NewNode().Init("--profile=test") + + r, err := os.Open(fixturePath) + require.NoError(t, err) + defer r.Close() + err = node.IPFSDagImport(r, fixtureCid) + require.NoError(t, err) + + node.StartDaemon() + + res := node.IPFS("key", "gen", "--type=ed25519", "ephemeral-key") + keyID := strings.TrimSpace(res.Stdout.String()) + ipnsName, err := ipns.NameFromString(keyID) + require.NoError(t, err) + + args := []string{"name", "publish", "--key=ephemeral-key", "--lifetime=5m"} + args = append(args, publishArgs...) + args = append(args, publishPath) + node.IPFS(args...) + + res = node.IPFS("name", "get", ipnsName.String()) + record := res.Stdout.Bytes() + require.NotEmpty(t, record) + + node.StopDaemon() + + return ipnsName, record + } + + t.Run("name get retrieves IPNS record", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t) + defer node.StopDaemon() + + publishPath := "/ipfs/" + fixtureCid + ipnsName := makeKey(t, node, "testkey") + + // publish a record first + node.IPFS("name", "publish", "--key=testkey", "--lifetime=5m", publishPath) + + // retrieve the record using name get + res := node.IPFS("name", "get", ipnsName.String()) + record := res.Stdout.Bytes() + require.NotEmpty(t, record, "expected non-empty IPNS record") + + // verify the record is valid by inspecting it + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--verify="+ipnsName.String()) + require.Contains(t, res.Stdout.String(), "Valid: true") + require.Contains(t, res.Stdout.String(), publishPath) + }) + + t.Run("name get accepts /ipns/ prefix", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t) + defer node.StopDaemon() + + publishPath := "/ipfs/" + fixtureCid + ipnsName := makeKey(t, node, "testkey") + + node.IPFS("name", "publish", "--key=testkey", "--lifetime=5m", publishPath) + + // retrieve with /ipns/ prefix + res := node.IPFS("name", "get", "/ipns/"+ipnsName.String()) + record := res.Stdout.Bytes() + require.NotEmpty(t, record) + + // verify the record + res = node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--verify="+ipnsName.String()) + require.Contains(t, res.Stdout.String(), "Valid: true") + }) + + t.Run("name get fails for non-existent name", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t) + defer node.StopDaemon() + + // try to get a record for a random peer ID that doesn't exist + res := node.RunIPFS("name", "get", "12D3KooWRirYjmmQATx2kgHBfky6DADsLP7ex1t7BRxJ6nqLs9WH") + require.Error(t, res.Err) + require.NotEqual(t, 0, res.ExitCode()) + }) + + t.Run("name get fails for invalid name format", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t) + defer node.StopDaemon() + + res := node.RunIPFS("name", "get", "not-a-valid-ipns-name") + require.Error(t, res.Err) + require.NotEqual(t, 0, res.ExitCode()) + }) + + t.Run("name put accepts /ipns/ prefix", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t) + defer node.StopDaemon() + + publishPath := "/ipfs/" + fixtureCid + ipnsName := makeKey(t, node, "testkey") + + node.IPFS("name", "publish", "--key=testkey", "--lifetime=5m", publishPath) + + res := node.IPFS("name", "get", ipnsName.String()) + record := res.Stdout.Bytes() + + // put with /ipns/ prefix + res = node.PipeToIPFS(bytes.NewReader(record), "name", "put", "--force", "/ipns/"+ipnsName.String()) + require.NoError(t, res.Err) + }) + + t.Run("name put fails for invalid name format", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t) + defer node.StopDaemon() + + // create a dummy file + recordFile := filepath.Join(node.Dir, "dummy.bin") + err := os.WriteFile(recordFile, []byte("dummy"), 0644) + require.NoError(t, err) + + res := node.RunIPFS("name", "put", "not-a-valid-ipns-name", recordFile) + require.Error(t, res.Err) + require.Contains(t, res.Stderr.String(), "invalid IPNS name") + }) + + t.Run("name put rejects oversized record", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t) + defer node.StopDaemon() + + ipnsName := makeKey(t, node, "testkey") + + // create a file larger than 10 KiB + oversizedRecord := make([]byte, 11*1024) + recordFile := filepath.Join(node.Dir, "oversized.bin") + err := os.WriteFile(recordFile, oversizedRecord, 0644) + require.NoError(t, err) + + res := node.RunIPFS("name", "put", ipnsName.String(), recordFile) + require.Error(t, res.Err) + require.Contains(t, res.Stderr.String(), "exceeds maximum size") + }) + + t.Run("name put --force skips size check", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t) + defer node.StopDaemon() + + ipnsName := makeKey(t, node, "testkey") + + // create a file larger than 10 KiB + oversizedRecord := make([]byte, 11*1024) + recordFile := filepath.Join(node.Dir, "oversized.bin") + err := os.WriteFile(recordFile, oversizedRecord, 0644) + require.NoError(t, err) + + // with --force, size check is skipped (but routing will likely reject it) + res := node.RunIPFS("name", "put", "--force", ipnsName.String(), recordFile) + // the command itself should not fail on size, but routing may reject + // we just verify it doesn't fail with "exceeds maximum size" + if res.Err != nil { + require.NotContains(t, res.Stderr.String(), "exceeds maximum size") + } + }) + + t.Run("name put stores IPNS record", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + publishPath := "/ipfs/" + fixtureCid + + // create a record on an ephemeral node (shut down before test node starts) + ipnsName, record := makeExternalRecord(t, h, publishPath) + + // start test node (has no local knowledge of the record) + node := makeDaemon(t) + defer node.StopDaemon() + + // put the record (should succeed since no existing record) + recordFile := filepath.Join(node.Dir, "record.bin") + err := os.WriteFile(recordFile, record, 0644) + require.NoError(t, err) + + res := node.RunIPFS("name", "put", ipnsName.String(), recordFile) + require.NoError(t, res.Err) + + // verify the record was stored by getting it back + res = node.IPFS("name", "get", ipnsName.String()) + retrievedRecord := res.Stdout.Bytes() + require.Equal(t, record, retrievedRecord, "stored record should match original") + }) + + t.Run("name put with --force overwrites existing record", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + publishPath := "/ipfs/" + fixtureCid + + // create a record on an ephemeral node + ipnsName, record := makeExternalRecord(t, h, publishPath) + + // start test node + node := makeDaemon(t) + defer node.StopDaemon() + + // first put the record normally + recordFile := filepath.Join(node.Dir, "record.bin") + err := os.WriteFile(recordFile, record, 0644) + require.NoError(t, err) + + res := node.RunIPFS("name", "put", ipnsName.String(), recordFile) + require.NoError(t, res.Err) + + // now try to put the same record again (should fail - same sequence) + res = node.RunIPFS("name", "put", ipnsName.String(), recordFile) + require.Error(t, res.Err) + require.Contains(t, res.Stderr.String(), "existing record has sequence") + + // put the record with --force (should succeed) + res = node.RunIPFS("name", "put", "--force", ipnsName.String(), recordFile) + require.NoError(t, res.Err) + }) + + t.Run("name put validates signature against name", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + publishPath := "/ipfs/" + fixtureCid + + // create a record on an ephemeral node + _, record := makeExternalRecord(t, h, publishPath) + + // start test node + node := makeDaemon(t) + defer node.StopDaemon() + + // write the record to a file + recordFile := filepath.Join(node.Dir, "record.bin") + err := os.WriteFile(recordFile, record, 0644) + require.NoError(t, err) + + // try to put with a wrong name (should fail validation) + wrongName := "12D3KooWRirYjmmQATx2kgHBfky6DADsLP7ex1t7BRxJ6nqLs9WH" + res := node.RunIPFS("name", "put", wrongName, recordFile) + require.Error(t, res.Err) + require.Contains(t, res.Stderr.String(), "record validation failed") + }) + + t.Run("name put with --force skips command validation", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + publishPath := "/ipfs/" + fixtureCid + + // create a record on an ephemeral node + ipnsName, record := makeExternalRecord(t, h, publishPath) + + // start test node + node := makeDaemon(t) + defer node.StopDaemon() + + // with --force the command skips its own validation (signature, sequence check) + // and passes the record directly to the routing layer + res := node.PipeToIPFS(bytes.NewReader(record), "name", "put", "--force", ipnsName.String()) + require.NoError(t, res.Err) + }) + + t.Run("name put rejects empty record", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t) + defer node.StopDaemon() + + ipnsName := makeKey(t, node, "testkey") + + // create an empty file + recordFile := filepath.Join(node.Dir, "empty.bin") + err := os.WriteFile(recordFile, []byte{}, 0644) + require.NoError(t, err) + + res := node.RunIPFS("name", "put", ipnsName.String(), recordFile) + require.Error(t, res.Err) + require.Contains(t, res.Stderr.String(), "record is empty") + }) + + t.Run("name put rejects invalid record", func(t *testing.T) { + t.Parallel() + node := makeDaemon(t) + defer node.StopDaemon() + + ipnsName := makeKey(t, node, "testkey") + + // create a file with garbage data + recordFile := filepath.Join(node.Dir, "garbage.bin") + err := os.WriteFile(recordFile, []byte("not a valid ipns record"), 0644) + require.NoError(t, err) + + res := node.RunIPFS("name", "put", ipnsName.String(), recordFile) + require.Error(t, res.Err) + require.Contains(t, res.Stderr.String(), "invalid IPNS record") + }) + + t.Run("name put accepts stdin", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + publishPath := "/ipfs/" + fixtureCid + + // create a record on an ephemeral node + ipnsName, record := makeExternalRecord(t, h, publishPath) + + // start test node (has no local knowledge of the record) + node := makeDaemon(t) + defer node.StopDaemon() + + // put via stdin (no --force needed since no existing record) + res := node.PipeToIPFS(bytes.NewReader(record), "name", "put", ipnsName.String()) + require.NoError(t, res.Err) + }) + + t.Run("name put fails when offline without --allow-offline", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + publishPath := "/ipfs/" + fixtureCid + + // create a record on an ephemeral node + ipnsName, record := makeExternalRecord(t, h, publishPath) + + // write the record to a file + recordFile := filepath.Join(h.Dir, "record.bin") + err := os.WriteFile(recordFile, record, 0644) + require.NoError(t, err) + + // start test node in offline mode + node := h.NewNode().Init("--profile=test") + node.StartDaemon("--offline") + defer node.StopDaemon() + + // try to put without --allow-offline (should fail) + res := node.RunIPFS("name", "put", ipnsName.String(), recordFile) + require.Error(t, res.Err) + // error can come from our command or from the routing layer + stderr := res.Stderr.String() + require.True(t, strings.Contains(stderr, "offline") || strings.Contains(stderr, "online mode"), + "expected offline-related error, got: %s", stderr) + }) + + t.Run("name put succeeds with --allow-offline", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + publishPath := "/ipfs/" + fixtureCid + + // create a record on an ephemeral node + ipnsName, record := makeExternalRecord(t, h, publishPath) + + // write the record to a file + recordFile := filepath.Join(h.Dir, "record.bin") + err := os.WriteFile(recordFile, record, 0644) + require.NoError(t, err) + + // start test node in offline mode + node := h.NewNode().Init("--profile=test") + node.StartDaemon("--offline") + defer node.StopDaemon() + + // put with --allow-offline (should succeed, no --force needed since no existing record) + res := node.RunIPFS("name", "put", "--allow-offline", ipnsName.String(), recordFile) + require.NoError(t, res.Err) + }) + + t.Run("name get/put round trip preserves record bytes", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + publishPath := "/ipfs/" + fixtureCid + + // create a record on an ephemeral node + ipnsName, originalRecord := makeExternalRecord(t, h, publishPath) + + // start test node (has no local knowledge of the record) + node := makeDaemon(t) + defer node.StopDaemon() + + // put the record + res := node.PipeToIPFS(bytes.NewReader(originalRecord), "name", "put", ipnsName.String()) + require.NoError(t, res.Err) + + // get the record back + res = node.IPFS("name", "get", ipnsName.String()) + retrievedRecord := res.Stdout.Bytes() + + // the records should be byte-for-byte identical + require.Equal(t, originalRecord, retrievedRecord, "record bytes should be preserved after get/put round trip") + }) + + t.Run("name put --force allows storing lower sequence record", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + publishPath := "/ipfs/" + fixtureCid + + // create an ephemeral node to generate two records with different sequences + ephNode := h.NewNode().Init("--profile=test") + + r, err := os.Open(fixturePath) + require.NoError(t, err) + err = ephNode.IPFSDagImport(r, fixtureCid) + r.Close() + require.NoError(t, err) + + ephNode.StartDaemon() + + res := ephNode.IPFS("key", "gen", "--type=ed25519", "ephemeral-key") + keyID := strings.TrimSpace(res.Stdout.String()) + ipnsName, err := ipns.NameFromString(keyID) + require.NoError(t, err) + + // publish record with sequence 100 + ephNode.IPFS("name", "publish", "--key=ephemeral-key", "--lifetime=5m", "--sequence=100", publishPath) + res = ephNode.IPFS("name", "get", ipnsName.String()) + record100 := res.Stdout.Bytes() + + // publish record with sequence 200 + ephNode.IPFS("name", "publish", "--key=ephemeral-key", "--lifetime=5m", "--sequence=200", publishPath) + res = ephNode.IPFS("name", "get", ipnsName.String()) + record200 := res.Stdout.Bytes() + + ephNode.StopDaemon() + + // start test node (has no local knowledge of the records) + node := makeDaemon(t) + defer node.StopDaemon() + + // helper to get sequence from record + getSequence := func(record []byte) uint64 { + res := node.PipeToIPFS(bytes.NewReader(record), "name", "inspect", "--enc=json") + var result name.IpnsInspectResult + err := json.Unmarshal(res.Stdout.Bytes(), &result) + require.NoError(t, err) + require.NotNil(t, result.Entry.Sequence) + return *result.Entry.Sequence + } + + // verify we have the right records + require.Equal(t, uint64(100), getSequence(record100)) + require.Equal(t, uint64(200), getSequence(record200)) + + // put record with sequence 200 first + res = node.PipeToIPFS(bytes.NewReader(record200), "name", "put", ipnsName.String()) + require.NoError(t, res.Err) + + // verify current record has sequence 200 + res = node.IPFS("name", "get", ipnsName.String()) + require.Equal(t, uint64(200), getSequence(res.Stdout.Bytes())) + + // now put the lower sequence record (100) with --force + // this should succeed (--force bypasses our sequence check) + res = node.PipeToIPFS(bytes.NewReader(record100), "name", "put", "--force", ipnsName.String()) + require.NoError(t, res.Err, "putting lower sequence record with --force should succeed") + + // note: when we get the record, IPNS resolution returns the "best" record + // (highest sequence), so we'll get the sequence 200 record back + // this is expected IPNS behavior - the put succeeded, but get returns the best record + res = node.IPFS("name", "get", ipnsName.String()) + retrievedSeq := getSequence(res.Stdout.Bytes()) + require.Equal(t, uint64(200), retrievedSeq, "IPNS get returns the best (highest sequence) record") + }) + + t.Run("name put sequence conflict detection", func(t *testing.T) { + t.Parallel() + h := harness.NewT(t) + publishPath := "/ipfs/" + fixtureCid + + // create an ephemeral node to generate two records with different sequences + ephNode := h.NewNode().Init("--profile=test") + + r, err := os.Open(fixturePath) + require.NoError(t, err) + err = ephNode.IPFSDagImport(r, fixtureCid) + r.Close() + require.NoError(t, err) + + ephNode.StartDaemon() + + res := ephNode.IPFS("key", "gen", "--type=ed25519", "ephemeral-key") + keyID := strings.TrimSpace(res.Stdout.String()) + ipnsName, err := ipns.NameFromString(keyID) + require.NoError(t, err) + + // publish record with sequence 100 + ephNode.IPFS("name", "publish", "--key=ephemeral-key", "--lifetime=5m", "--sequence=100", publishPath) + res = ephNode.IPFS("name", "get", ipnsName.String()) + record100 := res.Stdout.Bytes() + + // publish record with sequence 200 + ephNode.IPFS("name", "publish", "--key=ephemeral-key", "--lifetime=5m", "--sequence=200", publishPath) + res = ephNode.IPFS("name", "get", ipnsName.String()) + record200 := res.Stdout.Bytes() + + ephNode.StopDaemon() + + // start test node (has no local knowledge of the records) + node := makeDaemon(t) + defer node.StopDaemon() + + // put record with sequence 200 first + res = node.PipeToIPFS(bytes.NewReader(record200), "name", "put", ipnsName.String()) + require.NoError(t, res.Err) + + // try to put record with sequence 100 (lower than current 200) + recordFile := filepath.Join(node.Dir, "record100.bin") + err = os.WriteFile(recordFile, record100, 0644) + require.NoError(t, err) + + res = node.RunIPFS("name", "put", ipnsName.String(), recordFile) + require.Error(t, res.Err) + require.Contains(t, res.Stderr.String(), "existing record has sequence 200 >= new record sequence 100") + }) +} diff --git a/test/cli/rpc_content_type_test.go b/test/cli/rpc_content_type_test.go new file mode 100644 index 000000000..9124cfaac --- /dev/null +++ b/test/cli/rpc_content_type_test.go @@ -0,0 +1,167 @@ +// Tests HTTP RPC Content-Type headers. +// These tests verify that RPC endpoints return correct Content-Type headers +// for binary responses (CAR, tar, gzip, raw blocks, IPNS records). + +package cli + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "io" + "net/http" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestRPCDagExportContentType verifies that the RPC endpoint for `ipfs dag export` +// returns the correct Content-Type header for CAR output. +func TestRPCDagExportContentType(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + node.StartDaemon("--offline") + + // add test content + cid := node.IPFSAddStr("test content for dag export") + + url := node.APIURL() + "/api/v0/dag/export?arg=" + cid + + req, err := http.NewRequest(http.MethodPost, url, nil) + require.NoError(t, err) + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, "application/vnd.ipld.car", resp.Header.Get("Content-Type"), + "dag export should return application/vnd.ipld.car") +} + +// TestRPCBlockGetContentType verifies that the RPC endpoint for `ipfs block get` +// returns the correct Content-Type header for raw block data. +func TestRPCBlockGetContentType(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + node.StartDaemon("--offline") + + // add test content + cid := node.IPFSAddStr("test content for block get") + + url := node.APIURL() + "/api/v0/block/get?arg=" + cid + + req, err := http.NewRequest(http.MethodPost, url, nil) + require.NoError(t, err) + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, "application/vnd.ipld.raw", resp.Header.Get("Content-Type"), + "block get should return application/vnd.ipld.raw") +} + +// TestRPCProfileContentType verifies that the RPC endpoint for `ipfs diag profile` +// returns the correct Content-Type header for ZIP output. +func TestRPCProfileContentType(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + node.StartDaemon("--offline") + + // use profile-time=0 to skip sampling profiles and return quickly + url := node.APIURL() + "/api/v0/diag/profile?profile-time=0" + + req, err := http.NewRequest(http.MethodPost, url, nil) + require.NoError(t, err) + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, "application/zip", resp.Header.Get("Content-Type"), + "diag profile should return application/zip") +} + +// TestHTTPRPCNameGet verifies the behavior of `ipfs name get` vs `ipfs routing get`: +// +// `ipfs name get `: +// - Purpose: dedicated command for retrieving IPNS records +// - Returns: raw IPNS record bytes (protobuf) +// - Content-Type: application/vnd.ipfs.ipns-record +// +// `ipfs routing get /ipns/`: +// - Purpose: generic routing get for any key type +// - Returns: JSON with base64-encoded record in "Extra" field +// - Content-Type: application/json +// +// Both commands retrieve the same underlying IPNS record data. +func TestHTTPRPCNameGet(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + node.StartDaemon() // must be online to use routing + + // add test content and publish IPNS record + cid := node.IPFSAddStr("test content for name get") + node.IPFS("name", "publish", cid) + + // get the node's peer ID (which is also the IPNS name) + peerID := node.PeerID().String() + + // Test ipfs name get - returns raw IPNS record bytes with specific Content-Type + nameGetURL := node.APIURL() + "/api/v0/name/get?arg=" + peerID + nameGetReq, err := http.NewRequest(http.MethodPost, nameGetURL, nil) + require.NoError(t, err) + + nameGetResp, err := http.DefaultClient.Do(nameGetReq) + require.NoError(t, err) + defer nameGetResp.Body.Close() + + assert.Equal(t, http.StatusOK, nameGetResp.StatusCode) + assert.Equal(t, "application/vnd.ipfs.ipns-record", nameGetResp.Header.Get("Content-Type"), + "name get should return application/vnd.ipfs.ipns-record") + + nameGetBytes, err := io.ReadAll(nameGetResp.Body) + require.NoError(t, err) + + // Test ipfs routing get /ipns/... - returns JSON with base64-encoded record + routingGetURL := node.APIURL() + "/api/v0/routing/get?arg=/ipns/" + peerID + routingGetReq, err := http.NewRequest(http.MethodPost, routingGetURL, nil) + require.NoError(t, err) + + routingGetResp, err := http.DefaultClient.Do(routingGetReq) + require.NoError(t, err) + defer routingGetResp.Body.Close() + + assert.Equal(t, http.StatusOK, routingGetResp.StatusCode) + assert.Equal(t, "application/json", routingGetResp.Header.Get("Content-Type"), + "routing get should return application/json") + + // Parse JSON response and decode base64 record from "Extra" field + var routingResp struct { + Extra string `json:"Extra"` + Type int `json:"Type"` + } + err = json.NewDecoder(routingGetResp.Body).Decode(&routingResp) + require.NoError(t, err) + + routingGetBytes, err := base64.StdEncoding.DecodeString(routingResp.Extra) + require.NoError(t, err) + + // Verify both commands return identical IPNS record bytes + assert.Equal(t, nameGetBytes, routingGetBytes, + "name get and routing get should return identical IPNS record bytes") + + // Verify the record can be inspected and contains the published CID + inspectOutput := node.PipeToIPFS(bytes.NewReader(nameGetBytes), "name", "inspect") + assert.Contains(t, inspectOutput.Stdout.String(), cid, + "ipfs name inspect should show the published CID") +} diff --git a/test/cli/rpc_get_output_test.go b/test/cli/rpc_get_output_test.go new file mode 100644 index 000000000..ded237958 --- /dev/null +++ b/test/cli/rpc_get_output_test.go @@ -0,0 +1,74 @@ +package cli + +import ( + "net/http" + "testing" + + "github.com/ipfs/kubo/test/cli/harness" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestRPCGetContentType verifies that the RPC endpoint for `ipfs get` returns +// the correct Content-Type header based on output format options. +// +// Output formats and expected Content-Type: +// - default (no flags): tar (transport format) -> application/x-tar +// - --archive: tar archive -> application/x-tar +// - --compress: gzip -> application/gzip +// - --archive --compress: tar.gz -> application/gzip +// +// Fixes: https://github.com/ipfs/kubo/issues/2376 +func TestRPCGetContentType(t *testing.T) { + t.Parallel() + + node := harness.NewT(t).NewNode().Init() + node.StartDaemon("--offline") + + // add test content + cid := node.IPFSAddStr("test content for Content-Type header verification") + + tests := []struct { + name string + query string + expectedContentType string + }{ + { + name: "default returns application/x-tar", + query: "?arg=" + cid, + expectedContentType: "application/x-tar", + }, + { + name: "archive=true returns application/x-tar", + query: "?arg=" + cid + "&archive=true", + expectedContentType: "application/x-tar", + }, + { + name: "compress=true returns application/gzip", + query: "?arg=" + cid + "&compress=true", + expectedContentType: "application/gzip", + }, + { + name: "archive=true&compress=true returns application/gzip", + query: "?arg=" + cid + "&archive=true&compress=true", + expectedContentType: "application/gzip", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + url := node.APIURL() + "/api/v0/get" + tt.query + + req, err := http.NewRequest(http.MethodPost, url, nil) + require.NoError(t, err) + + resp, err := http.DefaultClient.Do(req) + require.NoError(t, err) + defer resp.Body.Close() + + assert.Equal(t, http.StatusOK, resp.StatusCode) + assert.Equal(t, tt.expectedContentType, resp.Header.Get("Content-Type"), + "Content-Type header mismatch for %s", tt.name) + }) + } +} diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index 1326c72b4..a37c8035b 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -141,7 +141,7 @@ require ( github.com/ipfs/go-cid v0.6.0 // indirect github.com/ipfs/go-datastore v0.9.0 // indirect github.com/ipfs/go-dsqueue v0.1.2 // indirect - github.com/ipfs/go-ipfs-cmds v0.15.0 // indirect + github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 // indirect github.com/ipfs/go-ipfs-redirects-file v0.1.2 // indirect github.com/ipfs/go-ipld-cbor v0.2.1 // indirect github.com/ipfs/go-ipld-format v0.6.3 // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index 8351caa9d..36876ffd0 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -314,8 +314,8 @@ github.com/ipfs/go-ds-leveldb v0.5.2 h1:6nmxlQ2zbp4LCNdJVsmHfs9GP0eylfBNxpmY1csp github.com/ipfs/go-ds-leveldb v0.5.2/go.mod h1:2fAwmcvD3WoRT72PzEekHBkQmBDhc39DJGoREiuGmYo= github.com/ipfs/go-dsqueue v0.1.2 h1:jBMsgvT9Pj9l3cqI0m5jYpW/aWDYkW4Us6EuzrcSGbs= github.com/ipfs/go-dsqueue v0.1.2/go.mod h1:OU94YuMVUIF/ctR7Ysov9PI4gOa2XjPGN9nd8imSv78= -github.com/ipfs/go-ipfs-cmds v0.15.0 h1:nQDgKadrzyiFyYoZMARMIoVoSwe3gGTAfGvrWLeAQbQ= -github.com/ipfs/go-ipfs-cmds v0.15.0/go.mod h1:VABf/mv/wqvYX6hLG6Z+40eNAEw3FQO0bSm370Or3Wk= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 h1:l1DaJI5/+uOKdmvYrXwN3j/zOApLr8EBB0IGMTB7UaM= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1/go.mod h1:YmhRbpaLKg40i9Ogj2+L41tJ+8x50fF8u1FJJD/WNhc= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-pq v0.0.4 h1:U7jjENWJd1jhcrR8X/xHTaph14PTAK9O+yaLJbjqgOw= From 07ad431b0090094c8a3282ae9ac8ddce3553daea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 08:24:30 -0800 Subject: [PATCH 46/49] chore(deps): bump github.com/coreos/go-systemd/v22 from 22.5.0 to 22.7.0 (#11178) Bumps [github.com/coreos/go-systemd/v22](https://github.com/coreos/go-systemd) from 22.5.0 to 22.7.0. - [Release notes](https://github.com/coreos/go-systemd/releases) - [Commits](https://github.com/coreos/go-systemd/compare/v22.5.0...v22.7.0) --- updated-dependencies: - dependency-name: github.com/coreos/go-systemd/v22 dependency-version: 22.7.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index cb38a63ce..d3c89d0ce 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/ceramicnetwork/go-dag-jose v0.1.1 github.com/cheggaaa/pb v1.0.29 github.com/cockroachdb/pebble/v2 v2.1.4 - github.com/coreos/go-systemd/v22 v22.5.0 + github.com/coreos/go-systemd/v22 v22.7.0 github.com/dustin/go-humanize v1.0.1 github.com/elgris/jsondiff v0.0.0-20160530203242-765b5c24c302 github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 diff --git a/go.sum b/go.sum index 562bd85a6..f8d24d4e9 100644 --- a/go.sum +++ b/go.sum @@ -129,8 +129,8 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= +github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/crackcomm/go-gitignore v0.0.0-20241020182519-7843d2ba8fdf h1:dwGgBWn84wUS1pVikGiruW+x5XM4amhjaZO20vCjay4= @@ -227,7 +227,6 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= From 3522136c4fc7cde6a60dd96979fde00c937556fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 08:41:16 -0800 Subject: [PATCH 47/49] chore(deps): bump github.com/multiformats/go-multiaddr-dns from 0.4.1 to 0.5.0 in the ipfs-ecosystem group (#11177) * chore(deps): bump github.com/multiformats/go-multiaddr-dns Bumps the ipfs-ecosystem group with 1 update: [github.com/multiformats/go-multiaddr-dns](https://github.com/multiformats/go-multiaddr-dns). Updates `github.com/multiformats/go-multiaddr-dns` from 0.4.1 to 0.5.0 - [Release notes](https://github.com/multiformats/go-multiaddr-dns/releases) - [Commits](https://github.com/multiformats/go-multiaddr-dns/compare/v0.4.1...v0.5.0) --- updated-dependencies: - dependency-name: github.com/multiformats/go-multiaddr-dns dependency-version: 0.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: ipfs-ecosystem ... Signed-off-by: dependabot[bot] * chore: run make mod_tidy --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com> --- docs/examples/kubo-as-a-library/go.mod | 2 +- docs/examples/kubo-as-a-library/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- test/dependencies/go.mod | 2 +- test/dependencies/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 1a3d9aeb5..9b4c71f96 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -138,7 +138,7 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect + github.com/multiformats/go-multiaddr-dns v0.5.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multicodec v0.10.0 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index ca60866cc..68b4486b0 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -485,8 +485,8 @@ github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= github.com/multiformats/go-multiaddr v0.16.1 h1:fgJ0Pitow+wWXzN9do+1b8Pyjmo8m5WhGfzpL82MpCw= github.com/multiformats/go-multiaddr v0.16.1/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= -github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= -github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= +github.com/multiformats/go-multiaddr-dns v0.5.0 h1:p/FTyHKX0nl59f+S+dEUe8HRK+i5Ow/QHMw8Nh3gPCo= +github.com/multiformats/go-multiaddr-dns v0.5.0/go.mod h1:yJ349b8TPIAANUyuOzn1oz9o22tV9f+06L+cCeMxC14= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= diff --git a/go.mod b/go.mod index d3c89d0ce..7a0445403 100644 --- a/go.mod +++ b/go.mod @@ -62,7 +62,7 @@ require ( github.com/libp2p/go-socket-activation v0.1.1 github.com/miekg/dns v1.1.72 github.com/multiformats/go-multiaddr v0.16.1 - github.com/multiformats/go-multiaddr-dns v0.4.1 + github.com/multiformats/go-multiaddr-dns v0.5.0 github.com/multiformats/go-multibase v0.2.0 github.com/multiformats/go-multicodec v0.10.0 github.com/multiformats/go-multihash v0.2.3 diff --git a/go.sum b/go.sum index f8d24d4e9..5e5520b51 100644 --- a/go.sum +++ b/go.sum @@ -591,8 +591,8 @@ github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= github.com/multiformats/go-multiaddr v0.16.1 h1:fgJ0Pitow+wWXzN9do+1b8Pyjmo8m5WhGfzpL82MpCw= github.com/multiformats/go-multiaddr v0.16.1/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= -github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= -github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= +github.com/multiformats/go-multiaddr-dns v0.5.0 h1:p/FTyHKX0nl59f+S+dEUe8HRK+i5Ow/QHMw8Nh3gPCo= +github.com/multiformats/go-multiaddr-dns v0.5.0/go.mod h1:yJ349b8TPIAANUyuOzn1oz9o22tV9f+06L+cCeMxC14= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= github.com/multiformats/go-multiaddr-net v0.1.1/go.mod h1:5JNbcfBOP4dnhoZOv10JJVkJO0pCCEf8mTnipAo2UZQ= diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index a37c8035b..f54f349cc 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -210,7 +210,7 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect - github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect + github.com/multiformats/go-multiaddr-dns v0.5.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect github.com/multiformats/go-multicodec v0.10.0 // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index 36876ffd0..4beeb4617 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -496,8 +496,8 @@ github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= github.com/multiformats/go-multiaddr v0.16.1 h1:fgJ0Pitow+wWXzN9do+1b8Pyjmo8m5WhGfzpL82MpCw= github.com/multiformats/go-multiaddr v0.16.1/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= -github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M= -github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc= +github.com/multiformats/go-multiaddr-dns v0.5.0 h1:p/FTyHKX0nl59f+S+dEUe8HRK+i5Ow/QHMw8Nh3gPCo= +github.com/multiformats/go-multiaddr-dns v0.5.0/go.mod h1:yJ349b8TPIAANUyuOzn1oz9o22tV9f+06L+cCeMxC14= github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g= From 22ca729eb3e57ace4378644bc7991eaa1103e425 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Feb 2026 22:08:57 -0800 Subject: [PATCH 48/49] chore(deps): bump github.com/hashicorp/go-version from 1.7.0 to 1.8.0 (#11160) * chore(deps): bump github.com/hashicorp/go-version from 1.7.0 to 1.8.0 Bumps [github.com/hashicorp/go-version](https://github.com/hashicorp/go-version) from 1.7.0 to 1.8.0. - [Release notes](https://github.com/hashicorp/go-version/releases) - [Changelog](https://github.com/hashicorp/go-version/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/go-version/compare/v1.7.0...v1.8.0) --- updated-dependencies: - dependency-name: github.com/hashicorp/go-version dependency-version: 1.8.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * chore: run make mod_tidy --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Andrew Gillis <11790789+gammazero@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- test/dependencies/go.mod | 2 +- test/dependencies/go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 7a0445403..1cc29bcb3 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/facebookgo/atomicfile v0.0.0-20151019160806-2de1f203e7d5 github.com/fsnotify/fsnotify v1.9.0 github.com/google/uuid v1.6.0 - github.com/hashicorp/go-version v1.7.0 + github.com/hashicorp/go-version v1.8.0 github.com/ipfs-shipyard/nopfs v0.0.14 github.com/ipfs-shipyard/nopfs/ipfs v0.25.0 github.com/ipfs/boxo v0.36.0 diff --git a/go.sum b/go.sum index 5e5520b51..ded71053b 100644 --- a/go.sum +++ b/go.sum @@ -316,8 +316,8 @@ github.com/guillaumemichel/reservedpool v0.3.0 h1:eqqO/QvTllLBrit7LVtVJBqw4cD0Wd github.com/guillaumemichel/reservedpool v0.3.0/go.mod h1:sXSDIaef81TFdAJglsCFCMfgF5E5Z5xK1tFhjDhvbUc= github.com/gxed/hashland/keccakpg v0.0.1/go.mod h1:kRzw3HkwxFU1mpmPP8v1WyQzwdGfmKFJ6tItnhQ67kU= github.com/gxed/hashland/murmur3 v0.0.1/go.mod h1:KjXop02n4/ckmZSnY2+HKcLud/tcmvhST0bie/0lS48= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index f54f349cc..f32d574da 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -127,7 +127,7 @@ require ( github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect github.com/gostaticanalysis/nilerr v0.1.1 // indirect github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect - github.com/hashicorp/go-version v1.7.0 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index 4beeb4617..3ffd5829b 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -280,8 +280,8 @@ github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1T github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= From dd882c0bdb9db2421d596f8eeb21ef84c38bccb6 Mon Sep 17 00:00:00 2001 From: Andrew Gillis <11790789+gammazero@users.noreply.github.com> Date: Tue, 3 Feb 2026 07:32:21 -0800 Subject: [PATCH 49/49] fix: recognize content-type application/x-tar (#11180) * fix: recognize content-type application/x-tar For context, see: https://github.com/ipfs/go-ipfs-cmds/pull/320 Closes: #11179 --- docs/examples/kubo-as-a-library/go.mod | 2 +- docs/examples/kubo-as-a-library/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- test/dependencies/go.mod | 2 +- test/dependencies/go.sum | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/examples/kubo-as-a-library/go.mod b/docs/examples/kubo-as-a-library/go.mod index 9b4c71f96..f9c3da720 100644 --- a/docs/examples/kubo-as-a-library/go.mod +++ b/docs/examples/kubo-as-a-library/go.mod @@ -85,7 +85,7 @@ require ( github.com/ipfs/go-ds-pebble v0.5.9 // indirect github.com/ipfs/go-dsqueue v0.1.2 // indirect github.com/ipfs/go-fs-lock v0.1.1 // indirect - github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 // indirect + github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260203151407-4b3827ebb483 // indirect github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect github.com/ipfs/go-ipfs-pq v0.0.4 // indirect github.com/ipfs/go-ipfs-redirects-file v0.1.2 // indirect diff --git a/docs/examples/kubo-as-a-library/go.sum b/docs/examples/kubo-as-a-library/go.sum index 68b4486b0..eb2c6d044 100644 --- a/docs/examples/kubo-as-a-library/go.sum +++ b/docs/examples/kubo-as-a-library/go.sum @@ -303,8 +303,8 @@ github.com/ipfs/go-dsqueue v0.1.2 h1:jBMsgvT9Pj9l3cqI0m5jYpW/aWDYkW4Us6EuzrcSGbs github.com/ipfs/go-dsqueue v0.1.2/go.mod h1:OU94YuMVUIF/ctR7Ysov9PI4gOa2XjPGN9nd8imSv78= github.com/ipfs/go-fs-lock v0.1.1 h1:TecsP/Uc7WqYYatasreZQiP9EGRy4ZnKoG4yXxR33nw= github.com/ipfs/go-fs-lock v0.1.1/go.mod h1:2goSXMCw7QfscHmSe09oXiR34DQeUdm+ei+dhonqly0= -github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 h1:l1DaJI5/+uOKdmvYrXwN3j/zOApLr8EBB0IGMTB7UaM= -github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1/go.mod h1:YmhRbpaLKg40i9Ogj2+L41tJ+8x50fF8u1FJJD/WNhc= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260203151407-4b3827ebb483 h1:FnQqL92YxPX08/dcqE4cCSqEzwVGSdj2wprWHX+cUtM= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260203151407-4b3827ebb483/go.mod h1:YmhRbpaLKg40i9Ogj2+L41tJ+8x50fF8u1FJJD/WNhc= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= diff --git a/go.mod b/go.mod index 1cc29bcb3..821cd0fbc 100644 --- a/go.mod +++ b/go.mod @@ -33,7 +33,7 @@ require ( github.com/ipfs/go-ds-measure v0.2.2 github.com/ipfs/go-ds-pebble v0.5.9 github.com/ipfs/go-fs-lock v0.1.1 - github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 + github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260203151407-4b3827ebb483 github.com/ipfs/go-ipld-cbor v0.2.1 github.com/ipfs/go-ipld-format v0.6.3 github.com/ipfs/go-ipld-git v0.1.1 diff --git a/go.sum b/go.sum index ded71053b..853d9d09b 100644 --- a/go.sum +++ b/go.sum @@ -373,8 +373,8 @@ github.com/ipfs/go-dsqueue v0.1.2 h1:jBMsgvT9Pj9l3cqI0m5jYpW/aWDYkW4Us6EuzrcSGbs github.com/ipfs/go-dsqueue v0.1.2/go.mod h1:OU94YuMVUIF/ctR7Ysov9PI4gOa2XjPGN9nd8imSv78= github.com/ipfs/go-fs-lock v0.1.1 h1:TecsP/Uc7WqYYatasreZQiP9EGRy4ZnKoG4yXxR33nw= github.com/ipfs/go-fs-lock v0.1.1/go.mod h1:2goSXMCw7QfscHmSe09oXiR34DQeUdm+ei+dhonqly0= -github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 h1:l1DaJI5/+uOKdmvYrXwN3j/zOApLr8EBB0IGMTB7UaM= -github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1/go.mod h1:YmhRbpaLKg40i9Ogj2+L41tJ+8x50fF8u1FJJD/WNhc= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260203151407-4b3827ebb483 h1:FnQqL92YxPX08/dcqE4cCSqEzwVGSdj2wprWHX+cUtM= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260203151407-4b3827ebb483/go.mod h1:YmhRbpaLKg40i9Ogj2+L41tJ+8x50fF8u1FJJD/WNhc= github.com/ipfs/go-ipfs-delay v0.0.0-20181109222059-70721b86a9a8/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= diff --git a/test/dependencies/go.mod b/test/dependencies/go.mod index f32d574da..3771466a9 100644 --- a/test/dependencies/go.mod +++ b/test/dependencies/go.mod @@ -141,7 +141,7 @@ require ( github.com/ipfs/go-cid v0.6.0 // indirect github.com/ipfs/go-datastore v0.9.0 // indirect github.com/ipfs/go-dsqueue v0.1.2 // indirect - github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 // indirect + github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260203151407-4b3827ebb483 // indirect github.com/ipfs/go-ipfs-redirects-file v0.1.2 // indirect github.com/ipfs/go-ipld-cbor v0.2.1 // indirect github.com/ipfs/go-ipld-format v0.6.3 // indirect diff --git a/test/dependencies/go.sum b/test/dependencies/go.sum index 3ffd5829b..943b2bbe3 100644 --- a/test/dependencies/go.sum +++ b/test/dependencies/go.sum @@ -314,8 +314,8 @@ github.com/ipfs/go-ds-leveldb v0.5.2 h1:6nmxlQ2zbp4LCNdJVsmHfs9GP0eylfBNxpmY1csp github.com/ipfs/go-ds-leveldb v0.5.2/go.mod h1:2fAwmcvD3WoRT72PzEekHBkQmBDhc39DJGoREiuGmYo= github.com/ipfs/go-dsqueue v0.1.2 h1:jBMsgvT9Pj9l3cqI0m5jYpW/aWDYkW4Us6EuzrcSGbs= github.com/ipfs/go-dsqueue v0.1.2/go.mod h1:OU94YuMVUIF/ctR7Ysov9PI4gOa2XjPGN9nd8imSv78= -github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1 h1:l1DaJI5/+uOKdmvYrXwN3j/zOApLr8EBB0IGMTB7UaM= -github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260130221847-44581e1f62e1/go.mod h1:YmhRbpaLKg40i9Ogj2+L41tJ+8x50fF8u1FJJD/WNhc= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260203151407-4b3827ebb483 h1:FnQqL92YxPX08/dcqE4cCSqEzwVGSdj2wprWHX+cUtM= +github.com/ipfs/go-ipfs-cmds v0.15.1-0.20260203151407-4b3827ebb483/go.mod h1:YmhRbpaLKg40i9Ogj2+L41tJ+8x50fF8u1FJJD/WNhc= github.com/ipfs/go-ipfs-delay v0.0.1 h1:r/UXYyRcddO6thwOnhiznIAiSvxMECGgtv35Xs1IeRQ= github.com/ipfs/go-ipfs-delay v0.0.1/go.mod h1:8SP1YXK1M1kXuc4KJZINY3TQQ03J2rwBG9QfXmbRPrw= github.com/ipfs/go-ipfs-pq v0.0.4 h1:U7jjENWJd1jhcrR8X/xHTaph14PTAK9O+yaLJbjqgOw=