The connection manager keeps the number of active peer connections within bounds:
Setting
Value
Low watermark
100 peers
High watermark
400 peers
Grace period
1 minute
When the number of connections exceeds the high watermark, the connection manager begins pruning connections down toward the low watermark. New connections are not pruned within the grace period.
cm, err := connmgr.NewConnManager(100, 400, connmgr.WithGracePeriod(time.Minute))
Separately from the connection manager, a libp2p resource manager caps connections and connection rate per source network, so one host cannot occupy the whole peer slate:
Limit
Value
IPv4 per /32 (single address)
maxConnsPerIP connections
IPv6 per /56
maxConnsPerIP connections
IPv6 per /48
4 × maxConnsPerIP connections
IPv4 loopback (127.0.0.0/8)
Unlimited connections
Loopback (127.0.0.0/8, ::1/128)
Exempt from connection rate limiting
Connection rate limiting allows roughly 1 new connection per second per IPv4 /32, with a burst of 2 × maxConnsPerIP and a one-minute grace period.
[!TIP] Multiple nodes behind one address
The per-IP cap is the reason several nodes sharing an egress address (or several nodes on one host) can fail to peer. Raise it with --max-conns-per-ip.
When dataDir is provided, the host generates an Ed25519 keypair on first run and persists it to {dataDir}/host.key. On subsequent starts, the key is loaded from disk, giving the node a stable peer ID across restarts.
{dataDir}/host.key # Ed25519 private key (libp2p marshaled format, mode 0600)
[!WARNING] Key Protection
The host key file is written with mode 0600 (owner read/write only). The data directory is created with mode 0700. Losing this key means the node gets a new peer ID on next start.
If dataDir is empty (e.g., in tests), a new ephemeral identity is generated each time.
The node dials each address on startup with a 10-second timeout per peer. A watchdog goroutine then re-dials any bootstrap peer that is not currently connected every 30 seconds, for the lifetime of the node — it is a permanent reconnection loop, not a one-off retry until first success.
Startup │ ├─ Dial peer A ─── success ├─ Dial peer B ─── fail │ └─ Watchdog (every 30s, forever) ├─ A connected? yes, skip └─ B connected? no, dial again
[!TIP] Multiaddr Format
A full multiaddr includes the transport and peer ID:
func SetupDHT(ctx context.Context, h host.Host) (*dht.IpfsDHT, error)
The DHT is configured with:
Setting
Value
Mode
Server (participates in routing)
Protocol prefix
/xe
The /xe protocol prefix ensures XE nodes form their own DHT, isolated from the public IPFS DHT. Server mode means the node stores and serves routing records, not just queries them.
After creation, Bootstrap() is called to populate the routing table from the existing peerstore.
The DHT is used by the Messenger to resolve peer IDs to addresses via FindPeer() when a target peer is not already known.