//! Multithreaded's side of the `livechat` crate. //! //! The crate owns transport, fan-out, ordering and bounded retention, and knows //! nothing about communities, memberships or bans. Everything app-specific //! arrives through four traits, and this module is those four impls plus the //! process-wide state they need. Design and the decisions behind it: wiki //! `livechat-design`. //! //! //! //! | Trait | Here | Backed by | //! |---|---|---| //! | `ChatRooms` | [`rooms`] | `communities.chat_policy` + state | //! | `ChatAuthz` | [`authz`] | `evaluate_write_access`, memberships, session perks | //! | `ChatIdentity` | [`identity`] | the `users` row, behind a bounded cache | //! | `ChatModeration` | [`moderation`] | `community_bans` + `mod_log` | //! //! One room per community: `RoomId` is the community id, and there is no rooms //! table. Routes are not here yet. use livechat::{Chat, ChatError, HubLimits, RateLimits}; pub mod authz; pub mod identity; pub mod moderation; pub mod rooms; pub use identity::IdentityCache; /// Connection budget for this process. /// /// `MemoryMax=512M` is a hard cgroup cap on the unit and an OOM restarts the /// whole site, not just chat, so these are an availability control rather than /// tidiness. Each listener costs a task, a buffer and a broadcast receiver, and /// the crate refuses a connection over budget with a 503 rather than /// allocating and hoping. const HUB_LIMITS: HubLimits = HubLimits { // A handful of tabs. Not a spam control: without it one client can eat the // global budget on its own. max_connections_per_user: 4, // The number that keeps the box alive. Well under what the cap would bear // so chat cannot be the thing that OOMs a forum. max_connections_total: 1_500, // Messages buffered per room before a slow listener is told it fell behind // and re-fetches from its cursor. Deep enough to absorb a busy moment, // shallow enough that 1500 idle-ish connections are not carrying a large // backlog each. room_buffer: 256, }; /// Send budget per (user, room). /// /// Deliberately not `USER_POST_RATE_LIMIT` (15 per 60s), which governs forum /// posts and is far too tight for chat: it would throttle a person having a /// normal conversation. Chat gets its own budget because it is a different /// activity, not because the forum's number needs relaxing. const RATE_LIMITS: RateLimits = RateLimits { // Enough to paste a few lines in a row without being punished for it. burst: 10, // One every two seconds sustained. A person talking never meets this; a // script is held to something a room and a 512M box can absorb. sustain_per_min: 30, }; /// Build this process's chat state. pub fn new_chat() -> Chat { Chat::new(HUB_LIMITS, RATE_LIMITS) } /// Wrap a host-side failure for the crate. /// /// One function so every impl reports the same way, and so a query failure /// stays distinguishable from a user being refused: `ChatError::Host` is a /// fault to log, while a `DenyReason` is expected traffic. pub(crate) fn host_error(err: E) -> ChatError where E: std::error::Error + Send + Sync + 'static, { ChatError::host(err) }