Group Core Objects
Group ID
The creator generates a new 16-byte nonce, never reused by the same creator on the same relay; a cryptographically secure random source is recommended. Compute SHA-256 over the network-bound JSON input of this identity object:
{
"$type": "meshline.group.identity",
"creator": "neo:860833102:...",
"nonce": "base64url...",
"relay_id": "0x..."
}
creator is the initial owner and relay_id the selected host; both and nonce remain unchanged throughout the group lifecycle. The group ID is as follows, where group_identity_input means the group identity object above:
group_id = "grp_" + base64url(first_16_bytes(SHA-256(network_bound_json_bytes(group_identity_input))))
group_id MUST match ^grp_[A-Za-z0-9_-]{22}$. Name, description, members, capacity, invitation policy, event positions, and key epochs do not participate. The ID binds the host but does not reveal it by reverse derivation.
GroupState
GroupState is current group state maintained by the host:
| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
group_id |
string | Yes | Group described by this state |
name |
string | Yes | Current name; MUST include a non-whitespace character; at most 256 UTF-8 bytes |
description |
string | No | Current description; omission means no description; if nonempty, MUST NOT be solely whitespace; at most 4 KiB (4,096 UTF-8 bytes) |
status |
string | Yes | active means existing; closed means permanently closed |
owner |
string | Yes | Owner account, appearing exactly once in current membership with role owner |
member_capacity |
integer | Yes | Positive member-count limit; may temporarily be below current count, retaining existing members but rejecting further admission |
member_count |
integer | Yes | Positive current count, including owner and administrators |
invite_policy |
string | Yes | Invitation policy; owner and administrators are not restricted by it |
invite_policy means:
| Value | Invitations ordinary members may create |
|---|---|
administrators |
None |
members_targeted |
Only targeted invitations naming invitee |
members_shareable |
Targeted and public invitations |
Membership is indexed by group and account IDs and contains account, role, and member_encryption_public_key. Clients establish membership, roles, and keys from the verified administration chain.
While the group exists, the relay MUST retain current state and membership. State-changing methods MUST atomically update projections and append corresponding events.
GroupEvent
GroupEvent is an authoritative timeline entry appended by the host for administration, key rotation, and messages:
| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
sequence |
integer | Yes | Unique assigned position; creation is 0, later values strictly exceed the prior head without required continuity. Clients use it for ordering, pagination, and message references |
epoch |
integer | Yes | Key epoch after the event; creation is 0, messages MUST match their envelope, and events not advancing keys retain the preceding event's epoch in the complete timeline |
payload |
object | Yes | Complete event body identified by payload.$type; structure and handling follow its object/method definition |
accepted_at |
integer | Yes | Relay acceptance UTC Unix seconds, used for retention, not as a substitute for sequence ordering |
signer_device_id |
string | Conditional | Device verified at client-write acceptance; required for client events, referencing group.sync certificates, and omitted for relay-initiated rotation |
Across the complete timeline, epoch is nondecreasing without required continuity.
Messages have payload.$type = meshline.group.message and may expire under Retention and access. Other events are administration events, permanently retained with verification certificates.
Message filtering/pruning or starting mid-history may make adjacent visible events nonadjacent in the full timeline. Administration events are unrestricted by message intervals and MUST NOT be pruned; complete synchronization from creation obtains all of them. Epoch handling follows group.sync.
Invitation creation/revocation, application submission/rejection, key-reset request submission/replacement/withdrawal/rejection, and client-secret rotation preparation do not enter the timeline. Approvers verify these pending records when acting. Join approval records authorized accounts and keys; reset approval records accounts and new keys. Banning and unbanning affect future permissions, so successful calls MUST write complete signed requests into the administration chain.
Relay Secret Rotation Events
Relay-initiated rotation generates this payload:
| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
$type |
string | Yes | Fixed as meshline.group.key.rotated |
The new epoch is outer GroupEvent.epoch. This relay-generated body carries no device_signature, and outer signer_device_id MUST be omitted. Clients check the fixed type and epoch advancement. It is permanently retained but changes neither members, roles, bans, client-secret commitment, nor administration head.
Administration Chain
Participating Events and Predecessor References
The chain links only client-signed administration events actually written to the timeline. Messages and relay rotations do not advance it. Participating body types are:
Body $type |
Effect |
|---|---|
meshline.group.create |
Establish group, initial owner, and client-secret commitment |
meshline.group.application.approval |
Admit members |
meshline.group.update |
Change properties |
meshline.group.role.update |
Change administrative roles |
meshline.group.owner.transfer |
Transfer ownership |
meshline.group.member.leave |
Signing account leaves |
meshline.group.member.removal |
Remove target members |
meshline.group.member.recovery.approval |
Approve new member public keys |
meshline.group.secret.rotation |
Update commitment and optional owner key |
meshline.group.member.ban |
Ban targets and remove current members among them |
meshline.group.member.unban |
Lift bans |
meshline.group.close |
Permanently close |
Creation starts the chain without prev_hash. Every other listed request's signed body MUST carry the previous locally verified administration digest as prev_hash, matching ^sha256:[A-Za-z0-9_-]{43}$, decoding to 32 bytes in canonical unpadded base64url. It is signed with all business fields and cannot be added or rewritten by the relay after verification. Requests not written do not advance the chain; message envelopes and relay-rotation bodies do not define this field.
Administration Digest Calculation
For administration event E, hash the network-bound input of complete payload, retaining root device_signature and all unknown properties:
management_hash(E) = network_bound_json_hash(E.payload)
Construction follows Network-bound JSON inputs. The digest includes the body signature; body signing excludes only root device_signature. signer_device_id locates the certificate; clients still MUST verify certificate, body signature, and account permission. Other outer fields retain ordering, time, and key checks. The next operation references management_hash(E).
Relay Commit and Concurrency Control
Relays MUST validate session, signature, and caller permissions. Creation establishes the starting point under group.create. Other requests MUST have prev_hash equal to the current head, with both permission and equality still holding at activation. Violating field constraints returns bad_request.
Success MUST append an administration event and update the head. Request evidence, state, event, timeline head, and administration head MUST commit atomically.
Of two requests citing one head, only one can succeed. To resubmit the other, synchronize new events, recheck permissions and operation content, and sign again. Clients MUST NOT replace only the digest while reusing the old signature.
Client Verification and Recovery
Chain Verification and State Reconstruction
Initially verify from creation: check expected ID derivation, creator account, certificate, and creation signature, establish state, and compute the head. Then verify each prev_hash, signature, and signer's membership, role, and ban state in the preceding state, apply the method's effects, and compute the new head.
Members are distinct by account, with no duplicates; roles are owner, administrator, or member. Creation establishes the owner; admission establishes ordinary members. Role changes, transfers, leaving, removal/banning, reset approval, and client rotation modify membership under their methods. Rejoining uses the newly approved key and cannot restore the old member record. Pending applications/resets do not change effective state.
Bans start empty. Clients obtain events through group.sync, adding and removing accounts according to verified ban/unban requests to reconstruct the set.
Applicants and resetting members sign complete requests containing their public keys. Approvers independently verify request signatures, certificates, and account binding, then sign those accounts and keys in approval objects. Other clients verify approval events and prior approver authority. Public-key sourcing and validation before secret wrapping MUST follow Client secret boxes.
Verification Interruption and Recovery
Missing intermediate administration events, invalid signatures, unauthorized operations, or uninterpretable administration extensions block later state verification; ordinary-message rejection rules MUST NOT be used to skip them. Unknown client administration body types MUST pause the projection until an extension defines hashing participation and state rules. Clients may retain them for reprocessing. Unknown properties still participate in hashes and signatures but affect state only as defined by known method fields.
On local reload, verified administration head, membership, bans, and synchronization position MUST remain consistent. Continue the verified chain rather than replacing it with a relay snapshot. On verification failure, stop applying later administration events and report an error. Missing records may be requested again through group.sync.