Build an anonymous chat app following google material UI design (called RChat):
- UI should unctionality-wise be like discord, but with only servers, direct messages, and channels.
- When a user wants to create their account, they should enter any username (space, special characters, anything should work). For passwords, they can either choose to enter a text-based password like normal (make sure there is no minimum passwords requirements, like "a" can be a valid password), or they can choose from an assortment (20) random (ensure a large list) words unique to each username and pick 7 in order which will serve as their password, and the same assortment will be given to them if they choose to login with a username that chose a word based password (should be done algorithmically) (for rate limiting here, make it so unlimited login attempts are allowed, but there is a 3 second gap between attempts. There is one exception, if the user has had over 1000 login attempts lock their account for the day). No profile pictures will be allowed, but in the account creation add an option for a github identicon like (find a library) profile picture, or a youtube like pfp where there is a standard "person" with a color background (let them choose with a color picker).
- In the main app, as soon as a user creates an account they are added the the main RChat server, which has only one general channel. Allow file uploads (image embeds too), plain text, and markdown rendering. IMPORTANT: Images/files/ALL media should have a max upload of 25MB, HOWEVER ALL media should be deleted exactly after 1 day of the post (replace the media message, though, with a message saying "File x was removed after 1 day of posting).
-
- Media should generate a unique URL that doesnt exist (reference DB) that downloads the file. The browser links to this URL when clicking on the file attachment for a message.
- Like discord's plus button UI on the sidebar (selecting either to make or join a server) and a server list on the left (including the RChat server), a user can create a server with a string name (any characters allowed), and others can join the server with that with an "invite" code as the same name (and on servers a sidebar on the right with all users should be present). The user who created the server is the "creator" (immutable status) and starts as a server admin, but creator status grants NO extra permissions. Server admins can add/delete/rename text channels, kick users, delete messages, and delete the server. By default, a server has a channel named "general" and it CANNOT be deleted unless there is another text channel (e.g. if there is only one text channel, it cannot be deleted).
-
- Creator vs Admin distinction: "Creator" is permanent and shown via a "Creator" badge in member list and server list. "Admin" is a role that can be granted/revoked. A creator starts with admin role but can lose it (via demotion or self-revoke). A creator without admin role has NO special permissions - cannot delete server, can only leave. Upon leaving, creator status persists; on rejoin, creator badge still displays. All database/frontend/variable references should use "creator" not "owner".
-
- Another feature: On the right sidebar for a server, it should showcase users that are online versus offline (ensure this is solely through the server, clients cannot and do not connect to eachother).
-
- Online status should be PER SERVER (if a user is viewing any channel in one server, they should be online in that server, and offline in all other servers).
- Add an option to the RChat website that allows users to view the default RChat server general chat without needing to login. On the login page add a “Skip to RChat → (cannot send messages)” button that achieves this functionality.
-
- They should also be able to join any server by name and view its channels, messages, and users, and because we cannot store the account in our database, the servers this user is on should be stored in the browser via localstorage.
- If a user right clicks on another user's username, they can choose to direct message them, with functionality like discord, creating a separately categorized server like instance, but with no channels directly to the user. Make this clear in several ways in the UI (e.g. a line separator on the left sidebar where DMs are at the top of the separator and servers are below.
-
- Important: If a server admin right clicks on another user on the member list in the right-hand pane, they should be able to grant them "admin" (server admin role). This shows an "Admin" badge on the member list. The "Creator" badge is separate and only shown for the original server creator.
-
- Users can delete their own messages, admins can delete ALL messages.
-
- When a user leaves a server, DO NOT delete their messages.
-
- There should be a "transfer admin" which removes the current user as server admin and grants server admin to the specified user, and "promote to admin" which simply adds that user as server admin alongside you. Note: "creator" status is immutable and separate from admin role - transferring admin does NOT change who created the server.
-
- Server admins and site admins can remove server admin role from ANY user including the server creator. The creator can also revoke their own admin. A creator without admin role is just a regular member with a "Creator" badge (no special permissions).
- For ALL user created content (usernames, messages, excluding passwords), implement or download a censoring library for profanity or adult content. Make sure this isn't implemented on the client, and instead the server API, and if the front end gets a blocked response, display it to the client. This should also be implemented purely locally.
- Make sure to implement proper ratelimiting, especially for sensitive routes.
- For database storage, use sqlite and a .db file that contains absolutely everything (other than the site config which should be in env vars), don't encrypt anything.
- Use WebRTC, and at the top right of the webpage display the local time (24 hour time in seconds), and a status light (green=up to date, yellow=degraded (like manual requests no websocket traffic), red=not getting real time/down/etc). Even things like the server list should be updated live with a websocket or channels etc.
- Do NOT use any external APIs, the website should work without access to external resources, maeaning once all the packages are installed etc, everything should work normally.
- For icons, use lucide icons or similar that you are familiar with.
- Make a readme describing this website, along with its odd choices that are not industry practice. These include things like the odd password and rate limiting rules, the anonymous style, the intentional non-encryption, etc.
-
- Important: Rate limiting and good security SHOULD exist, simply not for the user. (e.g. we have site-wide rate limiting for requests such that nobody can DOS the site, but not specific blocks to protect users like blocking login attempts or similar)
-
- The UI for uploading files should be seamless, you can click a plus button in the message bar, or drag and drop it, it should be added automatically as long as there are no issues with the file (only size limit). The file name should be shown above the message bar in a square box like many UIs. Also in the type messaging bar at the bottom, the right side should have a paper plane send icon as a button as opposed to pressing the enter key.
- A user should be able to DM themselves. This special dm (indicate in the UI nicely) is auto-created and cannot be closed. If a user tries to start a new DM putting their own username, it should redirect to this DM page.
- Usernames ARE user IDs, case insensitive.
-
- Servers should be case insensitive, and are also the server IDs as well as join code. Usernames are ALSO the user ID. (both lowercase)
- Site admins (the first account created) should have server admin permissions across the whole site (they can kick, delete messages, make other users server admins for a specified server, edit channels/rename for any server, delete ANY server via site admin panel, etc). However, they also have another capability of "banning" a user. This option is available on a right click, and will delete ALL messages site-wide, and the account of the selected user.
-
- SITE ban versus server ban
-
- Site bans should clear all messages, and add the username to a blacklist such that nobody can create an account with the same username
-
- Site admins must be COMPLETELY HIDDEN. No user should have any way of knowing who is a site admin. No badges, no indicators, nothing. Users can still grant site admins server admin role (which shows a normal "Admin" badge), and server members should be none the wiser that this user has site-wide powers.
-
-
- Thus, site admins can be banned from servers, but they can unban themselves via the site admin panel, or perform administration from there.
-
- Server admins should have a server admin panel that allows them to manage the server, currently with options to add/remove server admins (like the site admin panel), and rename the server.
-
- Site admins should NOT have the server admin panel, they should only have the site admin panel. However, they should have all the same options within the server management actions of the site admin panel.
- Profanity checks should not be applied in DMs, only servers.
- Tokens/logins shouldn't expire.
- Notification banners like discord
- Message threads
-
- Create a new section for users that have interacted (sent a message or joined a VC) in a server, but have since since on the right member panel list.
- Message search like discord with filtering
-
- Searching for user management and servers, all searches should be fuzzy
- Voice, video, screen share in the form of a channel on servers, and menu in DMs exactly like discord (server as a relay or P2P as described)
- Markdown formatting like discord, text/image censoring on click like discord as well (for files/images provide a new toggle on the file box when sending a message)
-
- If a call has only 1 user for 1 minute, end the call.
-
- Guests cannot join VC, prompt them to create an account
- Add a second file upload and voice/screenshare option that attempts P2P via peerjs. For voice/video, this should only work in 1 on 1 DMs where the other user also initiates a separately categorized P2P call, and the other user accepts the P2P call. Only one instance of a call of either type can be started, though. For files, in a server when a file is uploaded and the user (really, browser tab) is active "hosting" the file, another user may see the file and see that it is available for download, and download it. If the browser is not active, the file should still be present, but indicate seamlessly that it is not available for download. Thus, all the P2P type files a browser uploads should be kept in local storage for 1 day (just like the servers automatic file deletion), and anytime the browser tab comes online it will serve all its current P2P files. Important: For images, all uploaded P2P categorized images should be automatically downloaded and shown to other clients whenever the "hoster" peer is online. Important (CORRECTION, supersedes the 1-day rule for P2P): when P2P files are uploaded, the user is prompted to select or enter a custom expiration time covering all the P2P files in that message if multiple: indefinite, or a value in seconds, minutes, hours, days, etc. Local storage retention on the hoster follows the chosen expiration (indefinite means kept until manually removed); server-hosted uploads keep the 1-day rule. The server NEVER auto-deletes P2P messages or their attachment records; only manual deletion by the user removes them. Because the server holds no blob (null data), an unavailable P2P attachment (expired locally, or hoster offline) must cleanly indicate it is not available and suggest asking the uploader to send it again.
- Message attachments and embeds are individually deletable like discord: the message author (and anyone with message-delete permission) can remove a single attachment or embed via an X/remove option without deleting the message or its text. Removing a server-hosted attachment deletes its blob; removing a P2P attachment deletes its metadata record.
- URL embeds: a Rust service unfurls URLs found in messages into discord-like embed cards (fetch the page server-side; parse OpenGraph/twitter meta tags, title, description, site name, image/banner; cache results in the database; strict limits: http/https only, ports 80/443, short timeout, response size cap, redirect cap, and SSRF protection rejecting private/loopback/link-local addresses after DNS resolution). Embeds render under the message; the author (or message-delete permission holders) can remove a single embed, just the embed's image banner, or all embeds on a message. When the server has no internet access the fetch simply fails and no embed is produced; the site remains fully functional (this is the one permitted use of external network access, driven purely by user-posted URLs, never a required dependency).
- Super user permission grant in an admin UI, delete users (without deleting their server), delete servers, delete messages, just purely by name, etc.
- Add a dark mode site-wide enabled by default sun an moon icon/toggle in the solutely every component across the site and backgrounds work perfectly.
- Password protected servers. CLARIFICATION: guests can also view/join a password server if and only if they provide the correct password (implementation: the guest exchanges the password for a per-server access grant kept in localStorage, since guests have no account for the server to remember); without the password, password servers are closed to guests.
-
- Password-protected/invite only channels
- Env var enabling.disabling guests (also inherently closes off the entire site servers etc to be accounts only so no longer a readonly openly accessible site/db). CORRECTION/ADDITION: guest enabling/disabling must ALSO be toggleable live from the site admin panel (a fourth site settings switch alongside profanity/previews/uploads); the env var supplies the default/initial value.
- Streamer mode, hides servers, usernames, etc
-
- Proper settings menu like discord
- Support any db via env var connection string (postgres server, mongodb, sqlite via file, etc), a library should handle this
-
- Support generic S3 connection for file uploads (e.g. aws s3 or minio)
- Fall back with a degraded polling mode if websocket fails and indicate this (part of the original spec's green wss yellow polling red system)
- Identicons/solid color for servers
- Slow mode, fine grained permissions
-
- Add private channels and channel settings with an option to add users who are able to view the channel, send messages, read messages, versus see message history
-
- Specific server admin permissions (e.g. server deletion, message deletion, channel creation, channel renaming)
-
-
- User roles that also have permissions
-
- Add an option to view servers a user created in the options menu on the site administration panel for user management in actions
- Unit tests
- Paginate member list and server list (like discord, shows the first 50 and then an infinite scroll to the next 50 and so on)
- we should add a toggle to disable the profanity filter, AND we should have a toggle to disable asset previewing (e.g. show the download for an image, but not the image), AND we should have a toggle to disable asset uplods at all. This will be in the admin panel, all of these are disabled (e.g. profanity filter on, image preview on, image upload on) by default.
- ensure we integrate with openapi, and we enable a swagger UI for authenticated users Notes:
- No hardcoded word list, use a library or an API temporarily in the terminal to get a list of thousands of short words. The way the word login now is done not according to the spec it should be as follows: Each username is hashed somehow, each hash corresponds to one set of the word list, exactly. So, a one to one mapping of word lists to usernames. Thus, when typing in a username this algorithmic one-to-one word list is dynamically updated on the client.
- Mobile UI
Decisions (as built):
- Real-time transport is WebSocket throughout; WebRTC is used only for voice/video/screenshare media (mesh peers, server relays signaling over the existing WS). The status light reflects the WS: green live, yellow API-reachable polling fallback, red down.
- Databases: sqlite (default, single file) and postgres via DATABASE_URL through one sqlx AnyPool layer. MySQL and MongoDB are out of scope.
- Word passwords: sha256 of the lowercase username seeds ChaCha8 which samples 20 words from the memorable-wordlist crate; the password is the 7 picked words joined by spaces; verified like any password. Passwords are hashed with argon2 and per-user salts (hashing is not encryption; the no-encryption rule covers stored data).
- WS auth is a first message {"type":"auth","token"} after connect, never a URL query parameter. Tokens also ride an rchat_token cookie (SameSite=Strict) solely so logged-in browsers can open the authenticated Swagger UI at /api/swagger-ui.
- Site settings (admin panel, all default on): profanity_filter, asset_previews, asset_uploads, guests_enabled (seeded once from GUESTS_ENABLED). Flipping guests off closes every public route and live guest connections.
- Scale rules: no WS event may carry an unbounded user list. Presence is per-user deltas; member lists, admin lists, and the interacted list paginate 50 with online-first ordering.
- Permissions: five-bit mask (manage channels, delete messages, kick, delete server, manage admins). is_admin means all bits unless narrowed per-admin; roles union their bits; site admins hold all bits everywhere. Private channels are default-deny once any perm row exists; without read-history a member sees only messages after their join.
- One active or ringing call per user site-wide, of either kind (server or P2P). A voice room or call left with a single occupant for VOICE_IDLE_SECS (default 60) is ended by the server.
- URLs: the app lives at the root; channels are /s/{server}/{channelId}/, DMs /dm/{dmId}/; the s prefix exists because server names are arbitrary strings that could collide with reserved paths.
- Rate limits: per-IP 10 req/s burst 60 globally, 2 req/s burst 30 on auth and media routes; these protect the site, not the user (the 3s login gap and 1000/day lock are separate per-username rules).
- Sole-channel rule generalizes the spec: no server's last remaining channel can be deleted, general or not. The rchat home server cannot be renamed or deleted by anyone.
- Every icon-only control carries a hover tooltip. The site is an installable PWA (production-only service worker that never touches /api). CI enforces fmt, clippy -D warnings, eslint, prettier, and full builds, and publishes a nightly Docker image plus a moving nightly git tag.