@@ -15,27 +15,26 @@ import (
1515 "github.com/ipfs/boxo/files"
1616 "github.com/ipfs/boxo/path"
1717 icore "github.com/ipfs/kubo/core/coreiface"
18+ options "github.com/ipfs/kubo/core/coreiface/options"
1819 ma "github.com/multiformats/go-multiaddr"
1920
2021 "github.com/ipfs/kubo/config"
2122 "github.com/ipfs/kubo/core"
2223 "github.com/ipfs/kubo/core/coreapi"
2324 "github.com/ipfs/kubo/core/node/libp2p"
24- "github.com/ipfs/kubo/plugin/loader" // This package is needed so that all the preloaded plugins are loaded automatically
25+ "github.com/ipfs/kubo/plugin/loader" // registers built-in plugins
2526 "github.com/ipfs/kubo/repo/fsrepo"
2627 "github.com/libp2p/go-libp2p/core/peer"
2728)
2829
2930/// ------ Setting up the IPFS Repo
3031
3132func setupPlugins (externalPluginsPath string ) error {
32- // Load any external plugins if available on externalPluginsPath
3333 plugins , err := loader .NewPluginLoader (filepath .Join (externalPluginsPath , "plugins" ))
3434 if err != nil {
3535 return fmt .Errorf ("error loading plugins: %s" , err )
3636 }
3737
38- // Load preloaded and external plugins
3938 if err := plugins .Initialize (); err != nil {
4039 return fmt .Errorf ("error initializing plugins: %s" , err )
4140 }
@@ -53,37 +52,36 @@ func createTempRepo() (string, error) {
5352 return "" , fmt .Errorf ("failed to get temp dir: %s" , err )
5453 }
5554
56- // Create a config with default options and a 2048 bit key
57- cfg , err := config .Init (io .Discard , 2048 )
55+ identity , err := config .CreateIdentity (io .Discard , []options.KeyGenerateOption {
56+ options .Key .Type (options .Ed25519Key ),
57+ })
58+ if err != nil {
59+ return "" , err
60+ }
61+ cfg , err := config .InitWithIdentity (identity )
5862 if err != nil {
5963 return "" , err
6064 }
6165
62- // Use TCP-only on loopback with random port for reliable local testing.
63- // This matches what kubo's test harness uses (test/cli/transports_test.go).
64- // QUIC/UDP transports are avoided because they may be throttled on CI.
66+ // TCP on loopback with a random port. QUIC/UDP is disabled because it can
67+ // be throttled on some networks; TCP is more reliable for local testing.
6568 cfg .Addresses .Swarm = []string {
6669 "/ip4/127.0.0.1/tcp/0" ,
6770 }
68-
69- // Explicitly disable non-TCP transports for reliability.
7071 cfg .Swarm .Transports .Network .QUIC = config .False
7172 cfg .Swarm .Transports .Network .Relay = config .False
7273 cfg .Swarm .Transports .Network .WebTransport = config .False
7374 cfg .Swarm .Transports .Network .WebRTCDirect = config .False
7475 cfg .Swarm .Transports .Network .Websocket = config .False
7576 cfg .AutoTLS .Enabled = config .False
7677
77- // Disable routing - we don't need DHT for direct peer connections.
78- // Bitswap works with directly connected peers without needing DHT lookups.
78+ // No DHT: we connect peers by address, so content routing is not needed.
7979 cfg .Routing .Type = config .NewOptionalString ("none" )
8080
81- // Disable bootstrap for this example - we manually connect only the peers we need.
81+ // No automatic bootstrap: we connect only the peers we need.
8282 cfg .Bootstrap = []string {}
8383
84- // When creating the repository, you can define custom settings on the repository, such as enabling experimental
85- // features (See experimental-features.md) or customizing the gateway endpoint.
86- // To do such things, you should modify the variable `cfg`. For example:
84+ // Optional: enable experimental features by modifying cfg before Init, e.g.:
8785 if * flagExp {
8886 // https://github.com/ipfs/kubo/blob/master/docs/experimental-features.md#ipfs-filestore
8987 cfg .Experimental .FilestoreEnabled = true
@@ -94,10 +92,8 @@ func createTempRepo() (string, error) {
9492 // https://github.com/ipfs/kubo/blob/master/docs/experimental-features.md#p2p-http-proxy
9593 cfg .Experimental .P2pHttpProxy = true
9694 // See also: https://github.com/ipfs/kubo/blob/master/docs/config.md
97- // And: https://github.com/ipfs/kubo/blob/master/docs/experimental-features.md
9895 }
9996
100- // Create the repo with the config
10197 err = fsrepo .Init (repoPath , cfg )
10298 if err != nil {
10399 return "" , fmt .Errorf ("failed to init ephemeral node: %s" , err )
@@ -108,23 +104,18 @@ func createTempRepo() (string, error) {
108104
109105/// ------ Spawning the node
110106
111- // Creates an IPFS node and returns its coreAPI .
107+ // createNode opens the repo at repoPath and starts an IPFS node .
112108func createNode (ctx context.Context , repoPath string ) (* core.IpfsNode , error ) {
113- // Open the repo
114109 repo , err := fsrepo .Open (repoPath )
115110 if err != nil {
116111 return nil , err
117112 }
118113
119- // Construct the node
120-
121114 nodeOptions := & core.BuildCfg {
122115 Online : true ,
123- // For this example, we use NilRouterOption (no routing) since we connect peers directly.
124- // Bitswap works with directly connected peers without needing DHT lookups.
125- // In production, you would typically use:
126- // Routing: libp2p.DHTOption, // Full DHT node (stores and fetches records)
127- // Routing: libp2p.DHTClientOption, // DHT client (only fetches records)
116+ // No routing: peers are connected directly by address.
117+ // In production use libp2p.DHTClientOption or libp2p.DHTOption
118+ // so the node can find content and peers on the wider network.
128119 Routing : libp2p .NilRouterOption ,
129120 Repo : repo ,
130121 }
@@ -134,7 +125,7 @@ func createNode(ctx context.Context, repoPath string) (*core.IpfsNode, error) {
134125
135126var loadPluginsOnce sync.Once
136127
137- // Spawns a node to be used just for this run (i.e. creates a tmp repo) .
128+ // spawnEphemeral creates a temporary repo, starts a node, and returns its API .
138129func spawnEphemeral (ctx context.Context ) (icore.CoreAPI , * core.IpfsNode , error ) {
139130 var onceErr error
140131 loadPluginsOnce .Do (func () {
@@ -144,7 +135,6 @@ func spawnEphemeral(ctx context.Context) (icore.CoreAPI, *core.IpfsNode, error)
144135 return nil , nil , onceErr
145136 }
146137
147- // Create a Temporary Repo
148138 repoPath , err := createTempRepo ()
149139 if err != nil {
150140 return nil , nil , fmt .Errorf ("failed to create temp repo: %s" , err )
@@ -222,21 +212,12 @@ func main() {
222212 ctx , cancel := context .WithTimeout (context .Background (), 2 * time .Minute )
223213 defer cancel ()
224214
225- // Spawn a local peer using a temporary path, for testing purposes
215+ // Spawn a local peer using a temporary path, for testing purposes.
226216 ipfsA , nodeA , err := spawnEphemeral (ctx )
227217 if err != nil {
228218 panic (fmt .Errorf ("failed to spawn peer node: %s" , err ))
229219 }
230220
231- peerCidFile , err := ipfsA .Unixfs ().Add (ctx ,
232- files .NewBytesFile ([]byte ("hello from ipfs 101 in Kubo" )))
233- if err != nil {
234- panic (fmt .Errorf ("could not add File: %s" , err ))
235- }
236-
237- fmt .Printf ("Added file to peer with CID %s\n " , peerCidFile .String ())
238-
239- // Spawn a node using a temporary path, creating a temporary repo for the run
240221 fmt .Println ("Spawning Kubo node on a temporary repo" )
241222 ipfsB , _ , err := spawnEphemeral (ctx )
242223 if err != nil {
@@ -245,6 +226,27 @@ func main() {
245226
246227 fmt .Println ("IPFS node is running" )
247228
229+ // Connect nodeB to nodeA before adding content. This lets the connection
230+ // finish its setup during the Add below, so the fetch in Part IV is fast.
231+ peerAddrs , err := ipfsA .Swarm ().LocalAddrs (ctx )
232+ if err != nil {
233+ panic (fmt .Errorf ("could not get peer addresses: %s" , err ))
234+ }
235+ peerMa := peerAddrs [0 ].String () + "/p2p/" + nodeA .Identity .String ()
236+ fmt .Println ("Connecting to peer..." )
237+ if err := connectToPeers (ctx , ipfsB , []string {peerMa }); err != nil {
238+ panic (fmt .Errorf ("failed to connect to peer: %s" , err ))
239+ }
240+ fmt .Println ("Connected to peer" )
241+
242+ peerCidFile , err := ipfsA .Unixfs ().Add (ctx ,
243+ files .NewBytesFile ([]byte ("hello from ipfs 101 in Kubo" )))
244+ if err != nil {
245+ panic (fmt .Errorf ("could not add File: %s" , err ))
246+ }
247+
248+ fmt .Printf ("Added file to peer with CID %s\n " , peerCidFile .String ())
249+
248250 /// --- Part II: Adding a file and a directory to IPFS
249251
250252 fmt .Println ("\n -- Adding and getting back files & directories --" )
@@ -313,29 +315,7 @@ func main() {
313315
314316 /// --- Part IV: Getting a file from another IPFS node
315317
316- fmt .Println ("\n -- Connecting to nodeA and fetching content via bitswap --" )
317-
318- // Get nodeA's actual listening address dynamically.
319- // We configured TCP-only on 127.0.0.1 with random port, so this will be a TCP address.
320- peerAddrs , err := ipfsA .Swarm ().LocalAddrs (ctx )
321- if err != nil {
322- panic (fmt .Errorf ("could not get peer addresses: %s" , err ))
323- }
324- peerMa := peerAddrs [0 ].String () + "/p2p/" + nodeA .Identity .String ()
325-
326- bootstrapNodes := []string {
327- // In production, use real bootstrap peers like:
328- // "/dnsaddr/bootstrap.libp2p.io/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN",
329- // For this example, we only connect to nodeA which has our test content.
330- peerMa ,
331- }
332-
333- fmt .Println ("Connecting to peer..." )
334- err = connectToPeers (ctx , ipfsB , bootstrapNodes )
335- if err != nil {
336- panic (fmt .Errorf ("failed to connect to peers: %s" , err ))
337- }
338- fmt .Println ("Connected to peer" )
318+ fmt .Println ("\n -- Fetching content from nodeA via bitswap --" )
339319
340320 exampleCIDStr := peerCidFile .RootCid ().String ()
341321
0 commit comments