feat: DM epoch fix, federation relay, and mDNS mesh discovery

- schema: createChannel returns wasNew :Bool to elect the MLS initiator
  unambiguously; prevents duplicate group creation on concurrent /dm calls
- core: group helpers for epoch tracking and key-package lifecycle
- server: federation subsystem — mTLS QUIC server-to-server relay with
  Cap'n Proto RPC; enqueue/batchEnqueue relay unknown recipients to their
  home domain via FederationClient
- server: mDNS _quicproquo._udp.local. service announcement on startup
- server: storage + sql_store — identity_exists, peek/ack, federation
  home-server lookup helpers
- client: /mesh peers REPL command (mDNS discovery, feature = "mesh")
- client: MeshDiscovery — background mDNS browse with ServiceDaemon
- client: was_new=false path in cmd_dm waits for peer Welcome instead of
  creating a duplicate initiator group
- p2p: fix ALPN from quicnprotochat/p2p/1 → quicproquo/p2p/1
- workspace: re-include quicproquo-p2p in members
This commit is contained in:
2026-03-03 14:41:56 +01:00
parent e24497bf90
commit c8398d6cb7
27 changed files with 3375 additions and 303 deletions

View File

@@ -645,12 +645,16 @@ pub async fn resolve_identity(
}
}
/// Create a 1:1 DM channel with a peer. Returns the 16-byte channel ID.
/// If a channel already exists between the two users, returns the existing ID.
/// Create a 1:1 DM channel with a peer.
///
/// Returns `(channel_id, was_new)` where `channel_id` is the stable 16-byte identifier and
/// `was_new` is `true` iff this call created the channel for the first time. When `was_new` is
/// `false`, the channel already existed (created by the peer), and the caller should wait for
/// the peer's MLS Welcome to arrive via the background poller rather than creating a new MLS group.
pub async fn create_channel(
client: &node_service::Client,
peer_key: &[u8],
) -> anyhow::Result<Vec<u8>> {
) -> anyhow::Result<(Vec<u8>, bool)> {
let mut req = client.create_channel_request();
{
let mut p = req.get();
@@ -665,14 +669,14 @@ pub async fn create_channel(
.await
.context("create_channel RPC failed")?;
let channel_id = resp
.get()
.context("create_channel: bad response")?
let reader = resp.get().context("create_channel: bad response")?;
let channel_id = reader
.get_channel_id()
.context("create_channel: missing channel_id")?
.to_vec();
let was_new = reader.get_was_new();
Ok(channel_id)
Ok((channel_id, was_new))
}
/// Return the current Unix timestamp in milliseconds.