# Social — Friends, Chat & Groups — Plan > **Status:** Planned > **Owner:** Platform team > **Feature ID:** `social` > **Godot version:** 4.6.2 stable > **Backend:** FastAPI + SQLAlchemy (async) + PostgreSQL + Redis ## 1. Overview This document breaks the **Social** feature into user stories and right-sized implementation tasks. The feature delivers: - **Friends & following** — a friend request/accept model plus a one-way follow relationship, with block lists. - **Presence & status** — online/offline, custom status message, last-seen. - **Chat** — direct messages (DM) and in-experience (world/room) chat with **first-class safety**: server-side text filtering, report-message, and rate limiting. - **Groups** — named groups with roles (owner/admin/member) and a simple group page. - **Public profiles** — avatar, bio, favorite experiences, and a friends list that respects privacy settings. ## 2. Why chat safety is the differentiator The platform is explicitly meant to pull adults away from a youth-oriented competitor. Chat is the highest-risk surface: it is real-time, user-generated, and where abuse happens fastest. Safety is therefore treated as a **blocking requirement**, not an add-on: - Every message passes a **server-side text filter** before it is stored or delivered. The client filter is cosmetic only; the server is authoritative. - Every message can be **reported**; reports flow into a moderation queue with actor + reason + audit trail (mirroring `content_moderation.md`). - Sending is **rate limited** per user (Redis token bucket) to stop flooding and harassment-by-volume. - Report and block are always reachable from a message context in the UI. ## 3. Existing constraints (do not re-build) - **Auth:** JWT bearer tokens; `get_current_user` / `CurrentUser` dependency in `app/dependencies.py`. All social endpoints reuse this. - **DB:** async SQLAlchemy + Alembic; migration `0001_initial` is the head. - **Redis:** configured (`REDIS_URL`) but unused so far; adopted here for presence and rate limiting. - **Models:** `User`, `World` (owner_id), `Asset` (owner_id, is_public). Avatars and favorite-experience references point at existing rows. - **Client:** Godot 4.6.2, minimal `main_menu.gd`; no social UI yet. ## 4. Key design decisions | Concern | Decision | |---|---| | Message transport | REST for send/history; **WebSocket** gateway for real-time delivery + presence (matches `networking.md`'s "WebSocket as secondary transport" for chat) | | Presence store | Redis (TTL heartbeat) + persisted `last_seen_at` on the user record | | Text filtering | Pluggable `TextFilter` interface with a deterministic default (profanity list + normalization) so it runs without external services; third-party hook later | | Rate limiting | Redis token-bucket dependency, reusable across all social endpoints | | Reports | `message_reports` table + moderation endpoints (list/review/resolve) with audit fields | | Friends vs. follow | Friends = mutual (request→accept). Follow = one-way (no approval). Block = hard deny that also hides content and prevents DMs | | Privacy | Per-user settings JSON (`profile_visibility`, `friends_list_visibility`, `favorites_visibility`) enforced server-side on every read | | Groups | `groups` + `group_members` (role: owner/admin/member) + group page aggregate endpoint | | Real-time | One WebSocket endpoint multiplexing presence, DM, and world-chat messages via Redis pub/sub | ## 5. User stories ### US-1 Friends & following As a player, I can send a friend request, accept/decline incoming requests, unfriend, follow/unfollow other players, and block users so they can't interact with me. **Acceptance criteria** - Friend requests are one-directional until accepted; accepted friendships are mutual. - Follow is one-way and requires no approval. - Block prevents DMs, hides the blocked user's profile/friends interactions, and removes any existing friendship/follow between the two. - Duplicate/self requests are rejected idempotently. ### US-2 Presence & status As a player, I can see whether my friends are online, their custom status, and when they were last seen. **Acceptance criteria** - Online/offline is derived from a server-side heartbeat (Redis TTL), not client claims. - `last_seen_at` persists and is visible (subject to privacy settings). - Custom status text is optional and length-limited. ### US-3 Public profiles As a player, I can view a public profile page showing avatar, bio, favorite experiences, and a friends list that respects the owner's privacy settings. **Acceptance criteria** - Avatar and bio are editable by the owner only. - Favorite experiences reference existing `World` rows. - A viewer sees only the sections the owner's privacy settings allow; blocked users see a restricted view. ### US-4 Chat (DM + in-experience) with safety As a player, I can send direct messages and chat inside a world/room. Messages are filtered, rate limited, and reportable. **Acceptance criteria** - Server filters message text before storing/delivering; filtered content is either rejected (blocking) or redacted (configurable per filter policy). - Send rate is limited per user (token bucket); excess sends get `429`. - Every message has a stable id, author, timestamp, and conversation/world scope. - Any message can be reported with a reason; reports are stored and reviewable. - Real-time delivery over WebSocket with REST fallback for history. ### US-5 Groups As a player, I can create a group, join/leave groups, and (as owner/admin) manage roles and view a simple group page. **Acceptance criteria** - Each group has exactly one owner and any number of admins/members. - Role transitions are authorized (only owner can transfer ownership or promote/demote admins). - Group page lists name, description, member list with roles, and member count. ### US-6 Godot client As a player, I can access friends, presence, chat, profiles, and groups from the game UI. **Acceptance criteria** - An API client handles JWT auth, HTTP, and WebSocket for all social calls. - Dedicated screens/panels: friends+presence, DM & world chat (with report), profile page, group page. - Client-side filtering is cosmetic; server responses remain authoritative. ## 6. Task breakdown Right-sized to 1–3 files / one coherent piece of functionality each. Task IDs are sequential for dependency chaining. | # | Task | Story | Depends on | |---|------|-------|-----------| | 1 | Friends/follow/block models + Alembic migration | US-1 | — | | 2 | Friends & follow API router | US-1 | 1 | | 3 | Presence & status model + endpoints (Redis heartbeat) | US-2 | — | | 4 | Profile model + migration + own-profile endpoints | US-3 | 1 | | 5 | Public profile view + favorites + privacy enforcement | US-3 | 2, 4 | | 6 | Chat data models + migration (conversations, messages, reports) | US-4 | — | | 7 | Text filtering service + profanity config | US-4 | — | | 8 | Redis rate-limiting dependency | US-4 | — | | 9 | DM chat send/list endpoints (filter + rate limit) | US-4 | 6, 7, 8 | | 10 | Report-message endpoint + moderation queue | US-4 | 6, 9 | | 11 | In-experience (world/room) chat endpoints | US-4 | 9 | | 12 | Real-time WebSocket gateway (presence + DM + world chat) | US-4 | 3, 9, 11 | | 13 | Group + membership models + migration | US-5 | — | | 14 | Groups API (CRUD, join/leave, roles, group page) | US-5 | 13 | | 15 | Godot API client (auth + HTTP + WebSocket) | US-6 | 2 | | 16 | Godot friends & presence UI | US-6 | 3, 15 | | 17 | Godot chat UI (DM + world chat + report) | US-6 | 12, 15 | | 18 | Godot profile page UI | US-6 | 5, 15 | | 19 | Godot groups UI + group page | US-6 | 14, 15 | ## 7. Phasing - **Phase 1 (data + core APIs):** tasks 1–5 (friends, presence, profiles). - **Phase 2 (chat safety — the differentiator):** tasks 6–12 (chat + safety + realtime). This is the critical path and highest priority. - **Phase 3 (groups):** tasks 13–14. - **Phase 4 (client):** tasks 15–19, each landing after its backend dependency. ## 8. Out of scope for this feature - Age verification / maturity gating (covered by `content_moderation.md`). - Voice chat, media/image messages, message editing/deletion history. - Third-party profanity/toxicity vendors (the `TextFilter` interface leaves a hook, but the default is deterministic and dependency-free). - Mobile push notifications.