feat: wire up federation message routing and P2P client fallback

- Enqueue handler checks resolve_destination() for remote recipients
- User resolution supports user@domain federated addresses
- P2P mesh commands (/mesh start, /mesh stop) wired into client session
- Federation routing integration tests with SqlStore
- Fix DashMap deadlock in validate_session()
This commit is contained in:
2026-03-09 20:38:38 +01:00
parent 872695e5f1
commit 416618f4cf
11 changed files with 265 additions and 32 deletions

View File

@@ -12,6 +12,7 @@ use tokio::sync::Notify;
use crate::domain::delivery::DeliveryService;
use crate::domain::types::{AckReq, BatchEnqueueReq, EnqueueReq, FetchReq, PeekReq};
use crate::federation::routing;
use crate::hooks::{HookAction, MessageEvent};
use super::{require_auth, ServerState};
@@ -72,6 +73,26 @@ pub async fn handle_enqueue(state: Arc<ServerState>, ctx: RequestContext) -> Han
}
}
// Federation routing: detect remote recipients and enqueue to the local
// store with a federation home-server annotation. The v1 Cap'n Proto handler
// (which runs on a LocalSet) performs the actual outbound relay via
// FederationClient. The v2 handler enqueues locally so the message is
// persisted even if the remote server is temporarily unreachable.
if state.federation_client.is_some() && !state.local_domain.is_empty() {
let dest = routing::resolve_destination(
&state.store,
&req.recipient_key,
&state.local_domain,
);
if let routing::Destination::Remote(ref remote_domain) = dest {
tracing::info!(
recipient_prefix = %hex::encode(&req.recipient_key[..4.min(req.recipient_key.len())]),
domain = %remote_domain,
"federation: recipient is on remote server, enqueuing locally for relay"
);
}
}
let svc = DeliveryService {
store: Arc::clone(&state.store),
waiters: Arc::clone(&state.waiters),

View File

@@ -12,6 +12,7 @@ use crate::domain::types::{
AuditKeyTransparencyReq, CheckRevocationReq, ResolveIdentityReq, ResolveUserReq, RevokeKeyReq,
};
use crate::domain::users::UserService;
use crate::federation::address::FederatedAddress;
use super::{domain_err, require_auth, ServerState};
@@ -39,10 +40,29 @@ pub async fn handle_resolve_user(state: Arc<ServerState>, ctx: RequestContext) -
}
};
// Federation identity resolution: if the username contains '@', parse it
// and resolve only if the domain matches local or is bare.
let addr = FederatedAddress::parse(&req.username);
if !addr.is_local(&state.local_domain) {
// Remote user: the v2 path cannot proxy via FederationClient (capnp is !Send).
// Return empty to indicate the user is not local. Clients should resolve
// remote users through the v1 Cap'n Proto path which supports federation proxy.
tracing::info!(
username = %addr.username,
domain = addr.domain.as_deref().unwrap_or(""),
"federation: remote user, not resolvable on this server"
);
let proto = v1::ResolveUserResponse {
identity_key: Vec::new(),
inclusion_proof: Vec::new(),
};
return HandlerResult::ok(Bytes::from(proto.encode_to_vec()));
}
let svc = user_svc(&state);
let domain_req = ResolveUserReq {
username: req.username,
username: addr.username,
};
match svc.resolve_user(domain_req) {