pub struct GroupInfo {Show 28 fields
pub members: BTreeSet<String>,
pub display_names: HashMap<String, String>,
pub membership_revision: u64,
pub name: String,
pub description: String,
pub creator: AgentId,
pub created_at: u64,
pub updated_at: u64,
pub mls_group_id: String,
pub metadata_topic: String,
pub chat_topic_prefix: String,
pub policy: GroupPolicy,
pub policy_revision: u64,
pub roster_revision: u64,
pub members_v2: BTreeMap<String, GroupMember>,
pub join_requests: BTreeMap<String, JoinRequest>,
pub discovery_card_topic: Option<String>,
pub shared_secret: Option<Vec<u8>>,
pub secret_epoch: u64,
pub genesis: Option<GroupGenesis>,
pub state_revision: u64,
pub state_hash: String,
pub prev_state_hash: Option<String>,
pub security_binding: Option<String>,
pub tags: Vec<String>,
pub avatar_url: Option<String>,
pub banner_url: Option<String>,
pub withdrawn: bool,
}Expand description
Metadata for a group.
Persisted as JSON. The legacy v1 layout used a flat members: BTreeSet
plus a parallel display_names: HashMap. The v2 layout uses a structured
members_v2: BTreeMap<String, GroupMember>. For migration, the v1 fields
are deserialised (#[serde(default)]) but never written back
(skip_serializing). Call GroupInfo::migrate_from_v1 at load time to
fold any v1 data into v2.
Fields§
§members: BTreeSet<String>§display_names: HashMap<String, String>§membership_revision: u64§name: String§description: String§creator: AgentId§created_at: u64§updated_at: u64§mls_group_id: String§metadata_topic: String§chat_topic_prefix: String§policy: GroupPolicy§policy_revision: u64§roster_revision: u64§members_v2: BTreeMap<String, GroupMember>§join_requests: BTreeMap<String, JoinRequest>§discovery_card_topic: Option<String>32-byte random secret for MlsEncrypted groups. None for SignedPublic or for stub entries created via card import (importer isn’t a member yet).
secret_epoch: u64Monotonic epoch for the shared secret. Incremented on rekey (ban/remove).
genesis: Option<GroupGenesis>Stable genesis record — establishes the group’s permanent group_id.
Reconstructed from mls_group_id + creator + created_at when
migrating pre-D.3 blobs (see GroupInfo::migrate_from_v1).
state_revision: u64Monotonic revision of the signed state-commit chain. 0 = genesis (no authority-signed commits yet); bumped on every accepted event.
state_hash: StringCurrent state_hash — commitment to (group_id, revision, prev_hash,
roster_root, policy_hash, public_meta_hash, security_binding,
withdrawn). Recomputed by recompute_state_hash() after every
mutation.
prev_state_hash: Option<String>Previous state_hash so receivers can verify chain linking. None
at genesis.
security_binding: Option<String>Current security binding — for MlsEncrypted groups, a string of
the form "gss:epoch=N" so roster/policy/epoch changes cannot
silently drift apart. None for SignedPublic.
Optional tags for public discovery (Phase C.2 will hash these into
shard topics). Only meaningful for PublicDirectory groups.
avatar_url: Option<String>Optional avatar URL for public cards.
Optional banner URL for public cards.
withdrawn: boolWithdrawal/hidden supersession marker. Once set by a higher-revision signed commit, no further non-withdrawal actions may apply and subsequent public cards must carry the withdrawal flag.
Implementations§
Source§impl GroupInfo
impl GroupInfo
Sourcepub fn new(
name: String,
description: String,
creator: AgentId,
mls_group_id: String,
) -> Self
pub fn new( name: String, description: String, creator: AgentId, mls_group_id: String, ) -> Self
Create a new GroupInfo with the given policy (defaults to private_secure).
Sourcepub fn with_policy(
name: String,
description: String,
creator: AgentId,
mls_group_id: String,
policy: GroupPolicy,
) -> Self
pub fn with_policy( name: String, description: String, creator: AgentId, mls_group_id: String, policy: GroupPolicy, ) -> Self
Create a new GroupInfo with an explicit policy.
Rotate the group’s shared secret (called on ban/remove in MlsEncrypted groups). Returns the new secret and new epoch. Previous-epoch content encrypted by members still in the group is NOT decryptable at the new epoch — that’s forward secrecy across epochs.
Callers must arrange to distribute the new secret to remaining members (never to the departed/banned peer).
Phase D.3: also refreshes security_binding so the next state commit
incorporates the new epoch into state_hash.
Sourcepub fn stable_group_id(&self) -> &str
pub fn stable_group_id(&self) -> &str
The stable group_id (Phase D.3). Falls back to mls_group_id for
pre-D.3 groups where genesis has not yet been reconstructed.
Sourcepub fn public_meta(&self) -> GroupPublicMeta
pub fn public_meta(&self) -> GroupPublicMeta
Snapshot of the public metadata that contributes to the state hash.
Sourcepub fn recompute_state_hash(&mut self)
pub fn recompute_state_hash(&mut self)
Recompute and store state_hash from the current fields. Called
after every mutation; also called by constructors and the v1
migration path so new and migrated groups have a valid hash.
Sourcepub fn seal_commit(
&mut self,
keypair: &AgentKeypair,
now_ms: u64,
) -> Result<GroupStateCommit, ApplyError>
pub fn seal_commit( &mut self, keypair: &AgentKeypair, now_ms: u64, ) -> Result<GroupStateCommit, ApplyError>
Seal the current (already-mutated) state into a signed commit.
- bumps
state_revisionby 1, - records
prev_state_hash = self.state_hash(pre-bump hash), - recomputes the new
state_hash, - returns a signed
state_commit::GroupStateCommit.
Callers must mutate the group first (e.g. add_member, ban_member,
policy update) and then call seal_commit to produce the
authority-signed commit that can be published and verified by peers.
Sourcepub fn seal_withdrawal(
&mut self,
keypair: &AgentKeypair,
now_ms: u64,
) -> Result<GroupStateCommit, ApplyError>
pub fn seal_withdrawal( &mut self, keypair: &AgentKeypair, now_ms: u64, ) -> Result<GroupStateCommit, ApplyError>
Mark the group as withdrawn and seal the terminal higher-revision commit. A withdrawn group is superseded immediately for public discovery purposes — peers holding stale public cards must drop them on receipt of this commit regardless of TTL.
Withdrawal is owner-authored; callers must check role before calling.
Sourcepub fn apply_commit(
&mut self,
commit: &GroupStateCommit,
action_kind: ActionKind,
) -> Result<(), ApplyError>
pub fn apply_commit( &mut self, commit: &GroupStateCommit, action_kind: ActionKind, ) -> Result<(), ApplyError>
Accept a peer-authored signed commit on the apply-side.
Performs state_commit::validate_apply with the given action kind
and, on success, updates the local chain fields
(state_revision, state_hash, prev_state_hash, withdrawn) to
mirror the commit. Domain-specific mutations (roster/policy/meta)
are the caller’s responsibility and must be performed before
calling this method, so the post-mutation recomputed hash matches
commit.state_hash.
Sourcepub fn finalize_applied_commit(
&mut self,
commit: &GroupStateCommit,
) -> Result<(), ApplyError>
pub fn finalize_applied_commit( &mut self, commit: &GroupStateCommit, ) -> Result<(), ApplyError>
Finalize a commit after the caller has already performed
any action-specific pre-validation and mirrored the payload
mutation into self.
This is the second half of D.4 apply-side handling: callers may need the pre-mutation roster view to validate signer authority (e.g. self-leave), then mutate local state, then verify the recomputed post-mutation hash matches the signed commit.
Sourcepub fn secure_message_key(&self) -> Option<Vec<u8>>
pub fn secure_message_key(&self) -> Option<Vec<u8>>
Derive the per-message AEAD key from the group’s current shared secret. Returns None if the group has no shared secret (e.g., SignedPublic, or the caller hasn’t received a welcome yet).
Sourcepub fn derive_message_key(secret: &[u8], epoch: u64, group_id: &str) -> Vec<u8> ⓘ
pub fn derive_message_key(secret: &[u8], epoch: u64, group_id: &str) -> Vec<u8> ⓘ
Pure helper so both encryptor and decryptor derive the same key from (secret, epoch, group_id).
Sourcepub fn migrate_from_v1(&mut self)
pub fn migrate_from_v1(&mut self)
Migrate v1 (BTreeSet + display_names) data into v2 structured members. Also backfills Phase D.3 stable-genesis + state-hash fields for blobs written before D.3 landed. Idempotent: may be called multiple times.
Sourcepub fn add_member(
&mut self,
agent_id_hex: String,
role: GroupRole,
added_by: Option<String>,
display_name: Option<String>,
)
pub fn add_member( &mut self, agent_id_hex: String, role: GroupRole, added_by: Option<String>, display_name: Option<String>, )
Add or update a member. If the member already exists, updates state to Active.
Sourcepub fn add_member_with_kem(
&mut self,
agent_id_hex: String,
role: GroupRole,
added_by: Option<String>,
display_name: Option<String>,
kem_public_key_b64: Option<String>,
)
pub fn add_member_with_kem( &mut self, agent_id_hex: String, role: GroupRole, added_by: Option<String>, display_name: Option<String>, kem_public_key_b64: Option<String>, )
Add or update a member, optionally recording their ML-KEM-768 public
key for future secure-share delivery. If kem_public_key_b64 is
Some, it overwrites any previously-recorded value; None preserves
the existing one.
Sourcepub fn set_member_kem_public_key(
&mut self,
agent_id_hex: &str,
kem_public_key_b64: String,
)
pub fn set_member_kem_public_key( &mut self, agent_id_hex: &str, kem_public_key_b64: String, )
Record a member’s ML-KEM-768 public key without changing any other
state. Used when we learn a key for an existing member via a later
event (e.g. JoinRequestCreated after a stub was already seeded).
Sourcepub fn remove_member(&mut self, agent_id_hex: &str, removed_by: Option<String>)
pub fn remove_member(&mut self, agent_id_hex: &str, removed_by: Option<String>)
Mark a member as Removed (soft delete — entry retained for audit).
Sourcepub fn ban_member(&mut self, agent_id_hex: &str, banned_by: Option<String>)
pub fn ban_member(&mut self, agent_id_hex: &str, banned_by: Option<String>)
Mark a member as Banned.
Sourcepub fn unban_member(&mut self, agent_id_hex: &str)
pub fn unban_member(&mut self, agent_id_hex: &str)
Unban — transition Banned → Active (keeps current role).
Sourcepub fn set_member_role(&mut self, agent_id_hex: &str, role: GroupRole)
pub fn set_member_role(&mut self, agent_id_hex: &str, role: GroupRole)
Change a member’s role. Caller must verify caller’s authority first.
Sourcepub fn has_active_member(&self, agent_id_hex: &str) -> bool
pub fn has_active_member(&self, agent_id_hex: &str) -> bool
Check that a member is present and Active.
Sourcepub fn has_member(&self, agent_id_hex: &str) -> bool
pub fn has_member(&self, agent_id_hex: &str) -> bool
Legacy compat: true if active (matches old has_member semantics).
Sourcepub fn caller_role(&self, agent_id_hex: &str) -> Option<GroupRole>
pub fn caller_role(&self, agent_id_hex: &str) -> Option<GroupRole>
Returns the caller’s effective role if they are an active member.
Sourcepub fn is_banned(&self, agent_id_hex: &str) -> bool
pub fn is_banned(&self, agent_id_hex: &str) -> bool
Returns true if the agent is currently banned.
Sourcepub fn set_display_name(&mut self, agent_id_hex: &str, name: String)
pub fn set_display_name(&mut self, agent_id_hex: &str, name: String)
Set a display name for a member. Member must exist.
Sourcepub fn display_name(&self, agent_id_hex: &str) -> String
pub fn display_name(&self, agent_id_hex: &str) -> String
Get a member’s display name, falling back to truncated agent ID.
Sourcepub fn active_members(&self) -> impl Iterator<Item = &GroupMember>
pub fn active_members(&self) -> impl Iterator<Item = &GroupMember>
Iterator over currently active members.
Sourcepub fn active_member_count(&self) -> usize
pub fn active_member_count(&self) -> usize
Count of currently active members.
Sourcepub fn active_admin_count(&self) -> usize
pub fn active_admin_count(&self) -> usize
Count of currently active Admins (including Owner).
Sourcepub fn owner_agent_id(&self) -> Option<String>
pub fn owner_agent_id(&self) -> Option<String>
Owner’s agent hex, if one exists.
Sourcepub fn general_chat_topic(&self) -> String
pub fn general_chat_topic(&self) -> String
Default chat topic for the group (“general” room).
Sourcepub fn to_group_card(&self) -> Option<GroupCard>
pub fn to_group_card(&self) -> Option<GroupCard>
Build a shareable discoverable GroupCard from this group’s state.
Returns None if the group is Hidden.
The returned card carries the Phase D.3 state-commit binding
(revision, state_hash, prev_state_hash, issued_at,
expires_at, withdrawn) but is unsigned. Callers with the
authority’s keypair should call GroupCard::sign before
publishing to turn it into a verifiable public artifact.
Sourcepub fn to_signed_group_card(
&self,
keypair: &AgentKeypair,
) -> Result<Option<GroupCard>, ApplyError>
pub fn to_signed_group_card( &self, keypair: &AgentKeypair, ) -> Result<Option<GroupCard>, ApplyError>
Build and sign a GroupCard in one step.
Returns None if the group is Hidden AND not withdrawn. Callers
publishing a withdrawal card must call seal_withdrawal() first to
advance the state chain, then this helper to emit the terminal
signed card.
Trait Implementations§
Source§impl<'de> Deserialize<'de> for GroupInfo
impl<'de> Deserialize<'de> for GroupInfo
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for GroupInfo
impl RefUnwindSafe for GroupInfo
impl Send for GroupInfo
impl Sync for GroupInfo
impl Unpin for GroupInfo
impl UnsafeUnpin for GroupInfo
impl UnwindSafe for GroupInfo
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more