# How BitChat Manages Group Invites and Key Rotation in Private Messaging

> Discover how BitChat manages group invites and key rotation using creator-signed state objects and Noise protocol. Learn about secure membership and encryption key management.

- Repository: [permissionlesstech/bitchat](https://github.com/permissionlesstech/bitchat)
- Tags: internals
- Published: 2026-07-27

---

**BitChat implements creator-signed group state objects over the Noise protocol, where invitations and key rotations are distributed as signed payloads containing epoch counters and member lists, ensuring only the group creator can modify membership or encryption keys.**

The permissionlesstech/bitchat repository implements a privacy-focused group messaging system built on the Noise protocol. At its core, group management relies on cryptographically signed state objects that control member admission and symmetric key rotation through an epoch-based versioning system.

## Group Invitation Flow

### Creator-Signed Invitations

Group invitations originate exclusively from the creator through `ChatGroupCoordinator.inviteMember(nickname:)`. Located in [`bitchat/ViewModels/ChatGroupCoordinator.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/ViewModels/ChatGroupCoordinator.swift), this method validates the target peer exists in the current context, then constructs a **signed group payload** using `signedStatePayload(for:group:key:)`. This payload encapsulates the current group epoch, the complete member list (including the new invitee), and a cryptographic signature generated from the creator’s static signing key.

The invite logic ensures that only the creator possesses the private key necessary to authorize new members, preventing any participant from unilaterally expanding the group.

### Transport Over Noise

Once constructed, the invitation transmits as a `groupInvite` message (type `0x06` defined in [`bitchat/Protocols/BitchatProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Protocols/BitchatProtocol.swift)). The payload travels 1-to-1 over an established Noise session, ensuring confidentiality and integrity during transit. This design guarantees that only the intended recipient can decrypt and process the invitation, intercepting passive surveillance and man-in-the-middle attempts.

## Key Rotation Mechanism

### Epoch Increment and Key Generation

When membership changes—specifically when the creator removes a member—or when explicit rotation is requested, the system invokes `GroupStore.rotateKey(groupID:members:)` from [`bitchat/Services/Groups/GroupStore.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/Groups/GroupStore.swift). This function performs three critical operations:

- Increments the group's **epoch** counter to invalidate previous states
- Replaces the member list with the remaining participants
- Generates a fresh symmetric key (`newKey`) for subsequent message encryption

The updated `BitchatGroup` object persists these changes, creating a clean cryptographic boundary between old and new group compositions.

### Broadcasting Key Updates

Following a successful rotation, the coordinator sends a **key-update** message (type `0x07` in [`GroupProtocol.swift`](https://github.com/permissionlesstech/bitchat/blob/main/GroupProtocol.swift)) to all remaining members. Like invitations, this update uses `distributeState` with the creator's signature attached, forcing participants to discard the previous key and adopt the fresh one. The coordinator rebuilds the signed payload using `signedStatePayload(for:group:key:)` with the rotated group state and new symmetric key before transmission.

## Security Architecture

### Creator-Authority Verification

Because every state transition—whether inviting members or rotating keys—requires the creator's signature, the protocol maintains strict **creator authority**. Peers validate incoming states by verifying the signature against the creator's public key before applying any membership or key changes. This prevents compromised members from forging invites or injecting unauthorized key material.

### Replay Protection via Epochs

The epoch counter embedded in each group state functions as a monotonic sequence number. When `GroupProtocol.handleIncomingState` processes incoming messages, it rejects any state carrying an epoch lower than the current local value. This mechanism prevents **replay attacks** where adversaries might attempt to restore old group compositions or retired encryption keys.

## Implementation Examples

The following Swift code illustrates the core invitation and rotation flows:

```swift
// Inviting a new member (ChatGroupCoordinator.swift)
func inviteMember(nickname rawNickname: String) -> CommandResult {
    guard let peer = context.peers[rawNickname] else {
        return .error(message: String(localized: "system.group.peer_not_found"))
    }
    // Build a signed group state that includes the new member
    guard let payload = signedStatePayload(for: currentGroup, key: currentKey) else {
        return .error(message: String(localized: "system.group.invite_failed"))
    }
    // Send the invite (type .invite) over the Noise session
    distributeState(payload, group: currentGroup, excluding: [], type: .invite)
    return .ok(message: String(localized: "system.group.invited"))
}

```

```swift
// Rotating the group key after member removal (ChatGroupCoordinator.swift)
guard let (rotated, newKey) = context.groupStore.rotateKey(
        groupID: group.groupID,
        members: remainingMembers) else {
    return .error(message: String(localized: "system.group.rotate_failed"))
}

// Broadcast the new key (type .keyUpdate) to all remaining members
let payload = signedStatePayload(for: rotated, key: newKey)!
distributeState(payload, group: rotated, excluding: [], type: .keyUpdate)

```

```swift
// Message type definitions (GroupProtocol.swift)
enum GroupMessageType: UInt8 {
    case invite    = 0x06   // Creator-signed group state (invite)
    case keyUpdate = 0x07   // Creator-signed group state (key rotation)
}

```

The command-line interface for these operations registers through [`bitchat/Services/CommandProcessor.swift`](https://github.com/permissionlesstech/bitchat/blob/main/bitchat/Services/CommandProcessor.swift), exposing `/group invite` and related commands to the user interface.

## Summary

- **Creator-exclusive authority**: Only the group creator can sign invitations and key rotations via `signedStatePayload`, enforced by signature verification in `GroupProtocol`.
- **Epoch-based versioning**: The `epoch` counter in `GroupStore.rotateKey` provides atomic key transitions and replay protection.
- **Noise transport security**: Both `groupInvite` (0x06) and `keyUpdate` (0x07) messages transmit over established Noise sessions, ensuring confidentiality.
- **Atomic membership changes**: Key rotation automatically triggers when members are removed, ensuring forward secrecy for excluded participants.

## Frequently Asked Questions

### How are group invitations authenticated?

Invitations carry a cryptographic signature generated by the creator's static signing key within `signedStatePayload(for:group:key:)`. Recipients verify this signature in `GroupProtocol.handleIncomingState` before accepting the member list and group key, ensuring only the legitimate creator can authorize new participants.

### What happens when a member is removed from a BitChat group?

Removal triggers `GroupStore.rotateKey(groupID:members:)`, which increments the group epoch, generates a fresh symmetric key, and updates the member list. The creator then broadcasts a `keyUpdate` message (type `0x07`) to all remaining members, who immediately switch to the new key and invalidate the old one, preventing the removed member from decrypting future messages.

### How does BitChat prevent replay attacks on group state?

Each group state includes a monotonic `epoch` counter. When processing incoming states, `GroupProtocol.handleIncomingState` rejects any message with an epoch equal to or lower than the currently stored value. This ensures outdated invitations or key states cannot be replayed to restore previous group configurations.

### Can non-creator members invite others or rotate keys?

No. The protocol strictly reserves invitation and rotation capabilities to the creator. Because both operations require a valid signature from the creator's private key—which only `ChatGroupCoordinator` possesses in the signing context—non-creator members lack the cryptographic material necessary to generate valid `groupInvite` or `keyUpdate` payloads that peers would accept.