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

TEE-gated lattices

audience: integrators

Some lattices require integrators to run their agent inside a TDX enclave; most do not. This page explains the two postures and when each applies.

Two postures

Open read, TDX-gated write. The common case. Reading the lattice’s public surface (every organism’s read / watch / outcomes stream) requires no TEE; writing to it (Zipnet::submit, Offer::bid) is gated by a lattice-issued ticket. Integrator agents run on ordinary hardware; the ticket is the proof the lattice operator has vetted the agent.

TDX-gated write with attestation. For anonymity-sensitive deployments the lattice operator requires the submitting agent itself to run inside TDX. The organism’s write-side ticket validator additionally requires a valid TDX quote on the agent’s PeerEntry. This is the posture the zipnet v2 TDX path takes; lattices that want to extend the same property to bundles adopt it for offer too.

Which posture your lattice uses

The operator’s fact sheet (see handshake-with-operator.md) names the posture. If the operator publishes MR_TDs for any organism’s write-side, that organism is TDX-gated on writes. If MR_TDs are published only for the organism’s committee, writes are still open.

Compiling for TDX

When the lattice requires you to run in TDX, compile your agent with the tee-tdx feature enabled on the relevant organism crates and on mosaik itself:

[dependencies]
mosaik  = { version = "=0.3.17", features = ["tee-tdx"] }
zipnet  = { version = "0.1",     features = ["tee-tdx"] }
offer   = { version = "0.1",     features = ["tee-tdx"] }
# ... as needed ...

[features]
tdx-builder-ubuntu = ["mosaik/tdx-builder-ubuntu"]

In your build.rs:

fn main() {
    #[cfg(feature = "tdx-builder-ubuntu")]
    mosaik::tee::tdx::build::ubuntu()
        .with_default_memory_size("4G")
        .build();
}

cargo build --release --features tdx-builder-ubuntu produces a reproducible initramfs, OVMF, kernel, and a MR_TD hex file. You publish your MR_TD to your operator (so they can pin it in the write-side ticket validator) and boot your agent from the resulting image.

See the mosaik TDX tutorial for the full walk- through; the flow is identical to zipnet’s v2 TDX path.

What MR_TD pinning gets you

When the write-side ticket validator pins your MR_TD:

  • Your agent cannot be silently swapped for a different binary without the lattice operator updating the ticket.
  • A compromised host that is not running your published image cannot submit as your agent.
  • The lattice operator can revoke your ticket by rotating the pinned MR_TD without touching your agent’s secret key.

What it does not get you:

  • Protection against your own bugs. TDX measures the image, not the correctness of the code inside it.
  • Privacy of your code from the operator — they see the MR_TD, and reproducible builds mean they could build the same image themselves. TDX’s property is integrity, not code confidentiality.
  • Cross-organism attestation. Each organism’s ticket pins its own MR_TD; there is no notion of “the agent as a whole is attested”. If you need coupling (e.g. “the same TDX image submits to zipnet and offer”), publish one MR_TD and pin it on both organisms.

When MR_TDs rotate

Lattice operators rotate MR_TDs when they upgrade their committee image or revoke a compromised key. When your organism crate’s pinned MR_TD differs from the committee’s current one, your next verb() call returns Attestation error. Rotate your own image or your own build if your published MR_TD changed; if only the operator’s changed, wait for their new LatticeConfig release.

You are not required to pin anything

If the lattice is not TDX-gated on your write side (the default), do not compile with tee-tdx. Compiling with it when the lattice does not require it is a no-op — the ticket validator chain simply ignores the unused TDX ticket — but it adds build-time overhead you do not need.

Next reading