# Group Model and Keys

[Group hosting protocol](../README.md) · [Group core objects](../core-objects.md)

## Fixed Hosting

Each group selects a hosting relay at creation, with no later migration or automatic takeover. The relay is authoritative for current state, member projections, event order, and key-epoch order; clients retain long-term message history and their own secrets. Group state uses neither account home relays nor DHT.

## Role Permissions

Member roles are `owner`, `administrator`, or `member`. Each group always has exactly one owner.

Roles express in-group business permissions:

| Operation | owner | administrator | member |
|---|---|---|---|
| Send, synchronize, and subscribe | Yes | Yes | Yes |
| Change in-group nickname | Self only | Self only | Self only |
| Create invitations | Yes | Yes | Determined by `invite_policy` |
| View invitations | All | All | Own invitations |
| Revoke invitations | All | Own invitations | Own invitations |
| Approve join applications | Yes | Yes | No |
| Change properties/roles, transfer ownership, or close | Yes | No | No |
| Remove members | Any non-owner | Ordinary members only | No |
| Ban accounts | Any non-owner | Ordinary members or nonmembers | No |
| Unban | Yes | Yes | No |
| Approve member key resets | Any member, including self | Other ordinary members only | No |
| Rotate client group secret | Yes | No | No |
| Leave voluntarily | Transfer ownership first | Yes | Yes |

Relays MUST use roles current at execution. Events or list results prove only prior acceptance, not continued future administrative authority.

## Client and Relay Secrets

Groups use two random 32-byte secrets, both from cryptographically secure sources:

- `client_group_secret` is shared by current members and MUST NOT be obtained by the hosting relay.
- `relay_epoch_secret` is independently generated by the host for each `epoch`.

### Key Epoch Advancement

`epoch` starts at 0 and strictly increases within the group, without requiring continuity. Creation, application approval, leaving, removal, member-key-reset approval, client-secret rotation, and relay-initiated rotation create new epochs. A ban batch containing current members removes them and creates one epoch; a batch containing only nonmembers does not advance keys. Property/role changes, ownership transfers, unbanning, and ordinary messages do not advance epochs.

### Client Secret Commitment

Calculate the commitment using [Network-bound JSON inputs](../../../general.md#network-bound-json-inputs):

```text
client_secret_commitment =
  network_bound_json_hash({
    $type: "meshline.group.client_secret.commitment",
    group_id,
    secret: base64url(client_group_secret)
  })
```

Store the initial commitment at creation; only successful client-secret rotation replaces it. Application approval, membership changes, and relay-secret rotation may advance `epoch` while retaining the current commitment.

The relay does not know the client secret, so cannot recompute its commitment or confirm what a client secret box contains. Members receiving boxes perform those cryptographic checks. For join-approval and member-key-reset-approval requests, the relay checks the supplied commitment against the current one and rejects mismatches.

## Group Secret Boxes and Wrapping Rules

### `GroupSecretBox`

| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
| `alg` | string | Yes | Fixed as `X25519-HKDF-SHA256-AES256GCM`; unsupported or mismatched algorithms MUST cause box rejection |
| `enc` | string | Yes | Sender-generated ephemeral 32-byte X25519 public key for this box, unpadded base64url |
| `sealed_secret` | string | Yes | Concatenated 12-byte AES-GCM nonce, 32-byte ciphertext, and 16-byte tag, unpadded base64url; decryption MUST yield exactly 32 secret bytes |

Both client and relay secret boxes use `GroupSecretBox`, wrapping their respective 32-byte secrets as follows:

1. Generate an ephemeral X25519 pair per box and compute a shared secret from ephemeral private key and target public key. The recipient uses its target private key and box `enc` to compute the same secret. Both MUST reject all-zero results under [X25519 validation](../../../general.md#x25519-shared-secret-validation) before wrapping or unwrapping proceeds.
2. Use that shared secret as HKDF-SHA-256 IKM, SHA-256 of UTF-8 `Meshline/keybox-salt/v1` as salt, and the box type's [network-bound JSON input](../../../general.md#network-bound-json-inputs) below as info to derive a 32-byte wrapping key.
3. Encrypt the secret with AES-256-GCM using that key, an independently generated random 12-byte nonce, and AAD identical to info; package as `sealed_secret`.

Ephemeral private keys and nonces MUST come from cryptographically secure random sources. The two box types MUST use their own contexts and MUST NOT be interchanged.

#### Client Secret Boxes

KDF info and AES-GCM AAD use this network-bound object:

```json
{
  "$type": "meshline.group.client_secret_box.aad",
  "group_id": "grp_...",
  "account": "neo:860833102:...",
  "member_encryption_public_key": "base64url...",
  "client_secret_commitment": "sha256:..."
}
```

Clients MUST determine target account and `member_encryption_public_key` under the method. For new-member approval or public-key reset, the approver first verifies the complete signed member request and certificate; account comes from the verified certificate, and public key from the complete signed request. Failed chain, approver-permission, request-signature, or account-binding checks MUST prevent secret wrapping or upload.

For requests containing client boxes, the relay checks target accounts and coverage as required by the method, and each box's algorithm and encoding lengths. Without member private keys, it cannot unwrap boxes or verify plaintext, AAD targets, or client-secret commitments.

On decryption, recipients MUST derive the public key from the private key corresponding to the wrapping target and reconstruct AAD from expected context. Historical boxes use their original target public keys. Failed AEAD or post-decryption commitment checks MUST reject the box.

For rotation preparation, see [`group.secret.rotation.prepare`](../methods/keys.md#groupsecretrotationprepare). Member public-key changes do not rewrap historical client boxes; those still require the corresponding old private keys.

Client boxes do not bind `epoch`; an unchanged client secret and target member key allow box reuse across epochs. Boxes are outer delivery material of write methods, excluded from signed events. The submitting owner or administrator MUST ensure the secret and target context are correct.

#### Relay Secret Boxes

KDF info and AES-GCM AAD use this network-bound object:

```json
{
  "$type": "meshline.group.relay_secret_box.aad",
  "group_id": "grp_...",
  "account": "neo:860833102:...",
  "device_id": "dev_...",
  "epoch": 7
}
```

The relay determines calling account, device ID, and device encryption public key from the `group.key.sync` device session and wraps the relay secret accordingly. Recipients reconstruct AAD using their own device context.

## Group Application Secret Derivation

After obtaining both secrets and validating the commitment, clients derive a 32-byte group application secret:

```text
epoch_application_secret = HKDF-SHA-256(
  IKM  = client_group_secret || relay_epoch_secret,
  salt = SHA-256(UTF8("Meshline/group-epoch-salt/v1")),
  info = network_bound_json_bytes({
    $type: "meshline.group.epoch_secret",
    group_id,
    epoch,
    client_secret_commitment
  }),
  L = 32
)
```

Concatenating fixed-length shared secrets before HKDF is consistent with the fixed-length combination described in [RFC 9954 Section 3.3](https://www.rfc-editor.org/rfc/rfc9954.html#section-3.3).

## Security Boundaries

The relay alone holds only `relay_epoch_secret`; members hold the client secret and relay secrets sent to their devices. A relay may deny service, prune bounded history, or hide material from different devices, but cannot decrypt using only its own data. Collusion with any party still holding the client secret allows combination with retained relay secrets for corresponding epochs to derive application secrets and decrypt those messages.

After membership or device access ends, relay-secret rotation can prevent obtaining new message keys, but cannot repair disclosure of the client secret itself.

If client-secret disclosure is known or suspected, the owner is advised to rotate it, providing new boxes only to currently trusted members. If a current member is no longer trusted, the owner MUST remove it first, generate a fresh client secret, and complete rotation. After activation, parties without it cannot derive later application secrets. Rotation cannot restore confidentiality to disclosed or decrypted history or stop currently authorized members from colluding with the relay.

To exclude collusion by a departed or removed member, the owner MUST generate a fresh secret and commitment after membership ends and prepare rotation anew; continuing an earlier preparation does not achieve that exclusion.

Banning removes current membership and prevents rejoining; secret rotation follows the same rules as removal.

A new device's historical starting point is also only a relay-access boundary. If it obtains old ciphertext and relay secret for the same epoch elsewhere, the protocol cannot prevent decryption.

This protocol claims neither forward secrecy nor post-compromise security.
