multithreaded: implement the four livechat traits
MT's side of the crate seam: ChatRooms, ChatAuthz, ChatIdentity and
ChatModeration, plus the process state they hang off. Still no routes.
ChatAuthz is the one that mattered. src/routes/scope.rs records the C1
chronic (three ultra-fuzz runs of handlers evaluating predicates against
a community the slug did not name), and chat is a new surface on those
same URLs. Two structural properties keep it from being the fourth
instance. A gate holds the Room resolved from the slug, every predicate
reads room.id, and no method takes a community id, so the divergence
cannot be expressed. And good standing is now evaluate_write_access, one
function that check_write_access renders as a 403 and chat renders as a
DenyReason: two renderings, one predicate set, so a check added for the
forum reaches chat and the two cannot disagree about who is muted.
The gate is per-request because fan_plus reads UserPerks, which lives on
the session rather than in the database. It refuses outright when asked
about a user it was not built for, rather than answering with the wrong
person's perks.
Chat moderation reuses the existing stack rather than adding one. A
timeout is a mute with an expires_at, a ban is the same community_bans
row a forum ban writes, and every action lands in mod_log on the same
transaction as the mutation it records, so a purge cannot land without
its audit row. Four ModAction variants added; the message id travels in
the reason text because target_id is a UUID column and a chat message id
is a BIGINT.
Room state folds policy, community state and suspension fail-closed:
off and suspended close the room, Frozen and Archived go read-only via
allows_writes_for_members() rather than a variant match, so a new
CommunityState gets the same answer chat and the forum both give.
Identity is cached with a TTL and a hard cap, invalidated on rename.
SessionUser carries no avatar_url, and the uncached shape is one query
per author per reconnecting client at the exact moment every client in
the room reconnects at once.
Hub and rate limits are stated explicitly against the 512M cgroup cap
rather than inherited from the crate defaults, and chat gets its own send
budget: USER_POST_RATE_LIMIT (15/60s) governs forum posts and would
throttle a normal conversation.
25 trait tests including the full four-policy read/write matrix; 336
integration and 192 unit tests pass, clippy and fmt clean.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-08 18:52 UTC