Group Manifest

The Group Manifest is the authoritative membership record for a Sync Group. It defines who belongs, who can send, whose messages get accepted. Every device holds a copy. Any member can update it.

Structure

FieldTypeDescription
group_id[u8; 32]Stable random identifier for the group. Never changes.
versionu64Monotonically increasing. Bumps on every membership change.
membersVec<ManifestMember>All current members — noise pub + signing pub + display name.
issued_by[u8; 32]Signing public key of the member who issued this version.
signature[u8; 64]Ed25519 signature over `group_id

Each member entry holds:

  • noise_pub — the device’s X25519 key, used to address Envelopes.
  • signing_pub — the device’s Ed25519 key, used to verify Envelope signatures.
  • name — a short display name derived deterministically from the signing public key (first 4 bytes as hex). Not user-configurable at this layer.

The manifest is Protobuf on the wire. It travels as a GroupManifest message (tag 0x04) inside a regular Envelope — encrypted, signed, and relayed identically to Sync messages.

Validity rules

A device accepts a new manifest only if all three conditions hold:

  1. Valid signature. The Ed25519 signature verifies against issued_by, over the canonical signing message (group_id || version || members).
  2. Known issuer. issued_by is a member in the recipient’s current manifest. An update from a non-member is rejected even with a valid signature.
  3. Higher version. Incoming version is strictly greater than the current one. Same-version and lower-version updates are rejected.

The genesis manifest (the very first one, when the group is created) has no previous manifest to check against. The issuer check is skipped — the genesis is self-authorising.

Last-version-wins convergence

Any current member can issue a new manifest at any time. If two members issue conflicting updates at the same moment — A removes B while C adds D — both manifests propagate. Every recipient applies whichever has the higher version.

The result is eventual consistency with no consensus protocol involved. Higher version wins, always. In a tie (two simultaneous updates with the same version), neither is accepted — they both fail the strictly-higher check against each other, and one of the issuers will eventually retry with a higher version.

Membership changes

Adding a member. After pairing, the admitting device issues a new manifest that includes the joiner and pushes it to every existing member. The joiner also receives the current manifest as part of the join flow, so it has a complete view of the group from the moment it lands.

Soft removal. The removing device issues a new manifest that excludes the target. Remaining members update their local copy and start filtering the removed device’s messages. The removed device, when it eventually receives the new manifest, fires onRemovedFromGroup().

Destroy Group. Any member calls destroyGroup(). A Revoke message goes out to every known member. Every device wipes its local state and rotates its keypair on next launch. The manifest is gone, the group no longer exists.

For the full semantics of each operation, see Revocation.

Inbound filtering

Every inbound Envelope is checked against the current manifest after decryption and signature verification. If author_pub (extracted from the decrypted payload) isn’t a signing_pub in the current manifest, the message is silently discarded.

This is how soft removal actually takes effect at the message level. A removed device’s Envelopes pass relay routing (the relay doesn’t know about membership), pass decryption (the payload is still valid ciphertext), and pass Ed25519 signature verification (the device’s key is still cryptographically valid) — but they fail the manifest filter and get dropped without anyone telling the caller.

What the relay knows

Nothing. The relay has no concept of Group Manifests, group IDs, or membership. It routes Envelopes by recipient_pub. A manifest update is just another Envelope — routed to the recipient’s Inbox and deleted on delivery. The relay can’t tell a GroupManifest message from a Sync message.