Crate map
audience: contributors
Workspace at /Users/karim/dev/flashbots/zipnet/. Edition 2024, MSRV
1.93. Mosaik pinned to =0.3.17 (see CLAUDE.md
for rationale).
zipnet-proto (pure: no mosaik, no tokio, no I/O)
▲
│
zipnet-core (pure: no mosaik, no tokio, no I/O)
▲
│
zipnet-node ── mosaik 0.3.17 ── iroh 0.97 (QUIC)
▲ ▲
│ └──────────────────────────┐
│ │
zipnet (SDK facade) ├── zipnet-client
├── zipnet-aggregator
└── zipnet-server
The split between -proto, -core, and -node is load-bearing,
not cosmetic. Anything that touches tokio, mosaik, or I/O must
live in -node (or higher). Anything that could be reused by an
alternative transport lives in -proto / -core. If you find
yourself reaching for tokio::spawn or mosaik:: inside -proto or
-core, you are in the wrong crate.
zipnet-proto
Pure wire types and crypto primitives. No mosaik, no async.
| Module | Role |
|---|---|
wire | ClientEnvelope, AggregateEnvelope, PartialUnblind, BroadcastRecord, ClientId, ServerId, RoundId |
crypto | HKDF-SHA256 salt composition, AES-128-CTR pad generator, blake3 falsification tag |
keys | DhSecret (X25519 StaticSecret), ClientKeyPair, ServerKeyPair, public ClientBundle / ServerBundle |
params | RoundParams (broadcast shape) |
xor | xor_into, xor_many_into over equal-length buffers |
WIRE_VERSION is bumped any time a wire or params shape changes.
CommitteeMachine::signature() in zipnet-node mixes this in so
nodes with different wire versions will never form a group.
zipnet-core
Paper’s algorithms as pure functions over -proto types. No async.
| Module | Role |
|---|---|
client::seal | Algorithm 1 — TEE-side sealing of one envelope |
aggregator::RoundFold | Algorithm 2 — stateful XOR fold of envelopes for one round |
server::partial_unblind | Algorithm 3 — per-server partial computation |
server::finalize | Committee combine — aggregate + partials → broadcast |
slot | Deterministic slot assignment + slot layout helpers |
The full round trip is exercised by
server::tests::e2e_two_servers_three_clients, which constructs a
3-server / 4-client setup (2 talkers + 2 cover) and asserts that the
final BroadcastRecord contains each talker’s plaintext at the
expected slot with a valid falsification tag. No transport is
involved — this is the pure-algebra proof.
zipnet-node
The only non-SDK crate that imports mosaik. Hosts the
declare! items, the committee state machine, and the role event
loops.
| Module | Role |
|---|---|
protocol | declare::stream! + declare::collection! items, tag constants, ticket class constants |
committee | CommitteeMachine: StateMachine, Command, Query, QueryResult, LiveRound, CommitteeConfig |
tickets | BundleValidator<K>: TicketValidator for client / server bundle tickets |
roles::common | NetworkBoot helper that wraps iroh secret, tags, tickets, and mDNS setup |
roles::client | client event loop |
roles::aggregator | aggregator event loop |
roles::server | committee server event loop (single tokio::select! over three event sources) |
The role modules are reusable as a library — the three binaries are
thin CLI wrappers around them. Test code in
crates/zipnet-node/tests/e2e.rs reuses the same primitives but
inlines the server loop so it can inject a pre-built Arc<Network>
and cross-sync_with all peers before anything starts (same pattern
as mosaik’s examples/orderbook).
protocol.rs today vs target
protocol.rs currently declares its StreamId / StoreId literals
as flat strings ("zipnet.stream.client-to-aggregator", etc.). The
target per design-intro
is INSTANCE.derive("submit") / .derive("broadcasts") / … chained
off the per-deployment instance salt so multiple instances can
coexist on one mosaik universe without colliding. The migration
removes the ZIPNET_SALT.derive(shard) NetworkId scoping in
favour of the shared zipnet::UNIVERSE constant.
zipnet (SDK facade)
Public surface for consumers. Wraps zipnet-node and hides all
mosaik types (StreamId, StoreId, GroupId) from callers.
| Module | Role |
|---|---|
environments | UNIVERSE constant, instance_id(&str) fn, instance_id! macro |
client | Zipnet::bind, Zipnet::bind_by_id, publish, subscribe, shutdown |
error | Error { WrongUniverse, ConnectTimeout, Attestation, Shutdown, Protocol } |
types | Receipt, Round, Outcome, Message |
driver | internal task that plumbs publishes onto ClientToAggregator and broadcasts back |
Re-exports from mosaik that the SDK intentionally surfaces:
UniqueId, NetworkId, Tag, unique_id!. Nothing else is
re-exported — callers that need raw mosaik types have fallen off the
supported path and should drop to zipnet-node directly.
zipnet::instance_id(name) and zipnet::instance_id!("name") must
produce byte-identical outputs; the macro lowers to
mosaik::unique_id!(concat!("zipnet.", $name)) and the runtime fn is
UniqueId::from("zipnet." + name). If you change one, change the
other.
Binaries
Thin CLI wrappers around zipnet-node::roles::*. In v1 they still
take a ZIPNET_SHARD flag and scope to ZIPNET_SALT.derive(shard);
this predates the UNIVERSE + instance design and will be retired as
the binaries migrate to Zipnet::bind on UNIVERSE.
| Crate | Flags of note |
|---|---|
zipnet-client | ZIPNET_MESSAGE, ZIPNET_CADENCE |
zipnet-aggregator | ZIPNET_FOLD_DEADLINE |
zipnet-server | ZIPNET_COMMITTEE_SECRET, ZIPNET_MIN_PARTICIPANTS, ZIPNET_ROUND_PERIOD, ZIPNET_ROUND_DEADLINE |
Each binary also takes the common ZIPNET_SHARD, ZIPNET_SECRET,
ZIPNET_BOOTSTRAP, ZIPNET_METRICS — see Environment
variables.
Feature flags
zipnet-node/tee-tdx(off by default) — foldsmosaik::tickets::Tdx::new().require_own_mrtd()?into the committee’s admission validators. Requires mosaik’stdxfeature (on by default) and TDX hardware.zipnet-client/tee-tdx,zipnet-server/tee-tdx— re-export flips of the node crate’s flag.
Mock TEE is the default path (// SIMPLIFICATION: in source); TDX is
opt-in for v1 and the critical-path enforcement lands in v2 (see
Roadmap).
Dependency choices worth knowing
x25519-dalek2.0 pinsrand_core0.6 (not workspace rand 0.9). We break workspace coherence inzipnet-proto/Cargo.tomlby pullingrand_core = "0.6"explicitly forOsRngcompatibility withStaticSecret::random_from_rng. The crate-proper rand dep is workspace-pinned.mosaik = "=0.3.17"— the API we developed against. Upgrades are expected to break compile; thedeclare::stream!/declare::collection!macros are stable-ish, the ticket and group APIs have shifted across minor versions.