Skip to content

Go: your own libp2p host

This page shows how to use pkg/client on a go-libp2p host your application already has, such as one that also runs a DHT, GossipSub or your own protocols. If you are starting from nothing, follow the Quickstart instead.

client.New takes any host.Host. It opens a stream per request and never closes the host, so the host can serve the rest of your application. To reach a go-ricochet 1.1.0 server, the host needs the three things the server’s host uses.

Layer Requirement go-libp2p option
Transport UDX, from github.com/stephanfeb/go-libp2p-udx-transport libp2p.Transport(udxtransport.NewTransport)
Security Noise libp2p.Security(noise.ID, noise.New); included in go-libp2p’s defaults
Muxer Yamux, /yamux/1.0.0 libp2p.Muxer("/yamux/1.0.0", yamux.DefaultTransport); the go-libp2p default
Identity Any libp2p key; Ed25519 if you call client.WithEncryption libp2p.Identity(priv)

The transport is the one change most hosts need. The server listens on nothing but UDX, so a host with only go-libp2p’s default transports (TCP, QUIC, WebSocket, WebTransport, WebRTC) cannot dial it. libp2p.New() with no options builds exactly such a host.

go-ricochet 1.1.0 pins these versions in its go.mod. Match them, or at least the UDX wire version.

Module Version
Go 1.25.7
github.com/libp2p/go-libp2p v0.49.0
github.com/stephanfeb/go-libp2p-udx-transport v0.1.8
github.com/stephanfeb/go-udx (through the transport) v0.1.3, UDX wire version 3
Terminal window
go get github.com/stephanfeb/go-ricochet@v1.1.0
go get github.com/stephanfeb/go-libp2p-udx-transport@v0.1.8

libp2p.Transport replaces go-libp2p’s default transport set rather than adding to it. To keep your existing transports, list libp2p.DefaultTransports (or your own set) as well.

host.go
// Sketch: a host that keeps go-libp2p's default transports and adds UDX.
package main
import (
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/host"
udxtransport "github.com/stephanfeb/go-libp2p-udx-transport"
)
func newHost(priv crypto.PrivKey) (host.Host, error) {
return libp2p.New(
libp2p.Identity(priv),
libp2p.DefaultTransports,
libp2p.Transport(udxtransport.NewTransport),
libp2p.ListenAddrStrings(
"/ip4/0.0.0.0/tcp/0",
"/ip4/0.0.0.0/udp/0/udx",
),
// go-libp2p's default security (TLS and Noise) and muxer (Yamux)
// already include what the server speaks.
)
}

If your host sets libp2p.Security or libp2p.Muxer explicitly, it loses the defaults for that layer: make sure Noise and /yamux/1.0.0 are in the list.

If the host exists only to talk to Ricochet, build it the way go-ricochet’s own examples/mailbox does, with UDX alone:

// From examples/mailbox: a host that speaks what the server speaks.
h, err := libp2p.New(
libp2p.Identity(priv),
libp2p.NoTransports,
libp2p.Transport(udxtransport.NewTransport),
libp2p.ListenAddrStrings("/ip4/0.0.0.0/udp/0/udx"),
libp2p.Security(noise.ID, noise.New),
libp2p.Muxer("/yamux/1.0.0", yamux.DefaultTransport),
)

Connect to the server, then create the client with that server as a preference.

ricochet.go
// Sketch: attach a Ricochet client to an existing host h.
info, err := peer.AddrInfoFromString(
"/ip4/203.0.113.10/udp/55223/udx/p2p/12D3KooW...")
if err != nil {
return err
}
if err := h.Connect(ctx, *info); err != nil {
return err
}
cl := client.New(h, client.Config{
PreferredServers: []client.ServerPreference{
{PeerID: info.ID, Priority: 1},
},
})
defer cl.Close() // removes the client's stream handlers; h stays open
res, err := cl.SendMessage(ctx, recipient, []byte("hello"))

Streams find the server through the peerstore. h.Connect stores its address only briefly, and the connection keeps it there while it lasts; to redial after a drop, call h.Connect again, as examples/mailbox does, or store the address with a longer TTL (h.Peerstore().AddAddrs(info.ID, info.Addrs, peerstore.PermanentAddrTTL)). client.Config has two more fields: ConnectionTimeout (default 10 s) and MessageTimeout (default 30 s). The rest of the API is in Creating a client.

cl.RegisterNotificationHandler registers a stream handler for /ricochet/mailbox-notify/1.0.0 on your host, and cl.Close removes it. The server opens that stream over the connection your host already holds, so the host does not need a public address. Notifications for shared and public mailboxes go out on GossipSub topics instead; pkg/client does not subscribe to them, so if your host runs GossipSub you can subscribe to /ricochet/mailbox/<ownerPeerID>/<folderPath> yourself. See Push notifications.

A client host only dials out, so it works behind NAT without relay or hole punching. The server also runs a Circuit Relay v2 service by default; see NAT traversal & relay.