- HTML 71.8%
- Rust 12%
- JavaScript 11.1%
- CSS 4.9%
- Nix 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
|
||
| src | ||
| static | ||
| templates | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| flake.lock | ||
| flake.nix | ||
| README.md | ||
todays-meet
A local, nostalgic recreation of TodaysMeet, the backchannel chat site
many of us used in school. Visit /anything and that slug becomes a chat room. Built in Rust from the 2012 pages saved
by the Wayback Machine. The original HTML, CSS, JavaScript and images are reused as-is, and only the server is new.
This is a fan project and has no connection to the original service or its creators.
Run it
nix develop # add --extra-experimental-features 'nix-command flakes' if needed
cargo run
Open http://localhost:3000. Without Nix, any recent Rust toolchain works: cargo run. Run it from the repo root,
because static files are read from ./static.
What it does
- Rooms by slug: type a name on the home page (or go to
/my-room) to create a room. Names are 1 to 127 characters: letters, digits,-and_. - The room setting: how long until the room is deleted (2 hours, 8 hours, 12 hours, one day, one week or one month).
- Chat: pick a name, then post messages of up to 140 characters. The page polls for new ones every 15 seconds.
- Extra pages:
/room/read(projector view),/room/transcript,/help,/about,/privacy,/terms.
Rooms live in memory and vanish when the server stops.
Japanese version
Every page has a full Japanese translation in templates/ja/ (UI strings for the scripts live in static/javascript/ja.js). It is served when the CF-IPCountry header says JP (set by Cloudflare) or the browser's Accept-Language starts with ja; everyone else gets English.
Safe for students
- Profanity and link filter (
src/filter.rs) on messages, names and room names. It catches leetspeak (sh!t), stretched letters, spaced-out letters (f u c k,h t t p s) and spelled-out domains (example dot com) without flagging innocent words like "Scunthorpe". The word lists are starters. Extend the consts at the top of the file. It is a policy filter, not a security boundary, so determined users can get around it. - No clickable links: the original client turns
http://...text into links. The filter blocks every form of that text, so a link can't appear in chat.
Security and memory safety
- Memory-safe code: Rust with
#![forbid(unsafe_code)]. - Bounded memory: 500 rooms, 2000 messages per room, 32-character names, 140-character messages and a 4 KiB request body. Beyond those limits requests are refused (503, 429 or 413).
- Capacity ceiling: the caps above bound memory at roughly 1.1 GB in the very worst case (1,000,000 messages, every
character one that gets escaped to
") and about 200 MB with ordinary short messages. See "How many users" below. - Everything the visitor types is escaped on the server before it is stored. Room names are restricted to a safe character set.
- Resilience: a panicking request can't lock everyone out of the site (poisoned locks are recovered), requests time out after 10 seconds, and expired rooms are purged.
- Headers: a Content-Security-Policy that blocks framing and any off-site script, image or connection, plus
nosniff,Referrer-PolicyandX-Frame-Options. The old client needs inline script andeval, so the policy can't be stricter. - Local only: it listens on
127.0.0.1. There is no login, no TLS and no per-visitor rate limiting. If you expose it beyond your machine, put it behind a reverse proxy that adds TLS and rate limits.
cargo test runs the filter checks.
How many users?
Estimates, from the caps in the code plus a quick wrk load test of a release build on a 16-core machine over
loopback (no TLS, no real network):
| Question | Answer |
|---|---|
| Simultaneous rooms | 500 (hard cap) |
| Messages held at once | 1,000,000 (500 rooms x 2000) |
| Viewers per room | no cap |
| Realistic classroom load | about 15,000 students at once (500 rooms x ~30) |
| Polling capacity | ~335,000 poll requests/second measured. At one poll per viewer every 15 s, that is CPU for millions of viewers |
| Opening a full room | ~550 page loads/second of a 146 KB full room (worst case ~2 MB each, so ~50/s) |
| Memory | 5 MB with one full room of short messages; bounded by the caps otherwise |
CPU is not the limit. The real ceilings are:
- Open connections. Each open browser tab holds a connection, so the practical limit is your open-file limit
(
ulimit -n; the default on many systems is 1024, which means roughly 1,000 simultaneous viewers). Raise it to go higher. - Network bandwidth and TLS. Not measured here. The numbers above are loopback only.
- The room cap. 500 rooms is a deliberate safety limit. Raise
MAX_ROOMSinsrc/main.rsif you need more, and memory grows with it.
A single instance on one machine comfortably covers a school. Rooms are in memory, so there is nothing to scale across machines.
Layout
src/main.rs routes, in-memory rooms, limits
src/filter.rs profanity + link filter and its tests
templates/ archived pages with {{room}} / {{id}} placeholders
static/ archived CSS, JS and images
Credits
TodaysMeet was created by James Socol in 2008. The pages and assets here come from the Internet Archive's Wayback Machine. The archived Privacy and Terms texts were based on Automattic's, under Creative Commons Sharealike.