Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions core/core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/ipfs/kubo/repo"

"github.com/ipfs/boxo/bootstrap"
"github.com/ipfs/boxo/filestore"
"github.com/ipfs/boxo/keystore"
datastore "github.com/ipfs/go-datastore"
Expand Down Expand Up @@ -226,3 +227,85 @@ func TestHasActiveDHTClient(t *testing.T) {
}
})
}

// TestBootstrapWithEmptyPeerListAndStaleBackupPeers is a kubo-level regression
// test for https://github.com/ipfs/kubo/issues/11452: when no bootstrap peers
// are configured, the bootstrap process must not dial stale backup peers
// persisted from previous runs under TempBootstrapPeersKey.
//
// The dialing behavior itself is fixed and unit-tested in boxo
// (bootstrap.bootstrapRound skips the backup list when BootstrapPeers() is
// empty). This test verifies the kubo wiring end to end: IpfsNode.Bootstrap
// runs without error with an empty Bootstrap config and a populated
// TempBootstrapPeersKey, and starts a bootstrapper (the periodic process runs,
// but each round is a no-op for the backup list).
func TestBootstrapWithEmptyPeerListAndStaleBackupPeers(t *testing.T) {
ctx := context.Background()

ds := syncds.MutexWrap(datastore.NewMapDatastore())

// Seed the datastore with stale backup peers, as if left over from a
// previous run that had bootstrap peers configured. These are the peers
// that were dialed every 30s in the original bug. We write the JSON
// []string form that saveTempBootstrapPeers produces.
staleBytes := []byte(`["/dns4/bootstrap.libp2p.io/tcp/4001/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN","/dns4/ams-1.routing.cloudflare.ipfs.team/tcp/443/https/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"]`)
if err := ds.Put(ctx, TempBootstrapPeersKey, staleBytes); err != nil {
t.Fatalf("failed to seed stale backup peers: %v", err)
}

c := config.Config{}
c.Identity = testIdentity
c.Bootstrap = nil // no bootstrap peers configured
c.Routing.Type = config.NewOptionalString("none")

r := &repo.Mock{
C: c,
D: ds,
}

// IpfsNode.Bootstrap calls bootstrap.Bootstrap, which needs a real host.
h, err := golib.New(golib.ListenAddrStrings("/ip4/127.0.0.1/tcp/0"))
if err != nil {
t.Fatalf("failed to create libp2p host: %v", err)
}
t.Cleanup(func() { _ = h.Close() })

peerID, err := peer.Decode(testIdentity.PeerID)
if err != nil {
t.Fatalf("failed to decode peer ID: %v", err)
}

node := &IpfsNode{
Identity: peerID,
PeerHost: h,
Routing: routinghelpers.NewComposableParallel([]*routinghelpers.ParallelRouter{
{Router: routinghelpers.Null{}, IgnoreError: true},
}),
Repo: r,
}

// Explicitly pass an empty bootstrap peer function so loadBootstrapPeers
// (which would consult config) is not used; we want the empty-list path.
// BootstrapConfigWithPeers gives us sane defaults (Period, etc.).
cfg := bootstrap.BootstrapConfigWithPeers(nil)
cfg.BootstrapPeers = func() []peer.AddrInfo { return nil }
if err := node.Bootstrap(cfg); err != nil {
t.Fatalf("Bootstrap returned error with empty peer list and stale backup peers: %v", err)
}
if node.Bootstrapper == nil {
t.Fatal("Bootstrapper should be set (the periodic process runs; rounds are no-ops for the backup list)")
}
t.Cleanup(func() { _ = node.Bootstrapper.Close() })

// The stale backup peers must still be present in the datastore (we did
// not dial them, and the save process only runs after the first round
// completes and only saves currently-connected peers, of which there are
// none). This confirms we did not corrupt or clear the key.
got, err := ds.Get(ctx, TempBootstrapPeersKey)
if err != nil {
t.Fatalf("TempBootstrapPeersKey missing after Bootstrap: %v", err)
}
if string(got) != string(staleBytes) {
t.Errorf("TempBootstrapPeersKey modified by Bootstrap: got %q, want %q", string(got), string(staleBytes))
}
}
4 changes: 4 additions & 0 deletions docs/changelogs/v0.44.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@

### 🔦 Highlights

#### Empty `Bootstrap` list disables all bootstrap dialing

An empty `Bootstrap` list now fully disables the bootstrap process. Previously, even with no bootstrap peers configured, the node still loaded backup peers persisted from previous runs and dialed them every 30 seconds, causing unwanted external DNS resolution and connection attempts. This affected users who explicitly disabled routing (for example `Routing.Type=none` or `ipfs daemon --routing=none`) to run a local-only/offline node. With this change, no configured bootstrap peers means nothing to recover from, so the backup list is not consulted. Nodes that configure explicit `Bootstrap` peers are unaffected; `Peering.Peers` and mDNS are independent of `Bootstrap` and may still dial peers.

#### 📦️ Dependency updates

- update `go-ds-pebble` to [v0.5.13](https://github.com/ipfs/go-ds-pebble/releases/tag/v0.5.13)
Expand Down
2 changes: 2 additions & 0 deletions docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,8 @@ The special value `"auto"` automatically uses curated, up-to-date bootstrap peer
- **Automatic updates**: New bootstrap peers are added as the network evolves
- **Custom control**: Add your own trusted peers alongside or instead of the defaults

An empty list disables all bootstrap dialing, including saved backup peers from previous runs. This is the way to make a node fully local-only/offline (for example together with `Routing.Type=none` or `ipfs daemon --routing=none`): no configured bootstrap peers means nothing to recover from, so the backup list is not consulted. Note that `Peering.Peers` and mDNS are independent of `Bootstrap` and may still dial peers.

Default: `["auto"]`

Type: `array[string]` ([multiaddrs][multiaddr] or `"auto"`)
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,5 @@ exclude (
github.com/ipfs/go-ipfs-cmds v2.0.1+incompatible
github.com/libp2p/go-libp2p v6.0.23+incompatible
)

replace github.com/ipfs/boxo => github.com/karawitan/boxo v0.42.3-0.20260907071254-76cf61a1f633
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -340,8 +340,6 @@ 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.1.0 h1:nIWwfIE3AaG7RCDQIsrUonGCOTp7qSXzxH7ab/ss964=
github.com/ipfs/bbloom v0.1.0/go.mod h1:lDy3A3i6ndgEW2z1CaRFvDi5/ZTzgM1IxA/pkL7Wgts=
github.com/ipfs/boxo v0.42.3-0.20260904132258-02026ddcf262 h1:ccv4Wuz0qtHE52U0r4r2+NpnFbPYJp8riL6vTX6i3ic=
github.com/ipfs/boxo v0.42.3-0.20260904132258-02026ddcf262/go.mod h1:TCEs4l6Q6kfd1SUdYEzcxvC8JE1gc+6dObSs/6kOsb8=
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.4 h1:pgsT9i8zB4YQkBIQRrBwqbQiXPogRCiQnfxd2bC4koI=
Expand Down Expand Up @@ -441,6 +439,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0=
github.com/karawitan/boxo v0.42.3-0.20260907071254-76cf61a1f633 h1:NcN03Kibfy8T3ivuyUtW7xwIXVRNjT9h70QSmKfXyE4=
github.com/karawitan/boxo v0.42.3-0.20260907071254-76cf61a1f633/go.mod h1:TCEs4l6Q6kfd1SUdYEzcxvC8JE1gc+6dObSs/6kOsb8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
Expand Down