Note
This release was brought to you by the Shipyard team.
- Overview
- ๐ฆ Highlights
- ๐ฏ Announce CIDs on demand with
ipfs provide once - ๐งฉ Export and import partial CARs with
--local-only - โ๏ธ
Provide.DHT.Interval=0no longer disables providing - ๐ Fixed pin operations hanging under pinned reprovide strategies
- ๐ Smoother first-run upgrades from very old repos
- ๐ Reliable shutdown and container health checks
- ๐จ ERROR log for explicit listeners blocked by
Swarm.AddrFilters - ๐ OpenTelemetry: scope info now exposed as labels
- ๐ง Cleaner progress bars
- ๐ฆ๏ธ Dependency updates
- ๐ฏ Announce CIDs on demand with
- ๐ Changelog
- ๐จโ๐ฉโ๐งโ๐ฆ Contributors
Overview
๐ฆ Highlights
๐ฏ Announce CIDs on demand with ipfs provide once
ipfs provide once <cid>... announces CIDs to the routing system immediately, without waiting for the next scheduled reprovide. Use it when you want fine-grained control over when specific CIDs are announced.
CIDs can be streamed in on stdin, so you can pipe arbitrarily large lists without growing daemon memory:
# Announce every locally pinned CID.
ipfs pin ls | awk '{print $1}' | ipfs provide once# Announce every block reachable from a root (here, ~350 GiB of Wikipedia).
ipfs refs -r bafybeiaysi4s6lnjev27ln5icwm6tueaw2vdykrtjkwiphwekaywqhcjze | ipfs provide onceIn a terminal, the command shows a running count of queued CIDs. With --enc=json it emits one {"Queued":"<cid>"} line per CID, so downstream scripts can consume events as they arrive.
ipfs routing provide keeps working but is deprecated. See ipfs provide once --help for usage and migration notes.
๐งฉ Export and import partial CARs with --local-only
ipfs dag export --local-only writes a CAR with only the blocks you have locally; any missing blocks (and their subtrees) are skipped instead of failing the export. ipfs dag import --local-only reads such a partial CAR without trying to pin its roots.
This is useful when:
- you want to share part of a DAG (for example an MFS tree) that is only partly cached locally
- you fetched a partial CAR from a gateway that supports IPIP-0402 and want to add what you got to your local store
--local-only sets the matching companion flag automatically: on export it implies --offline; on import it implies --pin-roots=false. See ipfs dag export --help and ipfs dag import --help for details.
โ๏ธ Provide.DHT.Interval=0 no longer disables providing
Provide.DHT.Interval=0 now disables only the periodic reprovide schedule. New CIDs still announce via fast-provide-root and ipfs provide once. To fully disable providing, set Provide.Enabled=false.
Important
The daemon now refuses to start when Provide.DHT.Interval=0 is set without an explicit Provide.Enabled. Operators upgrading from an earlier kubo version must opt in to one of the two semantics:
Provide.Enabled=falseto fully disable providing (the previous behaviour ofInterval=0).Provide.Enabled=trueto keep ad-hoc providing while skipping the periodic reprovide schedule.
The startup error names both options. Pick the one that matches your intent.
๐ Fixed pin operations hanging under pinned reprovide strategies
ipfs pin ls, ipfs add, and other pin-touching operations could block for hours on nodes running with Provide.Strategy set to pinned, roots, or pinned+mfs (including +unique / +entities variants). The pin index held a read lock for the entire reprovide cycle, which on large pinsets takes many hours. Any pin operation issued during that window blocked, and further pin ls / ipfs add calls piled up behind it until the cycle finished.
The pinner now snapshots the index under the read lock and releases it before the reprovider starts, so pin operations are no longer blocked by the reprovide cycle. The default Provide.Strategy=all was not affected.
๐ Smoother first-run upgrades from very old repos
The one-time migration for repos from go-ipfs or Kubo older than v0.27 now retries across several gateways with HTTP timeouts, so a single slow or blocked gateway no longer hangs the daemon. Set Migration.DownloadSources to use your own gateway list.
๐ Reliable shutdown and container health checks
Sending SIGTERM or SIGINT to kubo could leave the daemon stuck "half-shutdown": internal subsystems had stopped, but the process kept running and answering the RPC API. Docker and Kubernetes health checks reported the node as healthy while it had quietly stopped serving content. Recovery required a manual docker restart. Separately, the pinner could log a pebble: closed panic trace when the datastore closed before ongoing pin operations finished.
What changed:
-
Bounded shutdown. A new
Internal.ShutdownTimeoutcaps how long a stuck shutdown can run, so a zombie daemon recovers instead of staying half-alive. Routine shutdowns finish in seconds; this is a belt-and-suspenders ceiling against unknown bugs and future regressions. The 12-hour default is high enough that no real-world deployment hits it and low enough to recycle a stuck node well before its DHT provider records expire (22 hours). On expiry, the daemon logs which subsystem failed and exits with status1. Set0to disable. -
ipfs diag healthysubcommand. Returns non-zero as soon as shutdown begins, even if the RPC API still answers. The kubo Docker image'sHEALTHCHECKnow uses it, so under--restart=on-failureor a Kubernetes liveness probe a half-shutdown daemon is recycled within seconds. -
Pinner shuts down cleanly. The pinner cancels and waits for ongoing pin work before the datastore closes, removing the
pebble: closedpanic trace from shutdown logs. -
DHT provider deadlines.
ipfs provide statnow returns promptly when the caller cancels, instead of blocking on a slow keystore lookup (previously seen at over an hour). Each provider record sent to a peer is capped byProvide.DHT.SendProviderRecordTimeout, so an unresponsive peer cannot stall a reprovide cycle.
๐จ ERROR log for explicit listeners blocked by Swarm.AddrFilters
If you list a specific address in Addresses.Swarm and a rule in Swarm.AddrFilters blocks it, no incoming connection reaches that listener. Kubo now logs one ERROR per such listener, naming the listener, the matching rule, and the field to remove the rule from.
The common trigger: a /ip4/127.0.0.1/tcp/.../ws listener fronted by nginx or Caddy on a server-profile node. The profile adds /ip4/127.0.0.0/ipcidr/8 to Swarm.AddrFilters, which rejects every proxy connection over loopback. See the reverse-proxy override row for the fix.
Wildcard listens (/ip4/0.0.0.0, /ip6/::) stay out of the ERROR log. Even if their interface expansion lands inside a filtered CIDR, the listener still accepts traffic on the interfaces outside that CIDR, so the filter is working as intended. These matches log at DEBUG instead, so you can still trace which interfaces an AddrFilters rule strips when you need to.
Addresses.NoAnnounce matches also log at DEBUG. Hiding addresses there is the point of the field, but the log line helps when you ask "why isn't this interface in my identify or DHT records?" and the answer is a CIDR rule you forgot you set.
๐ OpenTelemetry: scope info now exposed as labels
The Prometheus endpoint no longer emits the otel_scope_info metric. Each metric now carries otel_scope_name, otel_scope_version, and otel_scope_schema_url labels identifying the instrumentation library that produced it. Update dashboards or queries that read otel_scope_info to consume these labels instead. See docs/metrics.md for details.
๐ง Cleaner progress bars
ipfs add, ipfs cat, and ipfs get now hide their progress bar when stderr is piped or redirected, so a command like ipfs add file 2> log.txt no longer fills the log with progress-bar noise. Pass --progress=true to force the bar on, or --progress=false to hide it.
ipfs dag export and ipfs dag stat now correctly recognize MSYS2 and Git Bash terminals on Windows. Previously the bar was suppressed there even when running interactively.
๐ฆ๏ธ Dependency updates
- update Go to 1.26.4 (incl. 1.26.3)
- update
go-libp2p-pubsubto v0.16.0 - update
go-libp2p-kad-dhtto v0.40.0 (incl. v0.39.2) - update
go-fuse/v2to v2.10.1 (incl. v2.10.0) - update
cheggaaa/pbto v3.1.7 - update
boxoto v0.40.0 - update
p2p-forge/clientto v0.9.0 (incl. v0.8.1)
๐ Changelog
Full Changelog
- github.com/ipfs/kubo:
- docs: move p2p-forge bump to v0.42 changelog
- docs: note AutoTLS local testing needs UPnP
- chore: go 1.26.4 (#11350) (ipfs/kubo#11350)
- fix(libp2p): quieter dead-listener check (#11342) (ipfs/kubo#11342)
- feat: derive AgentSuffix from build origin (#11341) (ipfs/kubo#11341)
- chore(deps): bump p2p-forge to 0.9.0 (#11336) (ipfs/kubo#11336)
- chore: set version to v0.42.0-rc1
- chore: upgrade to Boxo v0.40.0 (#11338) (ipfs/kubo#11338)
- feat(dag): add --local-only to dag export and import (#11229) (ipfs/kubo#11229)
- chore: bump go-libp2p-kad-dht to v0.40.0 (#11334) (ipfs/kubo#11334)
- refactor: migrate away from cheggaaa/pb v1 (#11322) (ipfs/kubo#11322)
- docs: firewall (ufw) walkthrough for port 4001 (#11332) (ipfs/kubo#11332)
- fix: migration fetcher robustness (#11305) (ipfs/kubo#11305)
- chore: update boxo to remove io.Seeker from files.File (#11254) (ipfs/kubo#11254)
- feat(provide): add
ipfs provide onceand support Interval=0 mode (#11321) (ipfs/kubo#11321) - feat: bound graceful shutdown, add diag healthy (#11329) (ipfs/kubo#11329)
- feat(pinner): close pinner before repo on shutdown (#11296) (ipfs/kubo#11296)
- chore(deps): bump go-libp2p-kad-dht to v0.39.2 (#11323) (ipfs/kubo#11323)
- docs: clarify denylist scope vs routing layer (#11320) (ipfs/kubo#11320)
- chore(deps): align deps with ipfs/boxo#1152 (#11313) (ipfs/kubo#11313)
- feat(config): dead listener check (#11299) (ipfs/kubo#11299)
- fix(libp2p): drop empty addrs in AddrsFactory (#11302) (ipfs/kubo#11302)
- docs: clarify blockstore cache sizing and flatfs sharding (#11303) (ipfs/kubo#11303)
- fix(test): mock GitHub API in TestUpdate (#11300) (ipfs/kubo#11300)
- fix: resolve wildcard swarm in http provides (#11297) (ipfs/kubo#11297)
- Merge release v0.41.0 (ipfs/kubo#11295)
- fix(pins): snapshot index before emitting pins (#11290) (ipfs/kubo#11290)
- Upgrade to Boxo v0.39.0 (#11294) (ipfs/kubo#11294)
- fix(log): scope provide logs to "provider" subsystem (#11289) (ipfs/kubo#11289)
- fix(defaultServerFilters): strip loopback and non-public (#11286) (ipfs/kubo#11286)
- chore: bump p2p-forge to v0.8.0 (#11285) (ipfs/kubo#11285)
- fix: queryevent addrinfo race in kad-dht (#11288) (ipfs/kubo#11288)
- fix(examples): avoid bitswap race, use ed25519 (#11282) (ipfs/kubo#11282)
- fix(fuse): accurate
st_blocksandst_blksize(#11280) (ipfs/kubo#11280) - chore: start v0.42.0 dev cycle
- github.com/ipfs/boxo (v0.39.0 -> v0.40.0):
- Release v0.40.0 (ipfs/boxo#1161)
- upgrade to go-libp2p-kad-dht v0.40.0 (ipfs/boxo#1159)
- Typos (ipfs/boxo#1158)
- fix(routing/http): cap records after filtering (#1157) (ipfs/boxo#1157)
- feat(path/resolver): populate retrieval state (#1153) (ipfs/boxo#1153)
- feat(pqm): opt-in findpeer fallback on dial fail (#1156) (ipfs/boxo#1156)
- chore: go-libp2p-kad-dht v0.39.2 (#1155) (ipfs/boxo#1155)
- fix(files): remove io.Seeker from File interface (#1128) (ipfs/boxo#1128)
- feat(pinner): add Close with ErrClosed lifecycle (#1150) (ipfs/boxo#1150)
- fix(files): support js/wasm and wasip1/wasm builds (#935) (ipfs/boxo#935)
- github.com/ipfs/go-ds-dynamodb (v0.2.2 -> v0.3.0):
- feat!: migrate to aws-sdk-go-v2 (#22) (ipfs/go-ds-dynamodb#22)
- github.com/ipfs/go-ds-pebble (v0.5.10 -> v0.5.11):
- update version for release v0.5.11 (#87) (ipfs/go-ds-pebble#87)
- github.com/ipfs/go-ipfs-cmds (v0.16.0 -> v0.16.1):
- new version (#339) (ipfs/go-ipfs-cmds#339)
- fix spelling errors (#336) (ipfs/go-ipfs-cmds#336)
- github.com/ipfs/go-log/v2 (v2.9.1 -> v2.9.2):
- v2.9.2 bump (#184) (ipfs/go-log#184)
- chore: flag WithGroup as a known no-op
- fix: resolve slog LogValuer in zap bridge
- fix: inline top-level empty-key slog.Group
- fix: support slog.Group in the zap bridge
- github.com/ipfs/go-unixfsnode (v1.10.3 -> v1.10.4):
- updete version (ipfs/go-unixfsnode#96)
- github.com/ipld/go-car/v2 (v2.16.0 -> v2.16.1-0.20260428045700-c4b9f366f20c):
- feat(cmd): Make "extract" arguments more closely mirror common archive tools (#653) (ipld/go-car#653)
- fix: use %w when wrapping errors to preserve error chain (#658) (ipld/go-car#658)
- chore(release): modernise goreleaser config
- fix: remove broken trailing data check in "inspect --full" for CARv1 (#654) (ipld/go-car#654)
- Allow the car binary to provide a version. (#645) (ipld/go-car#645)
- Add
car put-blockcommand (#629) (ipld/go-car#629)
- github.com/ipshipyard/p2p-forge (v0.8.0 -> v0.9.0):
- chore: release v0.9.0 (#89) (ipshipyard/p2p-forge#89)
- refactor: bump deps and migrate to aws-sdk-go-v2 (#88) (ipshipyard/p2p-forge#88)
- feat(client): add WithHTTPClient option (#87) (ipshipyard/p2p-forge#87)
- feat: run Corefile check before run (#77) (ipshipyard/p2p-forge#77)
- github.com/libp2p/go-libp2p-kad-dht (v0.39.1 -> v0.40.0):
- chore: release v0.40.0 (#1257) (libp2p/go-libp2p-kad-dht#1257)
- fix(ResettableKeystore): speed up reset process and keep worker responsive (#1256) (libp2p/go-libp2p-kad-dht#1256)
- fix(provider): hold cycleStatsLk in batchReprovide defer (#1255) (libp2p/go-libp2p-kad-dht#1255)
- fix(provider): per-peer timeout on ADD_PROVIDER sends (#1252) (libp2p/go-libp2p-kad-dht#1252)
- fix(provider)!: bound keystore.Size in Stats with a timeout (#1251) (libp2p/go-libp2p-kad-dht#1251)
- chore: release v0.39.2 (#1249) (libp2p/go-libp2p-kad-dht#1249)
- test: fix flaky TestStartProvidingSingle (#1247) (libp2p/go-libp2p-kad-dht#1247)
- feat(provider): allow WithReprovideInterval(0) for burst-only mode (#1246) (libp2p/go-libp2p-kad-dht#1246)
- github.com/libp2p/go-libp2p-pubsub (v0.15.0 -> v0.16.0):
- Release v0.16.0 (#693) (libp2p/go-libp2p-pubsub#693)
- fix: properly log topic string (#694) (libp2p/go-libp2p-pubsub#694)
- rename {Add/Remove}Peer to On{New/Closed}OutboundStream
- Fix leaked state with OnNewIncomingStream/onClosedIncomingStream
- test: Add failing test demonstrating leak
- partialmessages: init fanout if empty (#690) (libp2p/go-libp2p-pubsub#690)
- log on error when handling rpc in extensions (#668) (libp2p/go-libp2p-pubsub#668)
- feat: add WithMessageFilter option to filter messages early in the notification pipeline (#678) (libp2p/go-libp2p-pubsub#678)
- partialmsgs: remove unused struct
- partialmsgs: PeerRequestsPartial means PeerRequestsPartial
- partialmsgs: Change Gossip Callback to have application call PublishPartial
- feat: FanoutOnly topic option (#676) (libp2p/go-libp2p-pubsub#676)
- Partial Messages API refactor (#671) (libp2p/go-libp2p-pubsub#671)
- Revert "Refactor parts metadata to an interface (#669)"
- Add threadsafe dynamic direct peer handling to GossipSub (#673) (libp2p/go-libp2p-pubsub#673)
- Refactor parts metadata to an interface (#669) (libp2p/go-libp2p-pubsub#669)
- Add Rpc.From()
- partialmsgs: Implement Partial Message gossip
- partialmsgs: Add explicit EagerPartialMessageBytes method
- partialmsgs: remove outdated comment
- partialmessages: Send fewer partsMetadata messages
- partialmessages: Change EagerPush to EagerPushWithParts
- partialmessages: PublishPartial to peers in group state as well
- fix stale comment in partialmessages
- fix logic of omitting IDONTWANT to peers that support partial messages
- fix bug of not sending full messages to peers that requested partial
- rename PartialMessagesExtension for consistency
- partialmessages: rename field to reflect use
- partialmessages: add per peer bounds on peer initiated groups
- partialmessages: Add pairwise interaction test
- add test for SupportsPartial subscribe option
- Add support for
supportsPartial - pb: add
supportsPartialfield in SubOpts - support fanout topics for partial messages
- limit the number of peer initiated group states we track
- nit: rename method from old form
- Add ability to update peer scores if using partial messages
- refactor score methods to accept topic string
- set write deadline for outgoing messages
- ensure that the Hello Packet is the first rpc sent
- add todo
- don't send IDONTWANT to partial message peers
- Add documentation for the RequestPartialMessages topic option
- partialmessages: add explicit MergePartsMetadata function
- partialmessages: add basic bitmap package
- add partial messages to gossipsub router
- implement Partial Messages
- add structured rpc logging
- update protobufs for partial messages
๐จโ๐ฉโ๐งโ๐ฆ Contributors
| Contributor | Commits | Lines ยฑ | Files Changed |
|---|---|---|---|
| @lidel | 42 | +7059/-920 | 188 |
| @MarcoPolo | 43 | +5818/-2113 | 122 |
| @guillaumemichel | 8 | +1422/-165 | 17 |
| @ChayanDass | 2 | +421/-18 | 10 |
| @parkan | 1 | +339/-0 | 3 |
| @gammazero | 12 | +142/-135 | 28 |
| @Vinayak9769 | 1 | +145/-78 | 10 |
| @laciferin2024 | 1 | +209/-0 | 3 |
| @rvagg | 4 | +160/-18 | 6 |
| @Wondertan | 1 | +154/-4 | 4 |
| @cortze | 1 | +125/-19 | 5 |
| @sukunrt | 3 | +58/-27 | 5 |
| @davidebeatrici | 1 | +55/-30 | 4 |
| @hsanjuan | 1 | +33/-15 | 5 |
| @willscott | 1 | +10/-2 | 2 |

