Installing and using Periplus
Periplus (PRP) is a cryptographic mesh-networking protocol; plexus is its Rust
stack. There are two binaries: prpd (the daemon / transport node) and periplus
(the command-line tools). Both are statically linked — no runtime, no shared libraries.
Periplus is a fork of Reticulum and is not wire-compatible with it. The protocol is specified in RFC 0001.
Install
Prebuilt binaries (recommended)
Grab the static musl binaries from the downloads, verify the digest,
and put them on your PATH:
curl -LO https://plexus.beer/dist/prpd-x86_64-linux-musl
curl -LO https://plexus.beer/dist/periplus-x86_64-linux-musl
curl -LO https://plexus.beer/dist/SHA256SUMS
sha256sum -c SHA256SUMS # verify before running
install -m755 prpd-x86_64-linux-musl /usr/local/bin/prpd
install -m755 periplus-x86_64-linux-musl /usr/local/bin/periplus
prpd --version && periplus --version
The short prp* command names (prpwalk, prpctl, …) are the same periplus binary
invoked under a different name — it dispatches on argv[0]. Symlink the ones you want
(or just use periplus <verb>, e.g. periplus walk == prpwalk):
cd /usr/local/bin && for c in prpctl prpose prplex prpend prpwalk prpath prping \
prpeer prpoll prpub prpull prpush prpipe prpkey prpsign; do ln -sf periplus "$c"; done
From source
A recent stable Rust toolchain, nothing else:
git clone https://git.itys.net/mjh/plexus.git && cd plexus
cargo build --release # -> target/release/{prpd,periplus}
cargo xtask install-commands # symlink the prp* names alongside the binary
Static (musl), the way the downloads are built:
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl -p plexus-prpd -p plexus-cli
Optional, off by default: --features iroh (an out-of-band Iroh QUIC control/status
plane) and, on plexus-prpd, --features pq (post-quantum link upgrade).
Run as a service
For a persistent node, use the hardened systemd unit deploy/prpd.service from the
source tree: it runs prpd as an unprivileged user with only CAP_NET_ADMIN (for the
plane), keeps the identity + recall cache in /var/lib/periplus so the node's address
survives restarts, and restarts on failure.
install -Dm644 deploy/prpd.service /etc/systemd/system/prpd.service
install -d /etc/periplus && periplus config init > /etc/periplus/periplus.toml # then edit
systemctl daemon-reload && systemctl enable --now prpd
Usage
Run a node
The daemon owns the protocol; the periplus CLI talks to it and to the mesh. The
simplest node listens for peers and announces itself:
prpd --listen 0.0.0.0:4242
To join an existing network, describe your links in a config file instead of the bare flags:
periplus config init > periplus.toml # a commented starter
$EDITOR periplus.toml
prpd --config periplus.toml
Each [[interface]] block is one link; type is TCPServer (listen), TCPClient (dial
a peer), or Auto (zero-config IPv6-multicast LAN discovery):
[[interface]]
type = "TCPClient"
peer = "hub.example.org:4242"
prpd writes its identity to plexus.identity (keep it — it is your address), a
recall cache to plexus.known, and a control socket at /tmp/prpd.sock that the CLI
uses. prpd --help lists every flag.
Identities & addresses
Your address is the hash of your keys — there is no registrar. Manage identities with
periplus id (aka prpkey):
periplus id generate node.identity # mint an identity
periplus id show node.identity # its identity hash (= address) + public key
periplus id hash node.identity plexus.node # the destination address for an app.aspect
Inspect & control a running node
These talk to the local prpd over its control socket:
periplus status # interfaces + traffic (prpctl interfaces)
periplus path # the path table (prpctl paths)
prplex # routing graph: destinations grouped by interface
prpeer list # known peers
prpub # tell prpd to announce now
prpend <dest-hash> # dry-run: resolve a destination + report reachability, sends nothing
prpctl is the automation-facing variant (prpctl paths --json).
Reach other nodes
# Round-trip reachability + RTT — a signed-proof ping:
prping <dest-hash> --peer <hub:port>
# Walk the known path (hops, next-hop, interface) to a destination:
prpwalk <dest-hash>
# Send / receive / fetch a file over the mesh (prpush and prpull are shorthands):
periplus copy <file> <dest-hash> --peer <hub:port> # send
periplus copy --listen --bind 0.0.0.0:5000 --save . # receive
periplus copy --fetch <remote-path> <listener-hash> --peer <hub:port> --identity node.identity
# A full-duplex byte pipe (netcat over the mesh):
prpipe --listen --bind 0.0.0.0:5000 # one end
echo hello | prpipe <dest-hash> --peer <hub:port> --identity node.identity
For prping to get a reply, the target's prpd must answer probes — run it with
--respond-to-probes. Remote status of another node:
prpoll <node-id-hash> --peer <hub:port> --identity node.identity # = periplus status --remote
The fd73 plane — IP over the mesh
Bring up the fd73::/16 IPv6 overlay on a TUN device (needs CAP_NET_ADMIN); ordinary
IP tools then ride the mesh:
sudo prpd --config periplus.toml --plane plexus0 --plane-name ord-01
# prpd logs its fd73:: address; from another plane host:
ping6 <fd73-address>
Add the built-in resolver so names work for unmodified programs — <name>.host.fd73
resolves to a node's fd73:: address, and the daemon forwards everything else upstream:
sudo prpd --config periplus.toml --plane plexus0 --plane-name ord-01 \
--plane-dns 127.0.0.1:53 --plane-upstream 1.1.1.1:53
# point the host's resolver at 127.0.0.1, then:
ping6 ord-01.host.fd73
ssh me@mjh.host.fd73
Enrollment and naming are loopback DNS lookups: dig enroll.ca.fd73 mints a certificate
and registers with the mesh's CA, and dig <name>.enroll.dns.fd73 claims a name. Run a
CA with --ca; trust one elsewhere with --trust-root <its public key>.
Full protocol details are in RFC 0001. --help on any command or
subcommand prints its full options.