Composition — members and organisms in a coalition
audience: contributors
Architecture maps composite organisms and members onto one example coalition. Composite organisms specifies the composite-organism shape. Basic services specifies Atlas, Almanac, and Chronicle. This chapter shows the wiring: which public surface each composite organism subscribes to on each spanned member, how the subscription graph grows as composite organisms and modules are added, and what fails where when an upstream stalls.
The wiring is deliberately weak. No cross-Group
atomicity, no shared state across members, no global
scheduler. Members and organisms react to each other’s
public commits via mosaik’s when() DSL and Collection
/ Stream subscriptions. This is the composition model
at every rung.
Two keys: member id and clock event
Every commit in every block-building lattice is keyed by slot (see builder composition). Every commit in a standalone organism is keyed by that organism’s own clock event — a slot, a tick, a round, a sequence number. A coalition adds the member id as an outer key on every cross-member fact.
The member_id used as a key in cross-member commits is
the first 20 bytes of the member’s blake3 stable-id
fingerprint. The full 32-byte value lives in Atlas cards
and in evidence pointers. The 20-byte truncation gives a
compact key that matches the mental model readers bring
from other ecosystems; collision resistance remains more
than sufficient at this rung.
Concretely:
- Intra-member commits continue to key on their own clock alone (no change from the member’s own book).
- Composite-organism commits that fan-out to multiple
target members key on
(target_member_id, target_clock_event). - Composite-organism commits that fan-in from multiple
origin members key on
(origin_member_id, origin_clock_event). - An integrator joining commits across a composite
organism and the members the composite organism touches
keys on
(member_id, clock_event)everywhere. - When a coalition ships an Almanac the composite
organism has opted into, the composite organism may
additionally carry
almanac_tickas an alignment key for cross-member joins where the members’ native clocks are not commensurate.
The 20-byte member_id is stable — it never changes for
a given member deployment. A member retirement yields a
new stable id and is therefore a new member id for every
coalition that adopts it.
The subscription graph
Each arrow is a subscription. The downstream component watches the upstream’s public collection or stream and reacts in its own state machine. No arrow represents atomic cross-Group commit. Dashed boxes are composite organisms; the oracle box on the left is a standalone organism.
integrators
(wallets,
searchers)
│
├──► zipnet[L1]:Submit ────► Broadcasts[L1] ──► UnsealedPool[L1] ─┐
│ │
├──► zipnet[L2]:Submit ────► Broadcasts[L2] ──► UnsealedPool[L2] ─┤
│ │
└──► offer[L1]:Bid / offer[L2]:Bid │
│
price-feed.eur ──► Price[tick] │
│
almanac ──► Ticks[K] │
│ │
└──────────── referenced by composite organisms for alignment ──┐ │
▼ │
┌────────────────── ┘
▼
┌───────────────────────┐
│ intents (composite) │
│ reads UnsealedPool[*] │
│ + price-feed.eur │
│ commits RoutedIntents │
└───────────────────────┘
│
RoutedIntents[L1,S] / RoutedIntents[L2,S]
▼
offer[L1]:AuctionOutcome offer[L2]:AuctionOutcome
│ │
atelier[L1]:Candidates atelier[L2]:Candidates
│ │
relay[L1]:AcceptedHeaders relay[L2]:AcceptedHeaders
│ │
tally[L1]:Refunds tally[L2]:Refunds
│ │
└────────────┬──────────────┘
▼
┌──────────────────────────┐
│ ledger (composite) │
│ reads Refunds[*] │
│ commits SharedRefunds │
└──────────────────────────┘
│
▼
integrators
atlas ──► MemberCards (consumed by dashboards, not coalition pipeline)
chronicle ──► ChronicleEntries (consumed by auditors, not coalition pipeline)
Read top to bottom:
- Wallets submit to each lattice’s
zipnet::Submit; searchers submit to each lattice’soffer::Bid. Neither changes from builder. - Each lattice’s
zipnetandunsealdo their usual work independently. The oracle organismprice-feed.eurpublishes ticks on its own clock. The Almanac (if shipped) publishes its own tick stream. - The
intentscomposite organism subscribes to every spanned member’s relevant public surface — lattices’UnsealedPools and, in this example,price-feed.eur’sPricestream. It may additionally subscribe to the Almanac for alignment keys. It observes cross-member intents and commits routed-intents entries per target lattice. - Each lattice’s
offersubscribes to the composite organism’sRoutedIntentscollection filtered for its own member id. Routed intents are an additional input to the auction, along with the lattice’s ownUnsealedPool. - Each lattice’s
atelierandrelayproceed as in builder. - Each lattice’s
tallycommits independently. - The
ledgercomposite organism subscribes to every spanned lattice’stally::Refundsand commits the aggregatedSharedRefundscollection. - Integrators read
SharedRefundsand per-member surfaces depending on their use case. Dashboards read the Atlas. Auditors read the Chronicle.
Atlas and Chronicle are not on any composite organism’s critical path in this example; they are consumed by dashboards and auditors directly. Modules serve observability and provenance, not the coalition’s data flow.
Subscription code shape
A contributor implementing a composite organism writes a
role driver in that composite organism’s crate. The
driver is a mosaik event loop multiplexing subscriptions
across members and calling group.execute(...) on its
own Group. The pattern is identical to any organism
driver, fanned out across more member kinds.
// Inside intents::node::roles::committee_member.
//
// `lattices` and `organisms` are the ordered sets of
// member references this composite organism spans,
// sourced from self.config.lattices and
// self.config.organisms.
loop {
tokio::select! {
// One subscription per spanned lattice's UnsealedPool.
ev = pool_a.when().appended() => {
let round = pool_a.get(ev).expect("appended implies present");
let intents = extract_cross_chain_intents(&round);
self.group.execute(IntentsCmd::ObservePool {
member_id: lattice_a_id,
slot: round.slot,
intents,
}).await?;
}
ev = pool_b.when().appended() => {
// ... same shape
}
// One subscription per spanned standalone organism.
ev = price_feed_eur.when().appended() => {
let tick = price_feed_eur.get(ev).unwrap();
self.group.execute(IntentsCmd::ObservePrice {
member_id: eur_feed_id,
tick: tick.seq,
price: tick.value,
}).await?;
}
// Optional: align to a coalition's Almanac.
ev = almanac.when().appended() => {
let t = almanac.get(ev).unwrap();
self.group.execute(IntentsCmd::AdvanceAlmanacTick(t.seq)).await?;
}
_ = self.apply_clock.tick() => {
// Per-slot deadline: seal the routing decision.
self.group.execute(IntentsCmd::SealSlot).await?;
}
}
}
Every observed event carries enough evidence
((member_id, clock_event, commit_pointer)) that the
state machine can validate it against the member’s public
surface during replay.
Apply order across the graph
Within one organism’s Group, mosaik’s Raft variant
guarantees every committee member applies commands in the
same order. Across composite organisms, and across
members, no such guarantee exists. The coalition layer
relies on the same two properties the builder layer does,
scaled up:
- Monotonicity by key. Within any component, commits
for key
(C, K+1)are not applied before(C, K). The component’s state machine enforces this per- member in its apply handler. - Eventual consistency by subscription. A downstream component’s driver observes every upstream commit eventually, because mosaik collections are append-only and readers converge.
Together these are enough to reconstruct a consistent per-member per-clock-event view across the whole coalition without global atomicity. An integrator wanting “the canonical decision for member C, clock event K, across members and composite organisms” reads each component’s per-(C, K) commit independently and joins on the key.
When an upstream stalls
Failure propagation is a small matrix. Rows are the failing component; columns are downstream components’ observable behaviour.
| Upstream fails | Same-member downstreams | Composite organisms that read this member | Other members |
|---|---|---|---|
| lattice organism | see builder composition | degraded — partial evidence per spec | unaffected |
| standalone organism | see that organism’s own book | degraded — partial evidence per spec | unaffected |
| member stable id bump (retirement) | in-member consumers re-pin | composite organism must redeploy against new stable id | unaffected |
| member content hash bump | in-member consumers re-pin if they pinned | composite organisms pinning content redeploy; stable-id-only pinners are unaffected | unaffected |
| composite organism stalls | no effect (members don’t depend on composite organisms for their own pipeline) | downstream composite-organism consumers see no new commits | unaffected |
| module stalls | no effect (members don’t depend on modules) | composite organisms aligned to this module see delayed/degraded commits | unaffected |
| composite-organism committee crosses threshold | no effect | bad commits possible (integrity lost); on-chain settlement is the final arbiter | unaffected |
| coalition operator retires coalition | no technical effect (members continue) | modules under retired coalition emit retirement markers, then stop | unaffected |
Two properties fall out, mirroring builder’s:
- Upstream failures degrade downstream outputs; they do
not corrupt them. A missing
UnsealedPool[L1, S]narrows theintentscomposite organism’s commit for that slot; the commit itself remains well-formed. - The pipeline is drainable per member. Failures on one member do not block the coalition’s other members. Each member’s pipeline is local; each composite organism applies what it observes.
What the coalition composition contract guarantees
Given the full commit logs of every member and every composite organism the coalition references:
- Deterministic replay per (member_id, clock_event). Anyone can recompute each component’s per-(C, K) decision and cross-check composite-organism commits against the member inputs they folded in.
- Independent auditability. A composite organism’s commit carries evidence pointers to the member commits it depends on. A consumer that trusts the members can verify a composite organism’s commit without trusting the composite organism’s committee, as long as the evidence pointers resolve.
- No silent corruption across coalitions. A member referenced from multiple coalitions is still one member; its commit log is one log. Different coalitions reading the same member see the same facts. A composite organism referenced from multiple coalitions is still one composite organism; its commit log is one log.
What the contract does not guarantee
- Atomic all-or-nothing commits across members. Already refused. An integrator reading a composite organism’s commit and the spanned members’ commits must tolerate differing commit wall-clock times.
- Cross-coalition coordination. Coalitions coexist, they do not coordinate. If a cross-coalition commit is genuinely needed, the answer is a composite organism that spans the relevant members directly — which both coalitions may reference independently.
- Bounded end-to-end latency. As in builder: if a composite organism stalls, downstream consumers never see its next commit. No composite-organism-level timeout triggers a dummy commit; operators requiring bounded latency add per-slot deadlines at the composite-organism level.
Contributors implementing a new composite organism
Wiring checklist for a composite organism:
- Identify every spanned member’s public surface you
subscribe to. Record as ordered slices of member
references in your
Config—lattices,organisms, or both. - Decide whether your commits fan-in, fan-out, or both. Fan-in composite organisms aggregate per-origin-member facts; fan-out composite organisms distribute one fact to multiple target members; some composite organisms do both.
- Key every commit by
(member_id, clock_event). Never by one alone. Use the 20-byte truncated member id. If your state machine has a natural sub-clock- event cadence, commit at that cadence but carry the owning(member_id, clock_event)pair. If the composite organism opts into a coalition’s Almanac and needs cross-member alignment, also carryalmanac_tick. - Write one role driver per
Groupmember role. Keep it as atokio::select!over the upstream subscriptions (one per member) and your local timers. - Validate evidence on replay. The upstream-event
pointers carried in your
Observe*commands must be checkable against the member’s public surface during replay; a replay that sees an unresolved evidence pointer must reject the command. - Document your per-member-failure policy. What happens when one spanned member stalls, reorders, or bumps its stable id or content hash — all are operational realities.
- Declare dependency on modules. If you require an Almanac for alignment, or consume Chronicle entries, document it.
- Document the composition contract in your
composite organism’s
contributors/composition-hooks.md(per-organism documentation). Update architecture and this page’s subscription graph to include the new composite organism if the example coalition is the right place for it.
Cross-references
- Architecture — the coalition shape these subscriptions run on.
- Composite organisms — the organism the arrows land on.
- Basic services — Atlas, Almanac, Chronicle details.
- Atomicity — what the subscription graph deliberately does not buy you.
- Threat model — how trust assumptions compose across the subscription graph.