Files
logos-libp2p-module/tutorial/docs/tutorial_2_custom_config.md

5.6 KiB

Tutorial 2: Custom Node Configuration

In the previous tutorial, we created a node with default settings that binds to a random port. In this tutorial, we'll learn how to configure the node explicitly — setting a fixed port, changing transports, and passing options via both C++ structs and JSON.

Understanding Libp2pModuleOptions

The Libp2pModuleImpl constructor accepts a Libp2pModuleOptions struct that lets you control every aspect of the node. Here are the most common configuration fields:

Field Type Default Description
addrs vector<string> ["/ip4/127.0.0.1/tcp/0"] Listen addresses
transport TransportType TRANSPORT_TYPE_TCP Transport protocol
maxConnections int 50 Max total connections
maxConnsPerPeer int 1 Max connections per peer
mountGossipsub bool true Enable GossipSub
mountKad bool true Enable Kademlia DHT
mountServiceDiscovery bool true Enable service discovery

Binding to a fixed port

By default nodes bind to port 0 (random). To listen on a specific port, provide a custom address in the addrs field.


#include <cstdio>
#include <string>
#include "plugin.h"

int main()
{
    printf("=== Tutorial 2: Custom Node Configuration ===\n\n");

    setLogLevel("fatal");

Method 1: Configure via C++ struct

The most explicit way to configure a node is by constructing Libp2pModuleOptions directly and passing it to the constructor.

    Libp2pModuleOptions options;

    // Listen on TCP port 9090 on all interfaces
    options.addrs = {"/ip4/0.0.0.0/tcp/9090"};

    // Limit to 10 total connections
    options.maxConnections = 10;

    // Allow up to 2 connections from the same peer
    options.maxConnsPerPeer = 2;

    // We don't need GossipSub or Kademlia for this basic example
    options.mountGossipsub = false;
    options.mountKad = false;
    options.mountServiceDiscovery = false;

    printf("Creating node with custom configuration...\n");
    printf("  Listen address: %s\n", options.addrs[0].c_str());
    printf("  Max connections: %d\n", options.maxConnections);
    printf("  Max per peer: %d\n", options.maxConnsPerPeer);

    Libp2pModuleImpl node(options);

    StdLogosResult startRes = node.start();
    if (!startRes.success) {
        fprintf(stderr, "Failed to start node: %s\n",
                startRes.error.c_str());
        return 1;
    }

Verify that the node is actually listening on the configured port:

    StdLogosResult info = node.peerInfo();
    if (!info.success) {
        fprintf(stderr, "Failed to get node info: %s\n",
                info.error.c_str());
        return 1;
    }

    printf("\nNode is live!\n");
    printf("  Peer ID: %s\n",
           info.value["peerId"].get<std::string>().c_str());
    for (const auto& addr : info.value["addrs"]) {
        printf("  Listening on: %s\n",
               addr.get<std::string>().c_str());
    }

    node.stop();

Method 2: Configure via JSON

When integrating with larger systems (e.g. logoscore), you'll often receive configuration as JSON. The same options can be supplied via Libp2pModuleOptions::fromJson().

    printf("\n--- Method 2: JSON configuration ---\n");

    std::string jsonConfig = R"({
        "addrs": ["/ip4/127.0.0.1/tcp/9091"],
        "maxConnections": 20,
        "mountGossipsub": false,
        "mountKad": false,
        "mountServiceDiscovery": false
    })";

    bool parseOk;
    std::string parseErr;
    auto jsonOpts = Libp2pModuleOptions::fromJson(jsonConfig, parseOk, &parseErr);

    if (!parseOk) {
        fprintf(stderr, "Failed to parse JSON config: %s\n",
                parseErr.c_str());
        return 1;
    }

    printf("Parsed JSON config:\n");
    printf("  Listen address: %s\n", jsonOpts.addrs[0].c_str());
    printf("  Max connections: %d\n", jsonOpts.maxConnections);

    Libp2pModuleImpl nodeFromJson(jsonOpts);

    StdLogosResult jsonStartRes = nodeFromJson.start();
    if (!jsonStartRes.success) {
        fprintf(stderr, "Failed to start JSON-configured node: %s\n",
                jsonStartRes.error.c_str());
        return 1;
    }

    StdLogosResult info2 = nodeFromJson.peerInfo();
    if (!info2.success) {
        fprintf(stderr, "Failed to get JSON-configured node info: %s\n",
                info2.error.c_str());
        return 1;
    }

    printf("\nJSON-configured node is live!\n");
    printf("  Peer ID: %s\n",
           info2.value["peerId"].get<std::string>().c_str());
    for (const auto& addr : info2.value["addrs"]) {
        printf("  Listening on: %s\n",
               addr.get<std::string>().c_str());
    }

    nodeFromJson.stop();

    return 0;
}

Summary

In this tutorial you learned:

  • How to configure a node with a fixed port
  • How to control connection limits
  • How to pass options via C++ struct or JSON
  • How to verify the node's actual bound addresses

Run tutorial

./build/tutorial/tutorial_2_custom_config

Exercise: Try different configurations

Experiment with these variations:

// QUIC transport instead of TCP
options.transport = TRANSPORT_TYPE_QUIC;
options.addrs = {"/ip4/127.0.0.1/udp/9092/quic-v1"};

// Listen on multiple addresses
options.addrs = {
    "/ip4/0.0.0.0/tcp/9093",
    "/ip4/0.0.0.0/tcp/9094"
};

← Creating and Starting a libp2p Node  |  Connecting Peers and Exchanging Data →