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

src/zipnet_io.rs

audience: ai

Anonymised I/O. The coalition knows requesters by ClientId; the provider sees only opaque zipnet envelopes. The unseal committee t-of-n decrypts the envelope and delivers cleartext plus a rotating peer_id to the addressed provider.

The Envelope struct deliberately does not carry a coalition ClientId. What the provider needs to do its job is exactly: the peer identity (for reply addressing), the peer’s x25519 public key (for receipt sealing), the image post-hash (for grant cross-check), an optional region hint, and a content- addressed pointer to the built image. Nothing more.

//! Zipnet I/O: anonymised submission and reply.
//!
//! The coalition layer knows requesters by their
//! `ClientId`. The provider only sees an opaque
//! `peer_id` inside a zipnet envelope — the zipnet
//! unseal committee t-of-n decrypts the envelope and
//! the provider receives a cleartext payload alongside
//! a rotating peer identity.
//!
//! This keeps the provider a content-based participant
//! in the coalition: it cannot enumerate "which coalition
//! members are running what workloads" because it never
//! learns their coalition identities.

use std::sync::Arc;

use anyhow::Context;
use mosaik::Network;
use coalition_compute::RequestId;

use crate::config::ZipnetBootConfig;

/// Opaque zipnet channel used for both inbound request
/// resolution and outbound reply (the encrypted SSH
/// receipt).
pub struct ZipnetChannel {
    // TODO: holds a zipnet::UnsealReader and a
    // zipnet::ReplySender. Prototype leaves the inner
    // types opaque.
    _network: Arc<Network>,
}

impl ZipnetChannel {
    pub async fn open(
        network: &Arc<Network>,
        cfg: &ZipnetBootConfig,
    ) -> anyhow::Result<Self> {
        // TODO: use zipnet::Unseal::<RequestEnvelope>::reader(...)
        // against cfg.network_id and cfg.unseal_config_hex.
        let _ = cfg;
        Ok(Self { _network: network.clone() })
    }

    /// Resolve a grant's `bearer_pointer` to the
    /// cleartext request envelope.
    ///
    /// The `bearer_pointer` is a blake3 of the sealed
    /// envelope published by the requester via zipnet;
    /// the unseal committee, on majority-honest
    /// assumption, makes the cleartext available to the
    /// addressed provider.
    pub async fn resolve(
        &self,
        bearer_pointer: &coalition_compute::UniqueId,
    ) -> anyhow::Result<Envelope> {
        let _ = bearer_pointer;
        anyhow::bail!(
            "ZipnetChannel::resolve is a prototype stub; wire up \
             the zipnet unseal reader to pull the sealed envelope, \
             verify the unseal quorum, and return the cleartext"
        )
    }

    /// Publish an encrypted reply (the SSH receipt)
    /// addressed to the zipnet peer the request came
    /// from. The receipt is already sealed to the
    /// peer's x25519 public key by `receipt::seal_to`;
    /// zipnet only carries it.
    pub async fn reply(
        &self,
        request_id: &RequestId,
        sealed_receipt: Vec<u8>,
    ) -> anyhow::Result<()> {
        let _ = (request_id, sealed_receipt);
        anyhow::bail!(
            "ZipnetChannel::reply is a prototype stub; wire up \
             zipnet::Reply::<SealedReceipt>::publish"
        )
    }
}

/// Decrypted request envelope as seen by the provider.
///
/// This is deliberately thin: the requester's coalition
/// `ClientId` is **not** in here. Only fields the
/// provider needs to do its job end to end.
pub struct Envelope {
    /// Ephemeral peer identity inside zipnet. Rotates
    /// per request by the requester.
    peer_id: [u8; 32],
    /// x25519 public key the provider will seal the
    /// SSH receipt to. Published by the requester
    /// alongside the request.
    peer_x25519: [u8; 32],
    /// The image post-hash the requester wants to run.
    /// Cross-checked against the grant's `image_hash`
    /// in `Provider::handle_grant`; mismatch aborts.
    image_hash: coalition_compute::UniqueId,
    /// Optional region hint inside the zipnet envelope.
    /// The grant also carries a region; on disagreement
    /// the provider uses the grant's region.
    requested_region: Option<String>,
    /// Content-addressed pointer to the requester's
    /// built image. The cloud-init script pulls from
    /// here.
    image_pointer: Vec<u8>,
}

impl Envelope {
    pub fn peer_id(&self) -> &[u8; 32] { &self.peer_id }
    pub fn peer_x25519_public(&self) -> &[u8; 32] { &self.peer_x25519 }
    pub fn image_hash(&self) -> coalition_compute::UniqueId {
        self.image_hash
    }
    pub fn requested_region(&self) -> Option<&str> {
        self.requested_region.as_deref()
    }
    pub fn image_pointer(&self) -> &[u8] { &self.image_pointer }
}

Up: compute-bridge.