# Groups
Consensus Groups are clusters of trusted nodes on the same mosaik network that coordinate with each other for load balancing and failover. Members of a group share a secret key, maintain a consistent replicated state through a modified Raft consensus protocol, and stay connected via an all-to-all mesh of persistent **bonds**.
## Overview
```
┌──────────┐
│ Node A │
│ (Leader) │
└────┬──┬───┘
bond/ \bond
/ \
┌──────────┐ ┌──────────┐
│ Node B │─bond─│ Node C │
│(Follower) │ │(Follower) │
└───────────┘ └───────────┘
```
Every pair of group members maintains a persistent **bond** — an authenticated, bidirectional QUIC connection. Bonds carry Raft consensus messages, heartbeats, and log-sync traffic.
## Trust Model
Groups are **not** Byzantine fault tolerant. All members within a group are assumed to be honest and operated by the same entity. The `GroupKey` acts as the primary admission control — only nodes that know the key can join.
For stronger integrity guarantees, groups can optionally require hardware attestations via `TicketValidator`s (see `GroupBuilder::require_ticket()`). When combined with TEE enclaves such as Intel TDX, this provides cryptographic proof that every group member is running the expected software, hardening the trust model against compromised or tampered nodes.
## Quick Start
```rust,ignore
use mosaik::groups::GroupKey;
// All group members must use the same key
let key = GroupKey::generate();
// Join with default settings (NoOp state machine)
let group = network.groups().with_key(key).join();
// Wait for leader election
let leader = group.when().leader_elected().await;
println!("Leader: {leader}");
// Check local role
if group.is_leader() {
println!("I am the leader");
}
```
## Group Identity
A `GroupId` is derived from up to four components:
1. **Group key** — the shared secret (`GroupKey`)
2. **Consensus configuration** — election timeouts, heartbeat intervals, etc.
3. **State machine signature** — the state machine's `signature()` + state sync `signature()`
4. **Ticket validator signatures** (optional) — the `signature()` of each configured `TicketValidator`, if `.require_ticket()` is used
Any divergence in these values across nodes produces a different `GroupId`, preventing misconfigured nodes from bonding.
```rust,ignore
// GroupId derivation (internal)
let id = key.secret().hashed()
.derive(consensus.digest())
.derive(state_machine.signature())
.derive(state_machine.state_sync().signature());
// For each require_ticket() validator:
// id = id.derive(validator.signature());
```
## Key Types
| `Groups` | Public API gateway — one per `Network` |
| `GroupBuilder` | Typestate builder for configuring and joining a group |
| `Group<M>` | Handle for interacting with a joined group |
| `GroupKey` | Shared secret for admission control |
| `GroupId` | Unique identifier (`Digest`) derived from key + config + machine |
| `Bond` / `Bonds` | Persistent connections between group members |
| `When` | Reactive status API for group state changes |
| `TicketValidator` | Trait for ticket-based peer authentication during bonding |
| `Expiration` | Validation result: `Never` or `At(DateTime<Utc>)` |
| `InvalidTicket` | Error returned when ticket validation fails |
| `ConsensusConfig` | Raft timing parameters |
## ALPN Protocol
Groups use `/mosaik/groups/1` as their ALPN identifier.
## Close Reason Codes
| `30_400` | `InvalidHandshake` | Error during handshake decoding |
| `30_404` | `GroupNotFound` | Group ID not known to acceptor |
| `30_405` | `InvalidProof` | Authentication proof invalid |
| `30_408` | `Timeout` | Timed out waiting for response |
| `30_429` | `AlreadyBonded` | A bond already exists between these peers |
## Subsystem Configuration
```rust,ignore
use mosaik::groups::Config;
let config = Config::builder()
.with_handshake_timeout(Duration::from_secs(2)) // default
.build()?;
```
| `handshake_timeout` | 2 seconds | Timeout for bond handshake completion |