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

Confluences — the cross-citizen organism

audience: contributors

A confluence is a mosaik-native organism whose Config fingerprint folds two or more citizen references (lattice references, OrganismRefs, or a mix) and whose public surface coordinates across the referenced citizens. This chapter specifies its shape, compares it against alternatives, and provides a template for commissioning a new one.

Modules (Atlas, Almanac, Chronicle) are, mechanically, organisms too; their shapes are specified once in basic services. They differ from general confluences in one way: modules derive under a coalition root, while commissioned confluences derive independently of any coalition. The generic confluence discipline below applies to both; the derivation difference is called out where it matters.

The reader is assumed to have read Designing coalitions on mosaik and Anatomy of a coalition.

Definition

A confluence is an organism in every sense the zipnet and builder books use the word. It has:

  • one Config fingerprint;
  • a narrow public surface (one or two named primitives);
  • a TicketValidator composition gating bonds;
  • typed Confluence::<D>::verb(&network, &Config) free- function constructors;
  • a deterministic state machine inside a mosaik Group.

What distinguishes a confluence from a standalone organism:

  • Its content fingerprint folds in multiple citizen references — the ordered set of citizens whose public surfaces it reads from (or writes to).
  • Its state machine’s commits are a function of inputs drawn from more than one citizen’s public surface, and serve consumers on more than one citizen.

A confluence is not a seventh lattice organism; it is not part of any lattice’s identity derivation; no per-citizen operator has to know what confluences reference their citizen. A confluence is independent of any coalition that references it — its committee, state machine, and log are shared across coalition references.

When to commission one

Commission a confluence when all three of the following hold.

  1. The aggregated fact is itself a commit. If what you need can be computed ad hoc by an integrator across N citizen handles, it does not need a confluence. If multiple downstream consumers would each have to repeat the same aggregation, or if the aggregation is itself the input to yet another organism, the commit pays for itself.

  2. The inputs span more than one citizen. A confluence that reads only from one citizen is an organism in disguise; it belongs as a standalone organism (or inside that citizen’s own composition, if the citizen is a lattice).

  3. The trust model is coherent across the inputs. A confluence inherits the worst-case trust assumption of the citizen inputs it reads. If you cannot name the cross-citizen trust shape (e.g. “majority-honest across each lattice’s unseal committee, t-of-n on the oracle organism, plus majority-honest confluence committee”), you have not finished the design.

When any of the three is false, the right answer is one of:

  • A cross-citizen integrator (Shape 1 in builder’s cross- chain chapter, generalised to any mix of citizens).
  • An in-lattice organism that reads a sibling lattice’s public surface (Shape 2).
  • A new standalone organism.
  • A new organism inside an existing lattice.

Confluence versus cross-citizen integrator

TraitIntegrator-spans-citizensConfluence
Runs a Raft committeenoyes
Commits to a state machinenoyes
Admits other peers via ACLnoyes
Consumes multiple citizensyesyes
Produces a reusable commitno (per-integrator in-memory join)yes (public surface consumers can read)
Cost to stand upa driver in the integrator’s agenta crate, a committee, an ACL
Availabilitybounded by one agentcommittee-level (Raft majority)
Trust assumptionintegrator’s ownexplicit, compositional

Heuristic: if three different integrators would all perform the same aggregation, it is a candidate confluence. If exactly one integrator needs it, keep it in the agent.

The Config fingerprint

A confluence’s Config folds in:

#[non_exhaustive]
pub struct ConfluenceConfig<'a> {
    /// Role name: "intents", "ledger", "market",
    /// "correlate", … For modules:
    /// "atlas", "almanac", "chronicle".
    pub name: &'a str,

    /// Ordered set of block-building lattices whose
    /// public surfaces this confluence reads or writes.
    pub lattices: &'a [LatticeConfig<'a>],

    /// Ordered set of standalone organisms whose public
    /// surfaces this confluence reads or writes. Same
    /// story as `lattices` above, for non-lattice
    /// citizens.
    pub organisms: &'a [OrganismRef<'a>],

    /// State-affecting parameters specific to this
    /// confluence. E.g. an intent router's policy
    /// constants, a shared ledger's attribution model,
    /// an almanac's tick cadence.
    pub content: ContentParams<'a>,

    /// TicketValidator composition gating committee
    /// admission.
    pub acl: AclComposition<'a>,
}

impl ConfluenceConfig<'_> {
    pub const fn confluence_id(&self) -> UniqueId { /* ... */ }
}

Identity derives from (name, spanned citizens, content, acl) with no coalition root folded in:

  CONFLUENCE(cfg) = blake3(
                      "confluence|"
                      || SCHEMA_VERSION_U8
                      || "|" || name
                      || ordered spanned LatticeConfig references
                      || ordered spanned OrganismRef references
                      || content_fingerprint
                      || acl_fingerprint
                    )

A confluence can therefore be referenced by N coalitions; its committee, state machine, and log are shared across those references. Operators wanting two independent deployments deploy two confluences with different content or different ACLs — the cryptographic gate is the fingerprint, not coalition scope.

For modules the derivation is different — they derive under a coalition root (see basic services — Deriving a module’s identity). The public surface, state-machine discipline, and ConfluenceConfig fields are otherwise identical.

Public surface

Confluences follow the narrow-public-surface discipline without modification. Guidelines for picking the surface:

  • Key every commit by (citizen_id, slot) or (citizen_id, tick) when the confluence is per- clock-event per-origin-citizen. citizen_id is the 20-byte truncated prefix of the citizen’s blake3 fingerprint. Integrators that join commits across confluences need this key.
  • Separate routing and attestation. If your confluence both routes inputs (a write-side stream) and commits the routing outcome (a read-side collection), keep the two primitives named. Do not fold them into one.
  • Expose attestations separately when the commit carries an attestable signature. Integrators that pull attestations to on-chain settlement contracts want the signature surface distinct from the routing surface.
  • If your confluence depends on a module, declare it. A correlator that aligns to Almanac ticks reads Almanac and commits under (citizen_id, almanac_tick) keys; state that in the crate’s composition-hooks doc and in the ConfluenceConfig.content parameters (since the alignment is state-affecting).

A design wanting three or more public primitives is likely two confluences.

State machine shape

Inside the confluence’s Group, the state machine is an ordinary mosaik StateMachine<M>. It differs from a standalone organism’s state machine only in what its inputs look like: driver code outside the state machine subscribes to public surfaces on each spanned citizen and feeds observed facts into the confluence group via group.execute(...).

Template for a confluence driver:

// Inside the confluence's node crate, role: committee member.
loop {
    tokio::select! {
        // Per-citizen subscriptions the confluence reads from.
        ev = lattice_a_subscription.next() => {
            let evidence = build_evidence_for_a(ev);
            confluence_group.execute(ConfluenceCmd::ObserveLatticeA(evidence)).await?;
        }
        ev = lattice_b_subscription.next() => {
            let evidence = build_evidence_for_b(ev);
            confluence_group.execute(ConfluenceCmd::ObserveLatticeB(evidence)).await?;
        }
        ev = organism_c_subscription.next() => {
            let evidence = build_evidence_for_c(ev);
            confluence_group.execute(ConfluenceCmd::ObserveOrganismC(evidence)).await?;
        }
        // Optional: align to a coalition's Almanac the
        // confluence has been configured to track.
        ev = almanac_subscription.next() => {
            let tick = /* ... */;
            confluence_group.execute(ConfluenceCmd::AdvanceTick(tick)).await?;
        }
        // Confluence's own timers, per its state machine.
        _ = apply_deadline.tick() => {
            confluence_group.execute(ConfluenceCmd::Apply).await?;
        }
    }
}

Rules:

  • Observations are evidence, not authority. The Observe* commands carry a pointer to the upstream citizen commit (citizen id, slot or tick, commit hash). The confluence’s state machine validates the pointer against the citizen’s public surface on replay.
  • Applies are pure functions. apply(ConfluenceCmd::Apply) reads the accumulated observations and emits the confluence’s commit. It does not make new cross-citizen reads inside apply; reads belong to the driver.
  • No cross-Group calls inside apply. Same discipline as every organism.

ACL and attestation

A confluence’s TicketValidator composition can include:

  • Per-citizen readership tickets. A confluence reading from lattice A’s unseal::UnsealedPool or from an oracle organism’s feed is an integrator of that citizen from the citizen’s perspective. Committee members must hold tickets the per-citizen operator issues, same as any other integrator reading that surface.
  • TDX attestation. If the confluence sees cleartext order flow or otherwise-sensitive data, the committee members are almost always required to run an attested TDX image. .require_ticket(Tdx::new().require_mrtd(confluence_mrtd)) is the standard line.
  • Optional coalition-scoped issuer recognition. If a confluence expects to be used predominantly under one coalition whose ticket issuer is stable, it may include that issuance root in its validator composition — a choice made on the confluence side, not imposed by the coalition. See ticket issuance.

Failure modes

A confluence has more failure modes than a standalone organism because its inputs are federated:

  • One spanned citizen stalls. The confluence’s state machine must decide whether to commit with partial evidence (degrading quality but preserving liveness) or to stall that slot pending the missing citizen (preserving quality but sacrificing liveness on the slot). This is a per-confluence choice; document it in the composition contract.
  • One spanned citizen’s stable id changes. The confluence’s Config folds that reference; a change means the confluence must be redeployed. Until it is, the confluence’s subscriptions to the old citizen simply stop producing events.
  • One spanned citizen’s content hash changes (if pinned). Same as above but limited to confluences that chose to pin content — stable-id-only pinners are unaffected.
  • Confluence committee loses majority. Same failure mode as any mosaik Raft group. Liveness is lost; integrity is intact.
  • Spanned citizen’s ACL changes. If a per-citizen operator revokes the ticket committee members hold, the confluence loses read access to that citizen’s public surface. Negotiated out of band; there is no technical recourse.

Composition patterns worth naming

Three patterns recur often enough to name. These are patterns, not required confluences; modules are the specified shapes.

Intent-router pattern

A confluence whose state machine commits per-slot routed intents keyed by (target_lattice_id, slot). Reads cleartext from each spanned lattice’s unseal::UnsealedPool; commits a RoutedIntents collection each target lattice’s offer subscribes to. Trust shape: TDX-attested committee plus t-of-n threshold on the cross-citizen cleartext channel.

The builder book’s Shape 3 bridge organism is an intent- router pattern seed.

Shared-ledger pattern

A confluence whose state machine aggregates per-lattice tally::Refunds into a cross-origin-citizen attribution set, committed as SharedRefunds and SharedAttestations. Trust shape: majority-honest confluence committee plus the per-citizen trust shape of each spanned tally. An on-chain settlement contract verifying a SharedAttestation verifies both the confluence’s signature and the underlying per-lattice tally::Attestation referenced as evidence.

Integrators needing one refund feed across chains bind the confluence; integrators concerned with a single chain bind that lattice’s tally directly. Both paths coexist.

Cross-feed correlator pattern

A confluence that pairs ticks from two or more oracle organisms (or more generally, two or more non-slotted standalone organisms), committing correlated values under (citizen_id, almanac_tick) when a coalition referencing the confluence ships an Almanac and the confluence opts in, or under (citizen_id, timestamp_bin) otherwise. Trust shape: majority-honest confluence committee plus the signing assumptions of each upstream oracle.

Other patterns worth naming (non-block-building)

The three patterns above originate in block-building. Other domains will yield more. Sketches:

  • Attestation aggregator. A confluence that folds MR_TD quotes from multiple attestation organisms across vendors or regions into a single attested membership set.
  • Coordination-market organiser. A confluence that runs an allocation auction where inputs come from several independent signal organisms and outputs drive several independent downstream citizens.
  • Cross-DA committer. A confluence that commits a combined availability proof over two or more DA shards.

Each is a ConfluenceConfig whose lattices slice may be empty and whose organisms slice names the upstreams.

Checklist mirror

The commissioning checklist in topology-intro — Checklist for commissioning a new confluence applies verbatim. This page is the reference; the topology-intro page is the forcing function when commissioning one.

Cross-references

  • topology-intro — why this rung exists.
  • basic-services — the three specified module shapes plus retirement markers.
  • ticket-issuance — the optional coalition-scoped issuance root.
  • architecture — where confluences sit in one example coalition.
  • composition — the subscription graph that citizens and confluences share.
  • atomicity — what confluences deliberately do not guarantee.
  • threat-model — how a confluence’s trust assumption composes with the spanned citizens’.