Expand description
peasub — a metadata-private gossip protocol built on top
of peashape, which in turn is built on pea2pea.
§What it does
peasub provides a gossip substrate in which every node
emits outbound traffic at a constant rate (or, optionally,
with Poisson-distributed inter-arrival times). Real
application messages are interleaved with cover traffic
drawn from the same code path, so a passive observer of
the wire cannot distinguish a “real” frame from a “cover”
frame, nor infer when a node has user activity.
The privacy property is not free: real messages are paced by the cover rate, so a node that wants to send at 10 msg/s must configure a cover rate of at least 10 msg/s, and the bandwidth cost is the same as if it were sending cover frames 100% of the time. The cover rate is the only knob that matters; the application should size its publish rate to the cover rate and not exceed it.
§Quick start
use std::time::Duration;
use peasub::{CoverStrategy, Node, NodeConfig};
let node = Node::new(NodeConfig {
name: Some("alice".into()),
listener_addr: Some("127.0.0.1:0".parse()?),
cover: CoverStrategy::Constant {
interval: Duration::from_millis(100),
},
..Default::default()
});
let _local_addr = node.spawn().await?;
// subscribe to incoming frames
let mut rx = node.subscribe();
// submit a real message
node.publish(b"hello, peasub")?;
// connect to a peer (any peer running a compatible `Node`)
// node.connect(remote_addr).await?;
// ... process received frames ...
node.shutdown().await;§Threat model
peasub is designed to defeat a passive global network
observer who can:
- observe every byte sent between every pair of nodes;
- observe the timing of every byte;
- but cannot break the cryptographic primitives
protecting the link (e.g. TLS via a
pea2peaHandshake).
Against such an observer, the cover-traffic schedule ensures that the timing distribution and size distribution of a node’s outbound traffic are independent of whether the application is publishing messages or not. The observer learns nothing about the existence, frequency, or destination of user activity beyond the rate the node has been configured for.
peasub does not attempt to defeat:
- an observer that can compromise the node itself;
- side channels outside the network (e.g. a screen-snooping adversary, or a process that visibly burns CPU only when the user is active);
- an observer that controls a non-trivial fraction of the network’s nodes and can correlate across them;
- traffic content analysis: cover hides when messages
are sent, not what they say. End-to-end payload
confidentiality is the application’s responsibility (or
can be layered on via a
pea2peaHandshake).
§Architecture
Nodewraps apeashape::Node(which in turn wraps apea2pea::Node) and adds two pieces of bookkeeping: an LRU of recently-seen message IDs (for dedup), and a background task that subscribes to the peashape incoming broadcast and re-enqueues novel frames into peashape’s low-priority lane for fanout. The high-priority lane is used for application-submitted messages (Node::publish); the low-priority lane is used for relays.- The length-delimited codec forces every frame on the
wire to a single fixed size (configurable via
NodeConfig::message_size), so the length of a frame is never a tell. - The first
ID_SIZEbytes of every frame are a random message identifier; real messages receive a random ID at publication time, cover messages receive a fresh random ID per emission. The receiver’s LRU dedups on this field, which is the only piece of the frame the application could meaningfully leak through. - When a peer receives a frame, it is deduplicated against the LRU, delivered to local subscribers, and re-queued into the peashape low-priority lane. The LIFO discipline of the low-priority lane forwards a fresh relay on the very next cover tick.
- When a tick fires, the peashape scheduler either pulls
from the high-priority lane, then the low-priority lane,
or generates a cover frame — and ships it to
fanoutrandomly-chosen connected peers. Every tick emits exactlyfanoutframes of the same on-the-wire size, so the metadata-privacy property is preserved regardless offanout.
§Choosing the cover rate
The cover rate is the only knob that controls the privacy / bandwidth trade-off. As a rule of thumb:
- the application should publish no faster than the cover rate, otherwise its messages accumulate in the high-priority lane;
relay_outbox_capacity(the low-priority lane capacity) should be sized tofanout * cover_rate * drain_secondsto keep relay backlog from crowding out user data.
§Composition with peashape
Every byte that hits the wire goes through
peashape’s scheduler and codec, so peasub inherits
the metadata-privacy property for free. Conversely, the
peashape library is a generic, application-agnostic
substrate; peasub is one specific application (gossip).
If you want to build a different application on the same
property — private RPC, private pub/sub, private file
chunking, private membership — use peashape directly.
Re-exports§
pub use pea2pea;
Structs§
- Node
- A single peer in a
peasubgossip network. - Node
Config - The set of parameters that govern a
Node.
Enums§
- Cover
Strategy - How the node generates outbound traffic.
- Error
- All errors that can be surfaced to the application through the
public
peasubAPI. - Topology
- Re-exported so that callers can wire up a topology in
tests without adding
pea2peaas a direct dependency. The way in which nodes are connected to each other; used inconnect_nodes.
Constants§
- ID_SIZE
- Re-exported for backward compatibility: the random message
identifier is sized by the peashape convention.
Size, in bytes, of the message-identifier field that prefixes
every frame in protocols built on top of
peashape.
Functions§
- connect_
nodes - Re-exported so that callers can wire up a topology in
tests without adding
pea2peaas a direct dependency. Connects the provided list of nodes in order to form the givenTopology.