feat: implement MLS lifecycle and multi-device support

Phase 5.3 (MLS lifecycle):
- Add group.proto with RemoveMember, UpdateGroupMetadata, ListGroupMembers, RotateKeys RPCs
- Add GroupService domain logic with metadata and membership persistence
- Add v2 RPC handlers for all 4 group management endpoints (method IDs 410-413)
- Add SDK functions: remove_member_from_group, leave_group, rotate_group_keys, set_group_metadata, get_group_members
- Add REPL commands: /group remove, /group rename, /group rotate-keys, /group leave
- Add 5 unit tests for GroupService (metadata CRUD, membership add/list/remove)

Phase 5.1 (multi-device):
- Wire device_id through SDK fetch/ack functions (fetch_for_device, ack)
- Add /devices list|add|remove REPL commands with tab completion
- Add clear_failed_outbox to ConversationStore
- Fix missing message_id/device_id fields in SDK proto struct initializers
This commit is contained in:
2026-03-04 20:20:55 +01:00
parent a90020fe89
commit b94248b3b6
7 changed files with 698 additions and 129 deletions

View File

@@ -165,6 +165,9 @@ impl QpqCompleter {
for sub in &["create", "invite", "leave", "list", "members", "remove", "rename", "rotate-keys"] {
names.push(format!("/group {sub}"));
}
for sub in &["list", "add", "remove"] {
names.push(format!("/devices {sub}"));
}
Self { names }
}
}
@@ -934,9 +937,8 @@ async fn do_devices(client: &mut QpqClient, args: &str) -> anyhow::Result<()> {
}
let rpc = client.rpc().map_err(|e| anyhow::anyhow!("{e}"))?;
// Generate a random device ID (16 bytes).
let mut dev_id = vec![0u8; 16];
quicproquo_core::getrandom::fill(&mut dev_id)
.map_err(|e| anyhow::anyhow!("rng: {e}"))?;
use rand::Rng;
let dev_id: Vec<u8> = rand::rng().random::<[u8; 16]>().to_vec();
let was_new =
quicproquo_sdk::devices::register_device(rpc, &dev_id, name).await?;
if was_new {