Binding to the Compute market
audience: ai
The bridge has a CoalitionConfig for bridge-β
and a boot TOML (setup). This
chapter walks the
handshake: the boot flow, the TDX self-quote, the
Compute-module resolution, the shuffle channel
opening, and the provider-card publication. Once
the card is live the market can route grants to
the bridge.
The convergence mechanism from
ai/emergent-coordination.md — coordination
markets shows up on the provider side
here. The bridge’s TicketValidator admits the
Compute module’s clearing rule (the module’s
Config fingerprint) and the module’s scheduler-
committee Measurements (pinned TDX). Two
TDX-attested bridges admitting the same module
Config agree on the same clearing rule.
The boot flow
The entry point is src/main.rs
(examples/compute-bridge/src/main.rs):
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 1. Load boot config.
let cfg = config::BootConfig::load_from_env()?;
// 2. TDX self-quote. Without a matching
// Measurements set the provider card will
// not match this binary and the Compute
// committee will reject registration.
let tdx_quote = tdx::self_quote().await?;
// 3. Shared mosaik network handle.
let network = Arc::new(
Network::new(mosaik::builder::UNIVERSE).await?,
);
// 4. Resolve the Compute module on the coalition.
// No Compute module, no bridge.
let compute_cfg = cfg.coalition.compute()?;
// 5. Shuffle channel for anonymised request/reply.
let zipnet = zipnet_io::ZipnetChannel::open(
&network, &cfg.zipnet,
).await?;
// 6. Fleet from the operator's enabled backends.
let fleet = backends::Fleet
::from_boot_config(&cfg.backends).await?;
// 7. Operator dashboard on localhost (non-essential;
// failures log and do not abort).
let dashboard = Arc::new(
dashboard::Dashboard::new(
&cfg.dashboard, fleet.clone(),
),
);
tokio::spawn({
let d = dashboard.clone();
async move { let _ = d.spawn().await; }
});
// 8. Provider loop: register the card, accept
// grants, return receipts.
let provider = provider::Provider::new(
network, compute_cfg, tdx_quote, zipnet,
fleet, cfg.provider, dashboard,
);
provider.run().await
}
Steps (1)–(6) run in order and each blocks the next. Steps (7) and (8) run in the same tokio runtime.
TDX self-quote
Step (2) is load-bearing for the provider card. The scheduler committee verifies the quote against the declared Measurements before accepting the card.
From
src/tdx.rs:
pub async fn self_quote() -> anyhow::Result<tdx::Quote> {
// Calls into the TDX guest's get_quote MMIO/IOCTL
// via the mosaik::tee::tdx wrapper. The quote's
// report_data folds:
// - the provider's ed25519 public key,
// - the bridge organism's Config.content hash,
// - a boot nonce so quotes can't be replayed.
mosaik::tee::tdx::get_quote(report_data()).await
}
The quote is signed by the TDX provisioning-cert
chain (traceable to Intel; local attackers cannot
forge it). Its report_data binds the provider’s
ed25519 public key, so later commits the provider
signs are tied to the same key the quote attests.
The boot nonce prevents replay across reboots.
The bridge’s TicketValidator
The validator declares what the bridge accepts bonds from and what Measurements it requires of counterparts. Three clauses carry the chapter:
use coalition::{TicketValidator, Tdx};
use coalition::acl::ComputeModule;
fn bridge_ticket_validator()
-> TicketValidator<'static>
{
TicketValidator::new()
// Scheduler committee must run the pinned
// TDX image. This is the bridge agreeing to
// the module's clearing rule.
.require_ticket(
Tdx::new().require_mrtd(
COMPUTE_MOD.module_image,
),
)
// Bridge accepts grants only from the exact
// Compute module Config fingerprint it
// booted against. A module rotation requires
// an explicit rebind via the retirement
// chain; the bridge does not silently follow
// scheduler rotations.
.require_ticket(
ComputeModule::pin(
compute_cfg.stable_id(),
),
)
// The module's own ACL gates who may request
// grants. The bridge does not add a separate
// requester ACL; admitting the module's is
// enough.
.verify_peer(
ComputeModule::requester_acl(
compute_cfg.stable_id(),
),
)
}
The bridge does not bond against other providers. The coordination market is the mediating policy; competing providers are observed (see market-reads) but not bonded to.
Publishing the provider card
provider.run() starts by publishing the card. The
card is what the market sees; it is folded into the
module’s Collection<ProviderId, ProviderCard> and
consulted on every clearing round.
From
src/provider.rs:
async fn register_provider_card(&self)
-> anyhow::Result<ProviderId>
{
let capabilities = self.fleet.capabilities().await?;
let card = ProviderCard {
provider_id: self.provider_id(),
tdx_quote: self.tdx_quote.clone(),
capabilities,
declared_rates: self.config.declared_rates,
zipnet_reply: self.zipnet.reply_pointer(),
refreshed_at: self.network.almanac().tick(),
};
coalition_compute::register(
&self.network, self.compute, card,
).await
}
What the card folds:
provider_id— blake3 of the bridge’s ed25519 public key, stable across restarts because the key comes from a TDX-sealed seed.tdx_quote— the quote fromsrc/tdx.rs.capabilities— the union of backends’ declared regions, TDX-capable flag, max CPU and RAM (chapter 4 walks the per-backend contribution).declared_rates— the posted $/core-hour and $/GiB-hour per backend. This is the competitive surface (chapter 3).zipnet_reply— the shuffle channel the bridge listens on.refreshed_at— the Almanac tick. Scheduler treats cards stale after a declared max-age; the bridge refreshes on capacity change.
Smoke test
With the card registered the operator checks that it is visible on the market before walking away:
use futures::StreamExt;
use tokio::time::{timeout, Duration};
let mut subscribers = network
.subscribe::<ProviderCardCollection>(
compute_cfg.stable_id(),
).await?;
let check = async {
while let Some(ev) = subscribers.next().await {
if ev.provider_id == provider.provider_id() {
tracing::info!("card live in market");
return Ok::<_, anyhow::Error>(());
}
}
anyhow::bail!("card not observed in market")
};
timeout(Duration::from_secs(30), check).await??;
Common failures:
TdxQuoteRejected: Measurements in the quote do not match the module’s admission policy. Rebuild against the pinnedcoalition-computeversion the module admits, or wait for the admission rotation.StaleModuleConfig:compute_cfgis older than the module currently runs. Pull the coalition’s updated handshake page, re-readcompute_cfg.stable_id(), restart.
Not yet
The validator has no ReputationFloor clause yet;
chapter 7 adds it once scoring history exists. The
bridge is registered but has not handled a grant;
chapter 5 picks that up. Market reads come next
(chapter 3).