//! Live chat shared by Multithreaded forums and MNW stream pages. //! //! Design + decisions: maintainer wiki. //! //! //! A thin crate. It owns the message and event vocabulary, bounded retention, //! and (as they land) the hub and SSE transport. It owns no schema, no authz, and //! no moderation policy: those arrive through four traits the host implements, so //! Multithreaded can gate on `CommunityScope` and the MNW server on //! `SubscriptionGate` without either concept appearing here. //! //! # Shape //! //! SSE downstream, plain `POST` upstream. Not a WebSocket. The upstream volume is //! one request per message typed, so bidirectionality buys nothing, and a `POST` //! passes through the CSRF, rate-limit, and session middleware both apps already //! run. It also avoids adding a `connect-src` CSP directive on the Multithreaded //! side, which currently has none. //! //! # Ephemeral, but written down //! //! Chat in both consumers expires. Messages are stored anyway, for two reasons: //! a deploy is a symlink swap plus a restart with no connection draining, so //! every client reconnects and needs its backlog replayed from a cursor; and //! moderation has to be able to reach a message that already scrolled past. //! //! [`Retention`] makes the bound un-opt-out-able. There is no `Option`, no //! "forever" sentinel, and no public field: the only constructor rejects anything //! over the crate ceilings. //! //! # Not here, on purpose //! //! No presence, no typing indicators, no edit path, and no link previews. The //! first two are the largest drivers of event volume against a hard 512M cgroup //! cap where an OOM restarts the whole site. Edit is surface with no benefit in a //! stream nobody re-reads. Link previews would point a server-side fetcher at //! unreviewed user input at chat volume, which is a much hotter SSRF surface than //! the forum-post path it would borrow. mod chat; mod error; mod event; mod hub; mod ids; mod message; mod rate_limit; mod retention; mod room; mod send; #[cfg(feature = "axum")] mod sse; mod stream; mod traits; pub use chat::{Chat, SendRequest}; pub use error::{ChatError, LimitScope}; pub use event::ChatEvent; pub use hub::{Hub, HubLimits, Subscription}; pub use ids::{MessageId, Nonce, RoomId, UserId}; pub use message::{MAX_MESSAGE_LEN, Message, MessageBody}; pub use rate_limit::{RateDecision, RateLimiter, RateLimits}; pub use retention::{MAX_AGE_CEILING, MAX_MESSAGES_CEILING, Retention}; pub use room::{Room, RoomState}; pub use send::SendRejection; #[cfg(feature = "axum")] pub use sse::{KEEPALIVE_INTERVAL, sse_response}; pub use stream::ChatStream; pub use traits::{ ChatAuthz, ChatIdentity, ChatModeration, ChatRooms, DenyReason, Identity, WriteAccess, };