Quickstart — stand up an agent that binds a coalition
audience: ai
This quickstart brings up a single AI agent in the integrator shape (the cheapest of the four shapes from overview): the agent observes a coalition’s public surfaces, acts on what it observes, and publishes no public mosaik commits. Section agents as citizens covers the other three shapes.
The code below assumes a Rust agent. Agents whose core
lives in Python or another language follow the same
pattern via whichever FFI they use to reach a mosaik
Network handle.
What you need before you start
- A
CoalitionConfigfrom the coalition operator — either the struct definition or a serialised fingerprint paired with the struct in a shared crate. - A ticket from each per-citizen operator whose write- side primitive the agent intends to use. Read-only agents typically need none; bidding, submitting, or publishing agents need one per write-side surface.
- The agent’s own policy code — however it loads, evaluates, and acts. Mosaik is agnostic about this.
Step 1 — declare the CoalitionConfig
Same pattern as the integrator quickstart:
use builder::LatticeConfig;
use coalition::{CoalitionConfig, ConfluenceConfig, OrganismRef};
const SUBSTRATE: CoalitionConfig<'static> = /* from operator release notes */;
The agent holds the constant as a compile-time input, not as a runtime registry lookup. This is the fingerprint-not-registry discipline; an agent that tries to “discover” its coalition via network queries has invited unauthenticated state into its core loop.
Step 2 — build the network handle
use std::sync::Arc;
use mosaik::Network;
use builder::UNIVERSE;
let network = Arc::new(Network::new(UNIVERSE).await?);
One Arc<Network> per agent process. Clone it into each
task that needs a handle.
Step 3 — open subscriptions on the surfaces the agent reads
The agent’s observation set is whatever the policy needs. Examples:
// Subscribe to a lattice's auction outcomes — the agent
// wants to track recent bids and fills before acting.
let outcomes = offer::Offer::<Bundle>::outcomes(
&network, &SUBSTRATE.lattices[0].offer,
).await?;
// Subscribe to a shared ledger confluence — the agent
// uses aggregated refunds as input to its utility
// function.
let refunds = ledger::Shared::<Refund>::read(
&network, &SUBSTRATE.confluences[0],
).await?;
// Read the coalition's Atlas to discover per-citizen
// endpoint hints and MR_TDs, if needed at cold start.
if let Some(atlas_cfg) = SUBSTRATE.atlas() {
let atlas = atlas::Atlas::<CitizenCard>::read(
&network, atlas_cfg,
).await?;
// Agent may cache atlas entries and refresh them on
// a cadence matching its policy horizon.
}
// If the coalition ships an Almanac, use it as the time
// axis across uncorrelated citizen clocks.
if let Some(almanac_cfg) = SUBSTRATE.almanac() {
let ticks = almanac::Almanac::<AlmanacTick>::read(
&network, almanac_cfg,
).await?;
// Pull `observed_at: Vec<(MemberId, SystemTime)>`
// from each tick for per-member timestamp brackets.
}
Step 4 — drive the agent loop
An inference-only agent typically runs a select! loop
over its subscriptions, feeds observations into the
policy, and dispatches the resulting action wherever the
action belongs (a write-side stream, an HTTP call, an
on-chain tx).
loop {
tokio::select! {
Some(outcome) = outcomes.next() => {
let obs = observation_from_outcome(&outcome);
if let Some(action) = policy.decide(obs).await? {
dispatch(action).await?;
}
}
Some(refund) = refunds.next() => {
policy.update_belief_from_refund(&refund);
}
_ = shutdown_signal() => break,
}
}
Rules for an agent at this layer:
- Policy is pure-ish. Write the policy so the same observation sequence reproduces the same action sequence given the same weights. This is not required by mosaik; it is required for the agent’s own debuggability when the policy is later migrated into an organism (shape 2, 3, or 4).
- Side effects are explicit.
dispatch(action)is the only point where the agent changes external state; the rest is observation and belief update. - No cross-task shared mutable state unless deliberate. Mosaik’s subscriptions are per-handle; aggregating across them should be done in one place.
Step 5 — ticketed write actions (if any)
When the agent actively writes, it holds one ticket per write-side surface, bonded at startup against the per-citizen operator’s issuance root:
// Example: the agent places bids on a lattice's `offer`.
// The Offer::<Bundle>::bid constructor verifies the
// ticket composition against offer's TicketValidator.
let bids = offer::Offer::<Bundle>::bid(
&network, &SUBSTRATE.lattices[0].offer,
).await?;
bids.send(bundle).await?;
Ticket lifecycle is the agent’s responsibility: rotate before expiry, re-bond if a ticket-issuance root changes, fail loud if a per-citizen operator has revoked.
What this shape does not give the agent
- Replayable outputs. An integrator-shape agent commits nothing on the universe. Its decisions are reproducible only if it keeps its own log.
- Public identity. Peers cannot address the agent directly; other agents know it only through the side- effects of its actions.
- Bonding with other agents. Other agents cannot hold a ticket against this one.
Graduate to shape 2 (standalone organism) when any of those becomes a requirement. See agents as citizens.
Cross-references
- What a coalition gives AI agents
- Agents as citizens
- Emergent coordination
- Integrators — quickstart — the closest analogue in the non-AI audience.