github ipfs/kubo v0.43.1

4 hours ago

Note

This release was brought to you by the Shipyard team.

Important

v0.43 is the last Kubo by the Shipyard. Our IPFS work ends on September 30, 2026. Bring your support and transition questions to the community forum.

  • ๐Ÿ”ฆ Highlights
    • ๐Ÿ”‡ Empty Bootstrap list disables all bootstrap dialing
    • ๐Ÿ—„๏ธ Improved datastore profiles and docs
    • ๐Ÿ—‚๏ธ Setting the flatfs shard depth is less error-prone
    • ๐Ÿ“ ipfs ls can print readable sizes and sort by size
    • ๐Ÿ“Œ Files on FUSE mounts keep their inode number
    • ๐Ÿงน FUSE mounts keep what you write after a rename
    • ๐Ÿ” ipfs key export writes are owner-only
    • ๐Ÿ›‘ ipfs shutdown exits a daemon started with --enable-gc
    • ๐Ÿ”— Gateway Ipfs-Uri header replaces X-Ipfs-Path (IPIP-548)
    • ๐Ÿ”„ Bitswap sees peers connected before it starts
    • ๐Ÿคซ No more background pings to HTTP providers
    • ๐Ÿ”• Telemetry is off by default
    • ๐ŸŒ Future-proofing browser retrieval: WebTransport fix for Safari 26
    • ๐Ÿ’พ Provider record writes are batched again
    • ๐Ÿ”’ Hardened CID profiles, new low-level knob
    • ๐Ÿ“ฆ๏ธ Dependency updates
  • ๐Ÿ“ Changelog
  • ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Contributors

This is mostly a bugfix release. It also carries a few small, opt-in configuration options and profiles that landed since v0.43.0.

๐Ÿ”ฆ Highlights

๐Ÿ”‡ Empty Bootstrap list disables all bootstrap dialing

Setting Bootstrap to [] now stops the node from dialing bootstrap peers of any kind, including the backup peers it saved from earlier runs. Until now, a node that had once run with the default bootstrappers kept dialing those saved peers every 30 seconds, even with an empty list and Routing.Type=none.

The result is a node that connects only to peers you chose: Peering.Peers, ipfs swarm connect, and peers found on the local network through mDNS. For no outbound traffic at all, also set Routing.Type to none and disable AutoConf.

Backup peers still work when Bootstrap has at least one entry: the node dials the configured peers first and falls back to saved peers only while it stays below the minimum peer count.

๐Ÿ—„๏ธ Improved datastore profiles and docs

Kubo keeps blocks in flatfs and everything else (pins, MFS root, IPNS and DHT provider records) in leveldb. leveldb is slow to reclaim space after bulk deletes, so on nodes that churn that metadata all day, datastore/ grows far past the live data. ipfs init --profile=flatfs-pebbleds keeps flatfs for blocks and puts the rest in pebble, which compacts deleted keys promptly. This works for new repos only. The profile is experimental, and pebble has less production use in Kubo than leveldb, so report problems in kubo issues. Details in the profile docs.

The default layout is unchanged. Its profile is now named flatfs-levelds, and flatfs and flatfs-measure stay as aliases. The datastore docs say which data lives in which store and why flatfs holds only blocks.

๐Ÿ—‚๏ธ Setting the flatfs shard depth is less error-prone

The flatfs shard depth (shardFunc) can only be set when a repo is created, by passing a config file to ipfs init. That route had traps. Fixed:

  • ipfs init <file> and ipfs daemon --init --init-config refuse a config without a private key, such as ipfs config show output, which used to produce a repo that crashed on start.
  • ipfs init checks Datastore.Spec before writing anything, so a typo in shardFunc no longer leaves a half-made repo.
  • ipfs config profile apply refuses a profile that would change the datastore layout of an existing repo instead of writing a config the repo can no longer open.
  • The Datastore.Spec vs datastore_spec mismatch error labels the two values correctly and says what to do.

docs/datastores.md has the steps for setting next-to-last/3 on a new repo, when it is worth it, and how to move an existing repo. Defaults are unchanged.

๐Ÿ“ ipfs ls can print readable sizes and sort by size

Two new flags on ipfs ls, both off by default. --human (-H) prints sizes in SI units such as 3.0 kB and 2.0 MB, matching ipfs repo stat -H. --sort-size (-S) lists the biggest entries first, so you can find what fills a directory without piping through sort. Run ipfs ls --help for how they combine with --stream, --size, and the JSON response.

๐Ÿ“Œ Files on FUSE mounts keep their inode number

A file on a mount made by ipfs mount used to get a new inode number roughly every second, whenever the kernel dropped and re-read its directory entry. Programs that check whether a file is still the same file read that as the file being replaced under them. Saving a file on /mfs with vim could fail with E949: File changed while writing, and pwd, find and backup tools could misbehave for the same reason.

All three mounts now hand out stable numbers. On /ipfs the number comes from the CID, so the same content is the same object whichever path reaches it, and the same number comes back after a remount. On /ipns and /mfs an entry keeps its number for as long as it exists, including across a rename, while a name that is deleted and created again is numbered afresh; those numbers are assigned per mount and start over on the next one. Deleting through ipfs files rather than through the mount is the exception: the mount does not see it, so the name keeps its old number if it comes back.

Smaller fixes come with it: mount points report an inode number instead of 0, ls -i agrees with stat, the link count is 1 rather than the 0 POSIX gives to a deleted file, and /ipns/<key> directories show their real permissions instead of d---------.

๐Ÿงน FUSE mounts keep what you write after a rename

Renaming on a mounted /mfs or /ipns left the kernel writing into the entry the rename had just taken away. mv a b followed by a write to b reported success and then quietly went back to the old contents a second later. A file created in a directory that had just been renamed vanished the same way, and the directory reappeared under the name it had been moved away from. Renaming over a directory that was not empty deleted everything in it, where POSIX asks for ENOTEMPTY.

On /ipfs, a listing no longer fails in its entirety when one child's block is missing, an entry in a codec the mount cannot decode reads back the block rather than refusing, and the ipfs.cid xattr answers with the CID from the path instead of a re-encoded form of it.

๐Ÿ” ipfs key export writes are owner-only

ipfs key export now writes the key file with owner-only permissions (0600), including when it overwrites an existing file with looser permissions. The export goes to a temporary file renamed over the target, so a failed export leaves the previous contents intact; character devices and pipes such as /dev/null are streamed to directly.

๐Ÿ›‘ ipfs shutdown exits a daemon started with --enable-gc

A daemon started with --enable-gc closed its repo on ipfs shutdown (POST /api/v0/shutdown) but the process never exited, because the periodic garbage collection loop only stopped on a signal. Supervisors that stop Kubo over the RPC API waited forever. The loop now also stops when the node closes; SIGINT and SIGTERM behave as before.

๐Ÿ”— Gateway Ipfs-Uri header replaces X-Ipfs-Path (IPIP-548)

Gateway responses now include an Ipfs-Uri header with a canonical ipfs:// or ipns:// address of the requested content path (IPIP-548). Every path segment is percent-encoded, so the address survives any file name, including names with spaces, %, #, and non-ASCII characters.

Important

Kubo no longer sends the deprecated X-Ipfs-Path header. HTTP header values cannot carry non-ASCII bytes, so a path with such a file name arrives garbled. Clients that read X-Ipfs-Path must migrate to Ipfs-Uri; decoding it back into a content path takes a single percent-decode of each path segment. Operators who need time can temporarily restore the legacy header with Gateway.DeprecatedXIpfsPath; even then Kubo omits it when the value contains bytes that cannot appear in an HTTP header.

๐Ÿ”„ Bitswap sees peers connected before it starts

A peer that was already connected when the node started stayed invisible to Bitswap: libp2p reports only connections opened after a notifier registers, so no want request was ever sent over such a connection. Nodes with other ways to find content usually masked it; a node relying on an already-connected peer could wait forever. Fixed in boxo v0.42.2.

๐Ÿคซ No more background pings to HTTP providers

Bitswap over HTTP no longer probes every connected HTTP provider with GET/HEAD /ipfs/bafkqaaa every 5 seconds for the lifetime of the process. Idle HTTP peers now generate no background traffic; latency is measured from the connection probe and from real retrieval responses instead (boxo v0.42.2).

๐Ÿ”• Telemetry is off by default

Kubo no longer ships with a telemetry endpoint. The collector at telemetry.ipshipyard.dev shuts down with the end of Shipyard's IPFS work, so a node now collects nothing and sends nothing unless you point it at your own collector with Plugins.Plugins.telemetry.Config.Endpoint. A node that reported under an earlier version removes its stored telemetry_uuid on the next daemon start. The opt-outs (IPFS_TELEMETRY=off, DO_NOT_TRACK=1, the config Mode) keep working for builds that set an endpoint. See docs/telemetry.md.

๐ŸŒ Future-proofing browser retrieval: WebTransport fix for Safari 26

Kubo's WebTransport listener now supports session flow control from WebTransport over HTTP/3 draft-16. Safari 26 opens no stream on a session unless the server advertises session limits, so Kubo advertises them. The limits are set so high they never apply; the QUIC per-connection limits and the libp2p resource manager remain the effective caps.

๐Ÿ’พ Provider record writes are batched again

go-libp2p-kad-dht v0.42.0, first shipped in Kubo v0.43.0, turned every DHT provider record write, and every delete made by the provider's garbage collection, into an individual, fsynced datastore operation. On nodes announcing many CIDs this showed up as constant disk activity. This release batches these writes and deletes again, 256 records at a time, restoring the disk write rate from before v0.43.0. Nothing changes on the wire or in the datastore layout; upgrading is all that is needed.

๐Ÿ”’ Hardened CID profiles, new low-level knob

The unixfs-v0-2015 and unixfs-v1-2025 profiles now pin the PBNode field order (links-first) explicitly, and regression tests lock in the exact bytes and CIDs they produce. Reading blocks in either field order is formalized in IPIP-550 and tested by gateway-conformance v0.14.1. Nothing changes by default.

For writers that need Data-first output (streaming readers can then parse HAMT shards without buffering all links first), there is a new opt-in Import.UnixFSPBNodeFieldOrder option. No profile enables it. Opting in changes the CIDs of newly added directories, HAMT shards, and multi-chunk files, and MFS re-encodes the directories that ipfs files operations rewrite; enable it only when consumers of your CIDs expect the new order.

๐Ÿ“ฆ๏ธ Dependency updates

๐Ÿ“ Changelog

Full Changelog

๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ Contributors

Contributor Commits Lines ยฑ Files Changed
@lidel 24 +5265/-515 106
@guillaumemichel 3 +1058/-148 10
@questfever 1 +450/-41 5
@capricornusx 1 +444/-12 5
@gammazero 10 +138/-100 29
@karawitan 2 +150/-7 7
@galargh 1 +5/-1 1
@weifanglab 1 +2/-2 1

Don't miss a new kubo release

NewReleases is sending notifications on new releases.