Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.

ModuleRole
wireClientEnvelope, AggregateEnvelope, PartialUnblind, BroadcastRecord, ClientId, ServerId, RoundId
cryptoHKDF-SHA256 salt composition, AES-128-CTR pad generator, blake3 falsification tag
keysDhSecret (X25519 StaticSecret), ClientKeyPair, ServerKeyPair, public ClientBundle / ServerBundle
paramsRoundParams (broadcast shape)
xorxor_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.

ModuleRole
client::sealAlgorithm 1 — TEE-side sealing of one envelope
aggregator::RoundFoldAlgorithm 2 — stateful XOR fold of envelopes for one round
server::partial_unblindAlgorithm 3 — per-server partial computation
server::finalizeCommittee combine — aggregate + partials → broadcast
slotDeterministic 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.

ModuleRole
protocoldeclare::stream! + declare::collection! items, tag constants, ticket class constants
committeeCommitteeMachine: StateMachine, Command, Query, QueryResult, LiveRound, CommitteeConfig
ticketsBundleValidator<K>: TicketValidator for client / server bundle tickets
roles::commonNetworkBoot helper that wraps iroh secret, tags, tickets, and mDNS setup
roles::clientclient event loop
roles::aggregatoraggregator event loop
roles::servercommittee 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.

ModuleRole
environmentsUNIVERSE constant, instance_id(&str) fn, instance_id! macro
clientZipnet::bind, Zipnet::bind_by_id, publish, subscribe, shutdown
errorError { WrongUniverse, ConnectTimeout, Attestation, Shutdown, Protocol }
typesReceipt, Round, Outcome, Message
driverinternal 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.

CrateFlags of note
zipnet-clientZIPNET_MESSAGE, ZIPNET_CADENCE
zipnet-aggregatorZIPNET_FOLD_DEADLINE
zipnet-serverZIPNET_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) — folds mosaik::tickets::Tdx::new().require_own_mrtd()? into the committee’s admission validators. Requires mosaik’s tdx feature (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-dalek 2.0 pins rand_core 0.6 (not workspace rand 0.9). We break workspace coherence in zipnet-proto/Cargo.toml by pulling rand_core = "0.6" explicitly for OsRng compatibility with StaticSecret::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; the declare::stream! / declare::collection! macros are stable-ish, the ticket and group APIs have shifted across minor versions.