A store-and-forward server for libp2p

The mail server, rebuilt for peer-to-peer.

Peers go offline. go-ricochet holds their messages, documents and feeds until they come back, the way SMTP and NNTP servers did for desktop software before platforms absorbed the client, the server and the data. One node is also a libp2p relay, a GossipSub host and a DHT server.

go get github.com/stephanfeb/go-ricochet/pkg/client
Aliceonline 09:00
ricochetholds ciphertext
Bobonline 18:40
Alice and Bob are never online at the same time. The server holds the message in between.

The idea is old

We had this once.

Store-and-forward made the desktop internet work: you chose your software, someone nearby ran a server, and open protocols connected them all.

1990s · Store and forward

Your client, anyone's server

Mail and news clients on the desktop. SMTP, NNTP and IMAP servers run by ISPs, universities and hobbyists. The protocol was the product.

2010s · The platform

One company's app

Client, server and data merged into a single service. Interoperation stopped. Your contacts, history and audience live in someone else's database.

Now · Ricochet

Store-and-forward, upgraded

Identity is a keypair, not an account. Payloads are end-to-end encrypted. Servers are interchangeable, listed by priority like MX records. NAT traversal is built in.

How it works

Built like a mail server, one agent per job.

Each agent is a libp2p protocol. If you know SMTP and IMAP, you already know the shape.

SenderMSA/sf-network/submitMTAroute & validateMDAstore & push
PostgreSQLmailboxesMAA/sf-network/accessRecipient
MMAmailboxes, ACLsSDAdocumentsSFAfeedsSCAcollectionsNotifypush

Alice's client opens a stream to the server Bob prefers and hands over the message. Encrypted payloads stay sealed: only Bob's key opens them.

cl.SendMessage(ctx, bobID, payload, client.WithEncryption())

What it isn't

Know the trade-offs before you build.

Store-and-forward has costs. Better to hear them here than to find them in production.

Not anonymous

The server sees the envelope: sender, recipient, folder and timestamps.

No forward secrecy

By design. If you need it, run a ratchet such as Signal on top.

Limits are fairness, not security

Identities cost nothing to make, so per-peer quotas share capacity. They don't stop a determined abuser.

Not a platform

No accounts, no global index, no ranking. Your app decides what people see.

For developers

A client in a few lines of Go.

Bring your own go-libp2p host with the UDX transport. The client tries your servers in priority order and reports every refusal as a typed error.

alice.gopkg/client
// The server speaks UDX only: give the host that transport.
h, _ := libp2p.New(
    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),
)

cl := client.New(h, client.Config{
    PreferredServers: []client.ServerPreference{
        {PeerID: serverPeerID, Priority: 1},
    },
})
defer cl.Close()

// Bob is offline. The server holds this until he collects it.
result, err := cl.SendMessage(ctx, bobID, []byte("hello"),
    client.WithEncryption(),
    client.WithExpiry(24 * time.Hour),
)

For operators

There is no hosted service. Run your own node.

Every server is somebody's own. Start one on a laptop to develop against, then put it on a public host for your users. You need PostgreSQL 14 or later.

Docker image: planned
curl -LO https://github.com/stephanfeb/go-ricochet/releases/download/v1.1.0/ricochet_1.1.0_linux_amd64.tar.gz
tar xzf ricochet_1.1.0_linux_amd64.tar.gz
cd ricochet_1.1.0_linux_amd64

psql postgres -c "CREATE ROLE ricochet LOGIN PASSWORD 'secret'"
createdb ricochet && psql ricochet < schema.sql

export RICOCHET_PG_PASSWORD=secret
./ricochet --development --pg-host localhost \
  --pg-database ricochet --pg-username ricochet --pg-sslmode disable

Presets for development, production and high capacity. Health checks, readiness and Prometheus metrics on a separate ops listener.

Clients & ecosystem

Reach it from Go, Dart or JavaScript.

Ricochet is a libp2p server that listens on UDX, a reliable transport over UDP. Any libp2p host with a UDX transport can connect: go-udx, dart-udx and js-udx provide one.