RFC 0001: The Periplus Protocol
- Status: Draft (v1)
- Author: Matthew J. Harmon (mjh@itys.net)
- Date: 2026-05-29
- Reference implementation: the
plexusRust stack (prpd/periplus). - Supersedes: nothing. Derived from: the Reticulum Network Stack (see Appendix A).
Abstract
Periplus (abbreviated PRP, pronounced "perp") is a cryptographic mesh-networking protocol for media that make no promises: LoRa, packet radio, a serial line, a TCP socket — anything that moves bytes. Every destination is a public key; every address is the hash of a key, so to claim an address you must hold the key that hashes to it. Periplus is medium-agnostic, connectionless at its base, and self-certifying at its core. It is derived from, but not wire-compatible with, Reticulum: the identity and key derivations are domain-separated under a Periplus namespace, so a Periplus node and a Reticulum node compute different addresses and cannot read each other's traffic.
This document specifies the Periplus wire protocol and the Plexus extension suite built on it, in enough detail to implement an interoperable node.
1. Introduction
1.1 Relationship to Reticulum
Periplus began as a clean-room reimplementation of the Reticulum Network Stack (RNS) and was, for a time, byte-compatible with it. It has since forked into its own protocol. The fork is realized by domain separation at two cryptographic chokepoints (§2.2): the identity hash and the symmetric key derivation. The byte layout of most structures is inherited from the RNS design — Periplus owes Reticulum the whole shape — but the cryptographic values diverge, so the two networks share a design and are never mistaken for each other. Periplus is not affiliated with the upstream Reticulum project.
1.2 Scope & layering
This RFC specifies two layers:
- The Periplus core protocol (§2–§11): identities, addressing, the packet format, authenticated encryption, announces, single-destination delivery, links, transport, resources, and interface access control. An implementation of this layer is a Periplus node.
- The Plexus extension suite (§12–§15): the
fd73::IPv6 plane, enrollment/CA, naming/DNS, and the negotiated extensions (PQ links, hardware attestation, the Iroh control plane). These ride above the core through reserved context bytes and the announceapp_datafield; a node MAY implement any subset. The seam between the core and the extensions is normative: core conformance MUST NOT depend on any extension.
The names reflect the layering: Periplus is the protocol; Plexus is the Rust
stack that implements it and adds the extension suite. Core domains are named
periplus.*; Plexus-extension domains are named plexus.* (§2.2).
1.3 Requirements language
The key words MUST, MUST NOT, REQUIRED, SHALL, SHOULD, SHOULD NOT, RECOMMENDED, MAY, and OPTIONAL are to be interpreted as in RFC 2119.
1.4 Notation
‖ denotes concatenation. trunc_n(x) is the first n bytes of x. Integers on the
wire are big-endian unless stated. H(x) is SHA-256. Byte-string literals are written
b"…". Field widths are in bytes.
2. Cryptographic primitives & domain separation
2.1 Algorithms
| Purpose | Algorithm |
|---|---|
| Key agreement | X25519 (Curve25519 ECDH) |
| Signatures | Ed25519 (verify_strict, no malleability) |
| Hash | SHA-256 (identities, destinations, packet hashes) |
| Keyed/overlay hash | BLAKE3 (the fd73:: plane, proof-of-work) |
| Symmetric AEAD | the Token: AES-256-CBC + HMAC-SHA-256, encrypt-then-MAC (§5) |
| Key derivation | HKDF-SHA-256 (RFC 5869) |
All MAC and signature comparisons MUST be constant-time. Secret keys MUST be zeroized on
drop. Implementations MUST use verify_strict (or equivalent) for Ed25519.
2.2 Domain-separation constants
These byte strings are the protocol's identity. The two periplus.* core domains are
what diverge Periplus from Reticulum; changing any value defines a different,
non-interoperable network.
| Domain string | Used in | Layer |
|---|---|---|
b"periplus.id.v1" |
identity-hash prefix (§3.2) | core |
b"periplus.kdf.v1" |
HKDF info for link & packet keys (§5, §8.2) |
core |
b"plexus.plane.addr.v1" |
fd73:: plane address (§12.1) |
ext |
b"plexus.plane.endorse.v1" |
CA endorsement signature (§13.2) | ext |
b"plexus.plane.pow.v1" |
enrollment proof-of-work (§13.3) | ext |
b"plexus.hw-attestation.v1" |
hardware identity attestation (§15.3) | ext |
Two additional wire-identity constants diverge from the pre-fork values: the
AutoInterface discovery group id is b"periplus" (§11 note), and the well-known
transport control destination app-name is prptransport (§9.1).
3. Identities & addressing
3.1 Identity
A Periplus identity is a pair of keypairs:
- an X25519 keypair for encryption (key agreement), and
- an Ed25519 keypair for signatures.
The private serialization is exactly 64 bytes: x25519_priv(32) ‖ ed25519_seed(32).
The public identity is enc_pub(32) ‖ sig_pub(32), in that order — the order in which
they are hashed and announced.
3.2 Identity hash
The identity hash is the node's fundamental address:
identity_hash = trunc_16( SHA256( "periplus.id.v1" ‖ enc_pub ‖ sig_pub ) )
Sixteen bytes — an IPv6-address width. The "periplus.id.v1" prefix is the fork's
first divergence point: the same keypair hashes to one value under Reticulum (no
prefix) and a different one under Periplus. To send from an address, a node must hold
the keypair that hashes to it; source spoofing is the second-preimage problem on
SHA-256, not a policy choice.
3.3 Destinations & address derivation
A destination names a service on an identity. Its address is:
name_hash = trunc_10( SHA256( "app_name.aspect1.aspect2.…" ) ) // dotted, UTF-8
address = trunc_16( SHA256( name_hash(10) ‖ identity_hash(16) ) )
This derivation is invariant across implementations and MUST be exact. Destination kinds:
- Single — addressed to one identity; traffic is encrypted to that identity (§7) or carried over a link (§8).
- Group — a shared-key destination: addressed exactly like a Single
(
SHA256(name_hash ‖ group_identity_hash)), but encrypted with a pre-shared 64-byte Token key rather than per-recipient ECDH. - Plain — unencrypted; used for control destinations (e.g. path requests, §9.1).
- Link — the ephemeral per-link destination (§8); its address is the link id.
3.4 Name hashing
name_hash is truncated to 10 bytes (not 16); it is concatenated with the 16-byte
identity hash before the final destination hash. Aspects are joined with ..
4. Packet format
4.1 Header
Every packet begins with a flags byte, a header-type-dependent address region, a hop counter, and a context byte.
byte 0 — flags:
bit 7 bit 6 bit 5 bit 4 bits 3-2 bits 1-0
[ifac] [hdr_type][ctx_flag][transport_type][dest_type] [packet_type]
byte 1 — hops (u8)
H1 (hdr_type=0): flags(1) hops(1) destination(16) context(1) = 19 bytes
H2 (hdr_type=1): flags(1) hops(1) transport_id(16) destination(16) context(1) = 35 bytes
transport_id is present only in H2 and names the next-hop transport node for routed
delivery (§9). ifac (bit 7) marks interface-authenticated framing (§11); most paths
leave it clear. ctx_flag reserves a context extension bit.
4.2 Type enumerations
packet_type : Data=0 Announce=1 LinkRequest=2 Proof=3
dest_type : Single=0 Group=1 Plain=2 Link=3
transport_type: Broadcast=0 Transport=1 Relay=2 Tunnel=3
header_type : H1=0 H2=1
4.3 Context byte
The trailing context byte qualifies the packet body. Reserved values (high range is link/resource control):
| Name | Value | Meaning |
|---|---|---|
| NONE | 0x00 | plain data / proof |
| REQUEST | 0x09 | link request payload (§8.4) |
| RESPONSE | 0x0A | link response payload (§8.4) |
| IP_DATAGRAM | 0xF1 | fd73:: plane fragment (§12.2) |
| EXT_LINK_UPGRADE | 0xF0 | PQ link upgrade (§15.2) |
| KEEPALIVE | 0xFA | link keepalive |
| LINKIDENTIFY | 0xFB | link identification (§8.3) |
| LINKCLOSE | 0xFC | link teardown |
| LINKPROOF | 0xFD | link data proof |
| LRRTT | 0xFE | link-request RTT reply (§8.1) |
| LRPROOF | 0xFF | link-request proof (§8.1) |
RESOURCE*, CACHE_REQUEST, PATH_RESPONSE, COMMAND*, and CHANNEL occupy the
low range; their values are fixed by the codec and listed in §17. 0xF1 and 0xF0 are
allocated from the otherwise-unused 0x0F..0xF9 gap for Plexus extensions.
4.4 MTU
The base link MTU is 500 bytes. Payloads larger than the usable per-packet space are carried by the resource machinery (§10) or, for IP, the plane fragmentation shim (§12.2). Implementations MUST negotiate the effective MTU down to the narrowest interface on a path.
5. The Token (authenticated encryption)
All confidential payloads use the Token, a modified-Fernet AEAD:
Token(plaintext) = IV(16) ‖ AES-256-CBC( PKCS7(plaintext) ) ‖ HMAC-SHA-256( IV ‖ CT )
- Encrypt-then-MAC. The HMAC covers
IV ‖ ciphertext. - No version byte and no timestamp — deliberately, to avoid metadata leakage.
- HMAC verification MUST be constant-time; decryption MUST verify the MAC before unpadding.
- Overhead is
16 + 32 = 48bytes plus PKCS7 padding.
The 64-byte key material is split: bytes 0..32 are the HMAC (signing) key, bytes
32..64 are the AES (encryption) key.
6. Announces
The announce is the protocol's nervous system: a signed, self-authenticating advertisement of a destination and its keys, flooded across the mesh.
6.1 Payload
[ enc_pub(32) ] [ sig_pub(32) ] [ name_hash(10) ] [ random_hash(10) ]
[ ratchet(32, optional) ] [ signature(64) ] [ app_data(variable) ]
6.2 Signature & verification
The Ed25519 signature covers:
destination_hash ‖ enc_pub ‖ sig_pub ‖ name_hash ‖ random_hash ‖ [ratchet] ‖ app_data
A receiver MUST: (1) recompute the destination hash from name_hash and the identity
hash of (enc_pub, sig_pub) and confirm it matches the packet's destination; (2) verify
the signature with sig_pub. Only then is the announce accepted, the keys learned, and
a path recorded (§9.1). Because the signature covers app_data, any extension data
(§15.1) is authenticated by the same act that authenticates the identity.
6.3 app_data and extension TLVs
app_data is opaque to the core. Extension-aware nodes append a TLV sequence after a
two-byte sentinel (§15.1); core nodes treat the trailing bytes as opaque and ignore
them. An announce carrying no extensions is byte-identical to one with none.
7. Single-destination (opportunistic) delivery
A node MAY send to a Single destination without a link, encrypting per-packet:
token_wire = ephemeral_x25519_pub(32) ‖ Token(plaintext)
The sender generates an ephemeral X25519 key, performs ECDH against the destination's encryption key (or its current ratchet, §8.5), derives a Token key (§8.2, salted with the recipient identity hash), and encrypts. The recipient tries its ratchet keys newest -first, then its static key.
Proofs. A destination MAY adopt a PROVE_ALL strategy: on successfully decrypting an
inbound single packet, it returns a Proof/Single packet addressed to
trunc_16(packet_hash) carrying an Ed25519 signature over the full 32-byte packet hash
(implicit-proof form, 64-byte signature). The original sender verifies the signature
against the destination identity and measures round-trip time. This is the prping
ack-and-forth.
8. Links
A link is an encrypted, bidirectional, forward-secret channel between two identities.
8.1 Handshake (three packets)
- LinkRequest (initiator → destination): ephemeral X25519 pubkey, signing pubkey, and a truncated link id.
- LRRTT (destination → initiator, context
0xFE): ephemeral X25519 pubkey, signing pubkey, a signed proof binding the link, and an RTT measurement. - LRPROOF (initiator → destination, context
0xFF): acknowledgment.
The link id is a truncated SHA-256 over the public link material; both sides derive it identically.
8.2 Key derivation
After the handshake both ends hold the same DerivedKey:
DerivedKey = HKDF-SHA-256( salt = link_id(16), ikm = ecdh_shared, info = "periplus.kdf.v1", L = 64 )
The info = "periplus.kdf.v1" is the fork's second divergence point: identical ECDH
secret and salt yield different keys under Periplus than under Reticulum, so link and
single-destination ciphertext is mutually unreadable. For single-destination packets
(§7) the salt is the recipient identity hash instead of a link id; everything else
is identical. The 64-byte output feeds a Token (§5).
8.3 Identification
A link is anonymous until a party proves who it is. The LINKIDENTIFY packet (context
0xFB) carries enc_pub(32) ‖ sig_pub(32) ‖ Ed25519_sign(link_id ‖ pubkeys),
encrypted over the link. The receiver reconstructs the identity, verifies the signature
over link_id ‖ pubkeys, and thereafter attributes the link to that identity hash. This
is the basis of admission control (§8.4) and of the plane's reverse-path filter (§12.3).
8.4 Requests & responses, and admission
Over an established link a node MAY issue a REQUEST (context 0x09) and receive a
RESPONSE (0x0A); payloads are msgpack. The request id is the truncated hash of the
REQUEST packet, derived identically by both ends.
Admission is authenticate-once: the handshake's ECDH + identify is the only asymmetric operation; thereafter the link is a settled symmetric channel and the peer's identity is a fact attached to it. A node enforces a policy — allow-all, allow-none, or an allow-list of identity hashes — by set membership against the link's proven identity (§8.3). Unauthorized requests are dropped silently, returning no signal that would confirm a guessed destination.
8.5 Ratchets & forward secrecy
Handshake key ephemerality gives per-link forward secrecy. A destination MAY advertise a rotating ratchet X25519 key in its announce (§6.1); senders use the current ratchet in place of the static encryption key, and the destination retains a window of recent ratchet privates to decrypt in-flight traffic. Ratchet rotation refreshes key material over a destination's lifetime without changing its address.
8.6 Lifecycle
States: Pending → Handshaking → Active → (Stale) → Closed. A link with no traffic for
KEEPALIVE × STALE_FACTOR becomes stale; a half-open link MUST be reaped after a bounded
handshake timeout. Implementations MUST bound the number of concurrent links.
9. Transport & routing
9.1 Path requests & the path table
Reachability is learned from announces, not configured. A node records, per destination:
the next-hop, the interface, the hop distance, and the time learned. A node that has not
heard a destination MAY send a path request to the well-known Plain control
destination prptransport.path.request; a transport node that knows a path replies, and
the announce that follows populates the requester's table.
9.2 Hops, transport nodes, relays
The header hops byte counts transport relays traversed (the raw wire count; a direct
peer is 0 hops on the wire). A transport node (transport_type = Transport, header
H2) rewrites transport_id, increments hops, and forwards toward the next hop, never
back out the interface a packet arrived on. Relay tables MUST be bounded.
10. Resources
Payloads exceeding a single packet are transferred as a resource: an advertisement announces the transfer (total size, part count, hash, flags), followed by the parts.
- Part count and transfer size MUST be bounded on receipt to prevent allocation abuse.
- Compression: a resource MAY be bzip2-compressed when that is smaller; the
FLAG_COMPRESSEDadvertisement bit signals it. The content hash is computed over the uncompressed payload. - Reliability is per-part with acknowledgement; segmentation supports transfers that exceed the windowed buffer.
11. Interface access control (IFAC)
An interface MAY be placed on a virtual network: a shared network_name +
passphrase derive an IFAC identity, and every packet on that interface is masked and
authenticated.
ifac_key = HKDF-SHA-256( salt = IFAC_SALT, ikm = SHA256(SHA256(netname) ‖ SHA256(netkey)), L = 64 )
ifac_identity = Identity::from_bytes( ifac_key )
tag = trunc_last_n( Ed25519_sign( ifac_identity, clean_packet ) ) // n = IFAC size
masked = packet XOR HKDF-SHA-256( salt = ifac_key, ikm = tag ) // tag left in clear
The header IFAC flag (bit 7) is set. On receipt a node unmasks, authenticates the tag,
and drops on failure — before any further parsing. A node MUST drop IFAC-flagged
packets on a plain interface and plain packets on an IFAC interface. Default IFAC size is
16 bytes (8 on RNode/KISS/AX.25). Note: the default AutoInterface discovery group id is
b"periplus" (diverged from the pre-fork value).
12. The fd73:: plane (IPv6 overlay) — Plexus extension
The plane carries ordinary IPv6 over Periplus links, entered through a TUN device, so
unmodified applications (ping6, ssh) ride the mesh.
12.1 Plane address
plane_address = fd73:: ‖ trunc_14( BLAKE3( "plexus.plane.addr.v1" ‖ enc_pub ‖ sig_pub ) )
A 112-bit suffix under the fd73::/16 ULA prefix. The hash is over the keys, not a
certificate, so the address is as durable as the identity — a cert may be reissued,
endorsed, or revoked without the address moving. fd73::/16 is a deliberate, disjoint
sibling of any structured fd53::/16-style mesh.
12.2 Fragmentation
IPv6 offers 1280-byte datagrams; a link's usable payload is smaller. The plane prepends a
6-byte shim under context 0xF1:
[ id: u32 ] [ index: u8 ] [ count: u8 ] ‖ fragment
Reassembly is best-effort: bounded table, ~3-second timeout, no retransmission. A dropped fragment is a dropped datagram, per the IP contract; reliability belongs to the layers above.
12.3 Reverse-path filtering
An inbound datagram is delivered only if its inner IPv6 source equals the
plane_address derived from the keys that proved themselves (§8.3) on the link it
arrived on. Source spoofing is a hash mismatch, not a firewall rule.
12.4 Neighbor discovery
There is no broadcast segment; the TUN comes up NOARP. Address↔key bindings are learned
from the announce stream (§6) — hearing an announce is neighbor discovery, with
resolution and authentication folded into one event.
13. Enrollment & endorsement — Plexus extension
A self-certifying address is sovereign; a network may still wish to recognize members.
13.1 Tiers
- Self-signed — a node mints its own plane cert (a body + an Ed25519 self-signature) and joins an open plane trust-on-first-use.
- Endorsed — a root signs the node's identity (§13.2), adding a name and an expiry.
- Open + proof-of-work — a public plane MAY require a key-bound PoW (§13.3) to enroll without being free to flood. A CA MAY also rate-limit.
13.2 Endorsement signature
endorsement = Ed25519_sign_root( "plexus.plane.endorse.v1" ‖ sig_pub ‖ not_after ‖ name )
The CA endorses without assigning: it never hands out an address (the address is the keys' already); it staples a name and an expiry onto an existing identity. Revocation is non-renewal. An endorsement MAY be held internally as provenance and need not be released on the wire.
13.3 Proof-of-work
PoW valid iff BLAKE3( "plexus.plane.pow.v1" ‖ enc_pub ‖ sig_pub ‖ nonce ) has ≥ D leading zero bits
The work is bound to the keys, not the address, so a nonce cannot be reused for a
different identity, and difficulty D MAY rise over time without any address moving.
13.4 The CA & the magic-DNS trigger
A node enrolls by asking the network a question: a DNS lookup of enroll.ca.fd73
(answered by the daemon on loopback, §14.3) mints the cert, grinds the PoW once, and
registers with whatever CA the mesh advertises. No configuration or key ceremony.
14. Naming & resolution — Plexus extension
14.1 Two-tier naming
Naming is the honest answer to Zooko's triangle (human-meaningful, globally unique, decentralized — pick two):
- Open petnames — self-asserted in the announce, learned trust-on-first-use, first-heard-wins. Decentralized and cheap; not unique. Conflicts are surfaced.
- Endorsed names — signed into an endorsement by a CA (§13.2); unique and authenticated within that CA, at the cost of asking one.
14.2 host.fd73 resolution
A name resolves at <name>.host.fd73. The node runs its own UDP DNS server (no external
resolver required):
AAAAfor<name>.host.fd73→ the node'sfd73::plane address.PTRin thefd73::/16reverse zone (…3.7.d.f.ip6.arpa) → the name.- Queries outside those zones are forwarded upstream, so the daemon can be a host's only resolver.
ping6 <name>.host.fd73 and ssh <name>.host.fd73 therefore work unmodified.
14.3 Magic-DNS control plane
Control actions are loopback DNS lookups with TTL 0:
enroll.ca.fd73— generate the cert + register with the CA (§13.4).<name>.enroll.dns.fd73— claim/rename to<name>.
The control plane is loopback-only; it MUST NOT be served to the network.
15. Extensions
15.1 Negotiation (the announce TLV mechanism)
Extensions ride in app_data after a two-byte sentinel; the signature (§6.2)
authenticates them.
app_data := [ user_app_data ] ‖ EXT_SENTINEL ‖ ext_tlv*
EXT_SENTINEL := 0xEE 0xEE
ext_tlv := type(u8) ‖ length(u16, big-endian) ‖ value[length]
A parser splits at the first sentinel and reads length-prefixed TLVs, stopping on truncation. With no TLVs, no sentinel is emitted. Type tags:
| Tag | Value | Extension |
|---|---|---|
| PQ_LINK | 0x01 | post-quantum link offer (§15.2) |
| HW_ATTESTATION | 0x02 | hardware identity attestation (§15.3) |
| CAPABILITIES | 0x03 | capability bitfield |
| PLANE_NAME | 0x04 | self-asserted plane petname (§14.1) |
| IROH_NODEADDR | 0x05 | Iroh control/status reachability (§15.4) |
0x06..0x7F are reserved for future standard extensions; 0x80..0xFF for vendor-private
ones.
15.2 PQ link mode
After the classical handshake (§8.1), extension-aware peers MAY upgrade a link with an
ML-KEM-768 key exchange authenticated by ML-DSA-65, then rekey the link Token over
classical_shared ‖ pq_shared (defense in depth: an attacker must break both X25519 and
ML-KEM). Each node commits SHA256(ml_dsa_verifying_key) in its PQ_LINK TLV so peers
bind to the PQ identity before upgrading. Two messages ride context 0xF0. The KEM is
ephemeral per link.
15.3 Hardware-backed identity attestation
A hardware root (Ed25519) endorses a node by signing its signing key:
attestation = root_pub(32) ‖ Ed25519_sign_root( "plexus.hw-attestation.v1" ‖ node_sig_pub )
carried in the HW_ATTESTATION TLV. Because the signature covers the very key the
announce proves, a valid attestation binds "a trusted root vouches for this identity" to
the announce-proven identity and cannot be replayed onto another node's announce. A
verifier checks it against a configured trust store of root public keys.
15.4 Iroh control + status plane
An OPTIONAL out-of-band channel for node status and control, beside the data plane, over
Iroh QUIC keyed by the node's Ed25519 signing key (so a node's Iroh NodeId equals its
Periplus signing key). A node advertises its NodeAddr (NodeId + direct socket
addresses) in the IROH_NODEADDR TLV; peers dial it directly. Two ALPNs: a read-only
status pull and an admin-gated control command, the latter reusing the same actions as
the magic-DNS control plane (§14.3). This channel carries no data — the fd73:: plane
over Periplus remains the data path.
16. Security considerations
- Self-certifying addressing makes source spoofing ill-typed (§3.2, §12.3), but does not by itself authorize: admission is a separate set-membership check on a link-proven identity (§8.4).
- Untrusted input is parsed before trust is decided. Implementations MUST: bound IFAC packet sizes before key derivation; bound resource part counts and transfer sizes; reap half-open links and cap concurrent links; bound relay and recall tables; write key files with restrictive permissions; and zeroize plaintext scratch buffers.
- Constant-time MAC and signature checks are mandatory (§2.1, §5).
- No metadata in the Token (no version, no timestamp) by design (§5).
- The fork is a security boundary by construction: the domain-separated derivations (§2.2) mean cross-protocol traffic neither addresses nor decrypts, so a Periplus deployment is isolated from Reticulum without any allow/deny configuration.
- Forward secrecy is per-link via ephemeral handshakes and ratchets (§8.5); single-destination opportunistic traffic has weaker secrecy and SHOULD prefer links for sustained exchange.
17. Constant & domain-separation registry
Domain strings: see §2.2.
Sentinel / TLV tags: EXT_SENTINEL = 0xEEEE; tags 0x01..0x05 (§15.1).
Header widths: H1 = 19 bytes, H2 = 35 bytes; AddressHash = 16 bytes; name_hash =
10 bytes; base MTU = 500.
Type enums: §4.2.
Context bytes: §4.3 (high range), plus the low-range RESOURCE_ADV, RESOURCE_REQ,
RESOURCE_HMU, RESOURCE_PRF, RESOURCE_ICL, RESOURCE_RCL, CACHE_REQUEST,
PATH_RESPONSE, COMMAND, COMMAND_STATUS, CHANNEL — authoritative values are pinned
by the reference codec and its conformance vectors.
Well-known destinations: prptransport.path.request (Plain),
prptransport.remote.management (§9.1).
Prefixes/ports: plane fd73::/16; AutoInterface discovery group b"periplus",
default discovery UDP port 29716; plane DNS on loopback.
A future revision of this RFC SHOULD inline the full context-byte table from the reference codec; until then the codec and its frozen conformance vectors are authoritative for any value not given numerically here.
18. References
- Normative: RFC 2119 (requirements language); RFC 5869 (HKDF); RFC 7748 (X25519); RFC 8032 (Ed25519); FIPS 197 / SP 800-38A (AES-CBC); FIPS 198 (HMAC); FIPS 203 (ML-KEM); FIPS 204 (ML-DSA); BLAKE3.
- Informative — Plexus design records: ADR-0001 (gateway/extension seam), ADR-0002
(foreign-protocol adapters), ADR-0003 (resource segmentation), ADR-0004 (the
fd73::plane), ADR-0005 (enrollment), ADR-0006 (naming), ADR-0007 (Iroh control plane), ADR-0008 (the fork from Reticulum).ARCHITECTURE.md(the implementation baseline). - Lineage: the Reticulum Network Stack (the design Periplus derives from); cjdns and
Yggdrasil (public-key-into-
fc00::/8self-certifying addressing).
Appendix A: divergence from Reticulum
Periplus is derived from Reticulum and inherits the shape of its structures: the packet header, the announce, the Token construction, the destination/identity derivations, links, resources, and IFAC are all recognizably Reticulum's design. Periplus is not wire-compatible. The divergence is deliberate and minimal — two cryptographic domain separations (§2.2):
- the identity hash gains the
"periplus.id.v1"prefix (§3.2), so identities and all derived addresses differ; and - symmetric key derivation gains the
"periplus.kdf.v1"HKDFinfo(§8.2), so link and packet ciphertext is mutually unreadable.
Two further wire-identity values diverge (the AutoInterface group id b"periplus" and the
prptransport control app-name). A consequence of (1) is that a Periplus node cannot
interoperate with, route to, or be addressed by a Reticulum node — the fork's deliberate
cost is leaving the existing Reticulum network. Attribution to the Reticulum project and
the Reticulum-rs lineage is retained in the reference implementation's NOTICE.