a clone of the old todays meet site
  • HTML 71.8%
  • Rust 12%
  • JavaScript 11.1%
  • CSS 4.9%
  • Nix 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-25 01:12:09 +09:00
src Add Japanese translation served to visitors in Japan 2026-10-25 01:12:09 +09:00
static Add Japanese translation served to visitors in Japan 2026-10-25 01:12:09 +09:00
templates Add Japanese translation served to visitors in Japan 2026-10-25 01:12:09 +09:00
.gitignore Local TodaysMeet clone in Rust, rebuilt from Wayback Machine snapshots 2026-10-24 23:32:43 +09:00
Cargo.lock Local TodaysMeet clone in Rust, rebuilt from Wayback Machine snapshots 2026-10-24 23:32:43 +09:00
Cargo.toml Add README; harden limits, headers and lock handling 2026-10-24 23:39:45 +09:00
flake.lock Local TodaysMeet clone in Rust, rebuilt from Wayback Machine snapshots 2026-10-24 23:32:43 +09:00
flake.nix Local TodaysMeet clone in Rust, rebuilt from Wayback Machine snapshots 2026-10-24 23:32:43 +09:00
README.md Add Japanese translation served to visitors in Japan 2026-10-25 01:12:09 +09:00

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-Policy and X-Frame-Options. The old client needs inline script and eval, 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:

  1. 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.
  2. Network bandwidth and TLS. Not measured here. The numbers above are loopback only.
  3. The room cap. 500 rooms is a deliberate safety limit. Raise MAX_ROOMS in src/main.rs if 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.