mosaik 0.3.8

A Rust runtime for building self-organizing, leaderless distributed systems.
Documentation
# Groups

Availability 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 sole admission control — only nodes that know the key can join.

## 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

| Type                | Description                                                      |
| ------------------- | ---------------------------------------------------------------- |
| `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

| Code     | Name               | Meaning                                   |
| -------- | ------------------ | ----------------------------------------- |
| `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()?;
```

| Option              | Default   | Description                           |
| ------------------- | --------- | ------------------------------------- |
| `handshake_timeout` | 2 seconds | Timeout for bond handshake completion |