1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! # bevy_symbios_multiuser
//!
//! A decentralized, low-latency multiplayer plugin for the Bevy engine.
//! Combines [ATProto](https://atproto.com/) for federated identity with
//! WebRTC (via [Matchbox](https://github.com/johanhelsing/matchbox)) for
//! peer-to-peer data transfer.
//!
//! ## Architecture
//!
//! The crate provides a generic message bus via [`plugin::SymbiosMultiuserPlugin`]
//! that accepts any serializable domain type `T`. Messages are transported over
//! WebRTC data channels: one **reliable** channel for state mutations and one
//! **unreliable** channel for ephemeral presence data.
//!
//! Authentication flows through a **Sovereign Broker** pattern:
//!
//! 1. The client authenticates with an ATProto PDS via
//! [`auth::create_session`], obtaining an [`auth::AtprotoSession`]. For
//! relay authentication with `auth_required = true`, the client must then
//! call [`auth::get_service_auth`] to obtain a *service auth token* — a
//! JWT signed by the user's `#atproto` key that third-party relays can
//! verify via DID document resolution. The `access_jwt` from
//! `create_session` is signed by the PDS service key and cannot be
//! verified this way. Wrap the service token in a
//! [`signaller::TokenSourceRes`] resource so the signaller uses it on
//! each connection attempt.
//! 2. The [`signaller::SymbiosSignallerBuilder`] passes this token to the
//! relay during the WebSocket handshake. On native targets, the token is
//! sent as an `Authorization: Bearer` header. On WASM targets, the token
//! is sent via the `Sec-WebSocket-Protocol` subprotocol trick (the
//! browser `WebSocket` API does not support custom headers).
//! 3. The relay (`relay` module, feature-gated) validates the JWT claims and — when
//! `auth_required` is enabled — resolves the issuer's DID document to
//! cryptographically verify the signature (ES256/P-256 or ES256K/secp256k1)
//! against the `#atproto` signing key. When [`RelayConfig::service_did`] is
//! set, the `aud` claim is also validated to prevent cross-service token
//! replay. The authenticated DID becomes the peer's session identity.
//! 4. Once signaling completes, data flows directly peer-to-peer over
//! WebRTC data channels.
//!
//! ## Quick Start
//!
//! ```rust,no_run
//! use bevy::prelude::*;
//! use bevy_symbios_multiuser::prelude::*;
//! use serde::{Serialize, Deserialize};
//!
//! #[derive(Serialize, Deserialize, Debug, Clone)]
//! enum GameMessage {
//! Move { x: f32, y: f32 },
//! Chat(String),
//! }
//!
//! fn main() {
//! App::new()
//! .add_plugins(DefaultPlugins)
//! .add_plugins(SymbiosMultiuserPlugin::<GameMessage>::new(
//! "wss://relay.example.com/my_room",
//! ))
//! .add_systems(Update, (handle_incoming, send_movement))
//! .run();
//! }
//!
//! fn handle_incoming(mut queue: ResMut<NetworkQueue<GameMessage>>) {
//! for msg in queue.drain() {
//! info!("From {:?}: {:?}", msg.sender, msg.payload);
//! }
//! }
//!
//! fn send_movement(mut writer: MessageWriter<Broadcast<GameMessage>>) {
//! writer.write(Broadcast {
//! payload: GameMessage::Move { x: 1.0, y: 2.0 },
//! channel: ChannelKind::Unreliable,
//! });
//! }
//! ```
//!
//! ## Features
//!
//! - `client` (default) — ATProto authentication, custom signaller for
//! authenticated relay connections.
//! - `tls` (default) — Enables TLS (via `rustls`) for both `reqwest` HTTPS
//! and `async-tungstenite` WebSocket (`wss://`) connections.
//! - `relay` — Sovereign Broker relay server with DID-based JWT signature
//! verification (ES256 + ES256K), room-based peer isolation, atomic
//! connection limits, SSRF-hardened DID resolution, message size caps,
//! idle/handshake/write timeouts, HTTP-level Slowloris protection, server-side
//! pings (WASM keep-alive), per-sender token-bucket rate limiting,
//! per-target burst limiting, per-domain and global `did:web` fetch
//! concurrency limiting, request coalescing, negative DID caching, peer ID
//! length validation, unique target cap, and JWT audience validation
//! (`service_did`). Built on `axum`.
/// Re-exports for convenient use.