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

Running a client

audience: operators

The typical zipnet publisher is an external user running their own TDX-attested agent — you don’t operate those. This page is about the reference zipnet-client binary you ship to publishers (or run yourself for a bundled wallet, a cover-traffic filler, or a smoke-test participant).

A client generates an X25519 keypair, publishes its public bundle via gossip, and seals one envelope per round. In production every client runs inside a TDX guest whose MR_TD matches the value your committee pinned; see Quickstart TDX section.

One-shot command

ZIPNET_INSTANCE="acme.mainnet" \
ZIPNET_MESSAGE="payload-to-broadcast" \
./zipnet-client --bootstrap <peer_id_of_aggregator_or_server>

Omit ZIPNET_MESSAGE to run a cover-traffic client that participates in every round with a zero payload. Cover traffic is the operator’s tool for raising the effective anonymity set size when real publishers are sparse.

Environment variables

VariableMeaningNotes
ZIPNET_INSTANCEInstance name to bind toRequired. Same string the committee uses; typos show up as ConnectTimeout.
ZIPNET_UNIVERSEUniverse overrideOptional; leave unset to use the shared universe.
ZIPNET_BOOTSTRAPPeer IDs to dial on startupAggregator’s PeerId or any committee server’s. Needed only on cold networks.
ZIPNET_MESSAGEUTF-8 message to seal per roundTruncate yourself to fit slot_bytes − tag_len. Default slot width is 240 bytes of user payload.
ZIPNET_CADENCETalk every Nth roundDefault 1. Useful for dialing your own talk/cover ratio.
ZIPNET_METRICSPrometheus bind addressOptional.

Building the TDX image you ship to publishers

Publishers to a TDX-gated instance need to run your client image (not their own ad-hoc build), because the committee will reject any client whose quote doesn’t match the pinned MR_TD. Build it the same way you build the server image — mosaik ships the builder:

// crates/zipnet-client/build.rs
fn main() {
    mosaik::tee::tdx::build::alpine()
        .with_default_memory_size("512M")
        .build();
}
# crates/zipnet-client/Cargo.toml
[dependencies]
mosaik = { version = "0.3", features = ["tdx"] }

[build-dependencies]
mosaik = { version = "0.3", features = ["tdx-builder-alpine"] }

Alpine is the usual choice for clients — ~5 MB versus Ubuntu’s ~25 MB — unless your agent has a specific glibc dependency. After cargo build --release the artifacts land under target/release/tdx-artifacts/zipnet-client/alpine/:

ArtifactWhat it’s for
zipnet-client-run-qemu.shSelf-extracting launcher publishers invoke on a TDX host.
zipnet-client-mrtd.hexThe 48-byte measurement. You pin this in the committee and publish it to readers.
zipnet-client-vmlinuzRaw kernel, for repackaging.
zipnet-client-initramfs.cpio.gzRaw initramfs.
zipnet-client-ovmf.fdRaw OVMF firmware.

Publish zipnet-client-mrtd.hex alongside your release notes. It goes into the committee’s Tdx::require_mrtd(...) configuration and into readers’ verification code. See Rotations and upgrades for rolling a new MR_TD without downtime.

What a healthy client log looks like

INFO zipnet_client: spawning zipnet client client=550fda1ffa
INFO zipnet_node::roles::common: zipnet up: network=<universe> instance=acme.mainnet peer=c2e9aeee0e... role=a8b7ed5911...
INFO zipnet_node::roles::client: client booting; waiting for rosters

After boot, every sealed envelope is a DEBUG event. Raise RUST_LOG to debug,zipnet_node=debug to see them.

Why a client’s envelope might get dropped

  • The client bundle hasn’t replicated yet. The first few rounds after a client connects may not include it in ClientRegistry. Wait for zipnet_client_registered to flip to 1 before relying on anonymity guarantees.
  • Slot collision with another client. v1’s slot assignment is a deterministic hash — two clients occasionally pick the same slot and XOR their messages into garbage. Neither falsification tag verifies, the committee still publishes the broadcast, the messages are lost, the clients retry next round. A 4x-oversized scheduling vector in v2 makes this rare.
  • Message is longer than slot_bytes − tag_len. The client exits with MessageTooLong. Shorten, or raise slot_bytes at the instance level (which retires the instance — see Rotations and upgrades).

Identity lifetime

In the mock path (TDX disabled), each process run generates a fresh X25519 identity — run-to-run unlinkability is free. In the TDX path, the identity lives in sealed storage inside the enclave so a restart preserves it; useful for reputation systems, but means the same enclave is recognizable across runs. Design accordingly when you pick a cover-traffic cadence.

See also