Group Model and Keys
Group hosting protocol · Group core objects
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_secretis shared by current members and MUST NOT be obtained by the hosting relay.relay_epoch_secretis independently generated by the host for eachepoch.
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:
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:
- 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
encto compute the same secret. Both MUST reject all-zero results under X25519 validation before wrapping or unwrapping proceeds. - Use that shared secret as HKDF-SHA-256 IKM, SHA-256 of UTF-8
Meshline/keybox-salt/v1as salt, and the box type's network-bound JSON input below as info to derive a 32-byte wrapping key. - 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:
{
"$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. 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:
{
"$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:
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.
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.