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.