Users can set a display name (shown instead of username in the message
list, room member list, TopBar, and admin Users tab) and upload a real
avatar, replacing the generated color-initial avatars everywhere a user
appears. Avatars are square-cropped and downscaled to 512px, reusing
app/storage.py's upload primitives from image uploads with a new square
option.
Two deliberate divergences from message-image handling, documented in
backend/README.md: the previous avatar file is deleted on replace/remove
(safe since it's strictly one file per user), and avatar serving is not
room-gated and uses a short cache (identity-addressed and mutable, unlike
a message image's permanent content-addressed URL).
Frontend: new ProfileModal reachable from the TopBar account menu;
AuthContext gains updateUser() so a profile change reflects instantly
everywhere without a refetch.
Invite-only FastAPI + SQLAlchemy(async) + Postgres backend (session-cookie
auth via CLI-provisioned accounts, open-room CRUD, single-instance /ws/chat)
and a React + Vite PWA frontend (login, room list, chat view). Backend tests
pass against a local Postgres DB. See README.md and backend/README.md for
setup, and ARCHITECTURE.md for the full phased design.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>