Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
23f1b59
corehttp/routing: wire GetClosestPeers()
hsanjuan Sep 5, 2025
ea8682c
testutils: update http content routing mock
hsanjuan Sep 26, 2025
e55712a
routinv/v1/GetClosestPeers: use cid.Cid keys. Read addrs from peerstore.
hsanjuan Oct 16, 2025
0f41957
Fix unkeyed fields warning
hsanjuan Oct 16, 2025
a109580
fix(routing): only expose WAN DHT peers in GetClosestPeers
lidel Oct 17, 2025
bb2c7d8
feat: expose Routing V1 HTTP API by default
lidel Oct 17, 2025
8e389e0
refactor(routing): improve GetClosestPeers error
lidel Oct 17, 2025
6f14b3b
test: add GetClosestPeers tests for all routing types
lidel Oct 17, 2025
e5978f9
merge: origin/master into feat/get-closest-peers
lidel Oct 18, 2025
a4ed175
Merge remote-tracking branch 'origin/master' into feat/get-closest-peers
hsanjuan Nov 12, 2025
14511d7
Update boxo to latest get-closest-peers commit
hsanjuan Nov 12, 2025
3d64d58
core/routing: GetClosestPeers: return error if cid is undef.
hsanjuan Nov 12, 2025
b7111b7
core/routing: move error handling outside of switch (review)
hsanjuan Nov 12, 2025
4e88fe9
core/routing: GetClosestPeers: handle IpfsDHT type too.
hsanjuan Nov 12, 2025
267b42f
boxo: update to feat/1004-get-closest-peers
hsanjuan Nov 12, 2025
49a9cff
go mod tidy
hsanjuan Nov 12, 2025
8b81a40
Update to boxo with merged GetClosestPeers
hsanjuan Nov 18, 2025
0b07da9
Merge remote-tracking branch 'origin/master' into feat/get-closest-peers
hsanjuan Nov 19, 2025
693f112
Restore routing helpers
hsanjuan Nov 19, 2025
638be7b
Add missing import
hsanjuan Nov 19, 2025
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
2 changes: 1 addition & 1 deletion config/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const (
DefaultInlineDNSLink = false
DefaultDeserializedResponses = true
DefaultDisableHTMLErrors = false
DefaultExposeRoutingAPI = false
DefaultExposeRoutingAPI = true
DefaultDiagnosticServiceURL = "https://check.ipfs.network"

// Gateway limit defaults from boxo
Expand Down
59 changes: 59 additions & 0 deletions core/corehttp/routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package corehttp

import (
"context"
"errors"
"fmt"
"net"
"net/http"
"time"
Expand All @@ -13,6 +15,9 @@ import (
"github.com/ipfs/boxo/routing/http/types/iter"
cid "github.com/ipfs/go-cid"
core "github.com/ipfs/kubo/core"
dht "github.com/libp2p/go-libp2p-kad-dht"
"github.com/libp2p/go-libp2p-kad-dht/dual"
"github.com/libp2p/go-libp2p-kad-dht/fullrt"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/routing"
)
Expand Down Expand Up @@ -96,6 +101,60 @@ func (r *contentRouter) PutIPNS(ctx context.Context, name ipns.Name, record *ipn
return r.n.Routing.PutValue(ctx, string(name.RoutingKey()), raw)
}

func (r *contentRouter) GetClosestPeers(ctx context.Context, key cid.Cid) (iter.ResultIter[*types.PeerRecord], error) {
// Per the spec, if the peer ID is empty, we should use self.
if key == cid.Undef {
return nil, errors.New("GetClosestPeers key is undefined")
}

keyStr := string(key.Hash())
var peers []peer.ID
var err error

if r.n.DHTClient == nil {
return nil, fmt.Errorf("GetClosestPeers not supported: DHT is not available")
}

switch dhtClient := r.n.DHTClient.(type) {
case *dual.DHT:
// Only use WAN DHT for public HTTP Routing API.
// LAN DHT contains private network peers that should not be exposed publicly.
if dhtClient.WAN == nil {
return nil, fmt.Errorf("GetClosestPeers not supported: WAN DHT is not available")
}
peers, err = dhtClient.WAN.GetClosestPeers(ctx, keyStr)
case *fullrt.FullRT:
peers, err = dhtClient.GetClosestPeers(ctx, keyStr)
case *dht.IpfsDHT:
peers, err = dhtClient.GetClosestPeers(ctx, keyStr)
default:
return nil, fmt.Errorf("GetClosestPeers not supported for DHT type %T", r.n.DHTClient)
}

if err != nil {
return nil, err
}

// We have some DHT-closest peers. Find addresses for them.
// The addresses should be in the peerstore.
records := make([]*types.PeerRecord, 0, len(peers))
for _, p := range peers {
addrs := r.n.Peerstore.Addrs(p)
rAddrs := make([]types.Multiaddr, len(addrs))
for i, addr := range addrs {
rAddrs[i] = types.Multiaddr{Multiaddr: addr}
}
record := types.PeerRecord{
ID: &p,
Schema: types.SchemaPeer,
Addrs: rAddrs,
}
records = append(records, &record)
}

return iter.ToResultIter(iter.FromSlice(records)), nil
}

type peerChanIter struct {
ch <-chan peer.AddrInfo
cancel context.CancelFunc
Expand Down
4 changes: 4 additions & 0 deletions docs/changelogs/v0.39.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ The `go-ipfs` name was deprecated in 2022 and renamed to `kubo`. Starting with t

All users should migrate to the `kubo` name in their scripts and configurations.

#### 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).

Comment on lines +171 to +174
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably thought of this better, but, assuming someone has exposed the gateway listen address already (:8080), doesn't this inadvertently exposes the routing API to the world?

And, if so, how does this interact with certain gateway options... for example NoFetch means the gateway will not trigger lookups nor fetch any content, but now this API would allow for such lookups to be performed, so it seems a door that was closed is being opened. There are also other gateway options that might make similar considerations.

### 📦️ Important dependency updates

- update `go-libp2p` to [v0.45.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.45.0) (incl. [v0.44.0](https://github.com/libp2p/go-libp2p/releases/tag/v0.44.0)) with self-healing UPnP port mappings and go-log/slog interop fixes
Expand Down
2 changes: 1 addition & 1 deletion docs/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,7 @@ Kubo will filter out routing results which are not actionable, for example, all
graphsync providers will be skipped. If you need a generic pass-through, see
standalone router implementation named [someguy](https://github.com/ipfs/someguy).

Default: `false`
Default: `true`

Type: `flag`

Expand Down
2 changes: 1 addition & 1 deletion docs/examples/kubo-as-a-library/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ go 1.25
replace github.com/ipfs/kubo => ./../../..

require (
github.com/ipfs/boxo v0.35.2
github.com/ipfs/boxo v0.35.3-0.20251118170232-e71f50ea2263
github.com/ipfs/kubo v0.0.0-00010101000000-000000000000
github.com/libp2p/go-libp2p v0.45.0
github.com/multiformats/go-multiaddr v0.16.1
Expand Down
4 changes: 2 additions & 2 deletions docs/examples/kubo-as-a-library/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,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.2 h1:0QZJJh6qrak28abENOi5OA8NjBnZM4p52SxeuIDqNf8=
github.com/ipfs/boxo v0.35.2/go.mod h1:bZn02OFWwJtY8dDW9XLHaki59EC5o+TGDECXEbe1w8U=
github.com/ipfs/boxo v0.35.3-0.20251118170232-e71f50ea2263 h1:7sSi4euS5Rb+RwQZOXrd/fURpC9kgbESD4DPykaLy0I=
github.com/ipfs/boxo v0.35.3-0.20251118170232-e71f50ea2263/go.mod h1:bZn02OFWwJtY8dDW9XLHaki59EC5o+TGDECXEbe1w8U=
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=
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,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.2
github.com/ipfs/boxo v0.35.3-0.20251118170232-e71f50ea2263
github.com/ipfs/go-block-format v0.2.3
github.com/ipfs/go-cid v0.5.0
github.com/ipfs/go-cidutil v0.1.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,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.2 h1:0QZJJh6qrak28abENOi5OA8NjBnZM4p52SxeuIDqNf8=
github.com/ipfs/boxo v0.35.2/go.mod h1:bZn02OFWwJtY8dDW9XLHaki59EC5o+TGDECXEbe1w8U=
github.com/ipfs/boxo v0.35.3-0.20251118170232-e71f50ea2263 h1:7sSi4euS5Rb+RwQZOXrd/fURpC9kgbESD4DPykaLy0I=
github.com/ipfs/boxo v0.35.3-0.20251118170232-e71f50ea2263/go.mod h1:bZn02OFWwJtY8dDW9XLHaki59EC5o+TGDECXEbe1w8U=
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=
Expand Down
138 changes: 138 additions & 0 deletions test/cli/delegated_routing_v1_http_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ package cli

import (
"context"
"encoding/json"
"strings"
"testing"
"time"

"github.com/google/uuid"
"github.com/ipfs/boxo/autoconf"
"github.com/ipfs/boxo/ipns"
"github.com/ipfs/boxo/routing/http/client"
"github.com/ipfs/boxo/routing/http/types"
Expand All @@ -14,8 +18,14 @@ import (
"github.com/ipfs/kubo/test/cli/harness"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/stretchr/testify/assert"
"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()

Expand Down Expand Up @@ -143,4 +153,132 @@ func TestRoutingV1Server(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "/ipfs/"+cidStr, value.String())
})

t.Run("GetClosestPeers returns error when DHT is disabled", func(t *testing.T) {
t.Parallel()

// Test various routing types that don't support DHT
routingTypes := []string{"none", "delegated", "custom"}
for _, routingType := range routingTypes {
t.Run("routing_type="+routingType, func(t *testing.T) {
t.Parallel()

// Create node with specified routing type (DHT disabled)
node := harness.NewT(t).NewNode().Init()
node.UpdateConfig(func(cfg *config.Config) {
cfg.Gateway.ExposeRoutingAPI = config.True
cfg.Routing.Type = config.NewOptionalString(routingType)

// For custom routing type, we need to provide minimal valid config
// otherwise daemon startup will fail
if routingType == "custom" {
// Configure a minimal HTTP router (no DHT)
cfg.Routing.Routers = map[string]config.RouterParser{
"http-only": {
Router: config.Router{
Type: config.RouterTypeHTTP,
Parameters: config.HTTPRouterParams{
Endpoint: "https://delegated-ipfs.dev",
},
},
},
}
cfg.Routing.Methods = map[config.MethodName]config.Method{
config.MethodNameProvide: {RouterName: "http-only"},
config.MethodNameFindProviders: {RouterName: "http-only"},
config.MethodNameFindPeers: {RouterName: "http-only"},
config.MethodNameGetIPNS: {RouterName: "http-only"},
config.MethodNamePutIPNS: {RouterName: "http-only"},
}
}

// For delegated routing type, ensure we have at least one HTTP router
// to avoid daemon startup failure
if routingType == "delegated" {
// Use a minimal delegated router configuration
cfg.Routing.DelegatedRouters = []string{"https://delegated-ipfs.dev"}
// Delegated routing doesn't support providing, must be disabled
cfg.Provide.Enabled = config.False
}
})
node.StartDaemon()

c, err := client.New(node.GatewayURL())
require.NoError(t, err)

// Try to get closest peers - should fail gracefully with an error
testCid, err := cid.Decode("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn")
require.NoError(t, err)

_, err = c.GetClosestPeers(context.Background(), 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
errStr := err.Error()
assert.True(t,
strings.Contains(errStr, "not supported") ||
strings.Contains(errStr, "not available") ||
strings.Contains(errStr, "500"),
"Expected error indicating DHT not available for routing type %s, got: %s", routingType, errStr)
})
}
})

t.Run("GetClosestPeers returns peers for self", func(t *testing.T) {
t.Parallel()

routingTypes := []string{"auto", "autoclient", "dht", "dhtclient"}
for _, routingType := range routingTypes {
t.Run("routing_type="+routingType, func(t *testing.T) {
t.Parallel()

// Single node with DHT and real bootstrap peers
node := harness.NewT(t).NewNode().Init()
node.UpdateConfig(func(cfg *config.Config) {
cfg.Gateway.ExposeRoutingAPI = config.True
cfg.Routing.Type = config.NewOptionalString(routingType)
// Set real bootstrap peers from boxo/autoconf
cfg.Bootstrap = autoconf.FallbackBootstrapPeers
})
node.StartDaemon()

// 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)
}, 30*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())

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")

// Verify structure of returned records
for _, record := range records {
assert.Equal(t, types.SchemaPeer, record.Schema)
assert.NotNil(t, record.ID)
assert.NotEmpty(t, record.Addrs, "peer record should have addresses")
}
})
}
})
}
42 changes: 35 additions & 7 deletions test/cli/testutils/httprouting/mock_http_content_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ import (
// (https://specs.ipfs.tech/routing/http-routing-v1/) server implementation
// based on github.com/ipfs/boxo/routing/http/server
type MockHTTPContentRouter struct {
m sync.Mutex
provideBitswapCalls int
findProvidersCalls int
findPeersCalls int
providers map[cid.Cid][]types.Record
peers map[peer.ID][]*types.PeerRecord
Debug bool
m sync.Mutex
provideBitswapCalls int
findProvidersCalls int
findPeersCalls int
getClosestPeersCalls int
providers map[cid.Cid][]types.Record
peers map[peer.ID][]*types.PeerRecord
Debug bool
}

func (r *MockHTTPContentRouter) FindProviders(ctx context.Context, key cid.Cid, limit int) (iter.ResultIter[types.Record], error) {
Expand Down Expand Up @@ -115,3 +116,30 @@ func (r *MockHTTPContentRouter) AddProvider(key cid.Cid, record types.Record) {
r.peers[*pid] = append(r.peers[*pid], peerRecord)
}
}

func (r *MockHTTPContentRouter) GetClosestPeers(ctx context.Context, key cid.Cid) (iter.ResultIter[*types.PeerRecord], error) {
r.m.Lock()
defer r.m.Unlock()
r.getClosestPeersCalls++

if r.peers == nil {
r.peers = make(map[peer.ID][]*types.PeerRecord)
}
pid, err := peer.FromCid(key)
if err != nil {
return iter.FromSlice([]iter.Result[*types.PeerRecord]{}), nil
}
records, found := r.peers[pid]
if !found {
return iter.FromSlice([]iter.Result[*types.PeerRecord]{}), nil
}

results := make([]iter.Result[*types.PeerRecord], len(records))
for i, rec := range records {
results[i] = iter.Result[*types.PeerRecord]{Val: rec}
if r.Debug {
fmt.Printf("MockHTTPContentRouter.GetPeers(%s) result: %+v\n", pid.String(), rec)
}
}
return iter.FromSlice(results), nil
}
2 changes: 1 addition & 1 deletion test/dependencies/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,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.2 // indirect
github.com/ipfs/boxo v0.35.3-0.20251118170232-e71f50ea2263 // 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.5.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions test/dependencies/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,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.2 h1:0QZJJh6qrak28abENOi5OA8NjBnZM4p52SxeuIDqNf8=
github.com/ipfs/boxo v0.35.2/go.mod h1:bZn02OFWwJtY8dDW9XLHaki59EC5o+TGDECXEbe1w8U=
github.com/ipfs/boxo v0.35.3-0.20251118170232-e71f50ea2263 h1:7sSi4euS5Rb+RwQZOXrd/fURpC9kgbESD4DPykaLy0I=
github.com/ipfs/boxo v0.35.3-0.20251118170232-e71f50ea2263/go.mod h1:bZn02OFWwJtY8dDW9XLHaki59EC5o+TGDECXEbe1w8U=
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=
Expand Down
Loading