I wanted it to be low friction, so there are no accounts. Your identity is an anonymous id in localStorage, and you can move it to another device by scanning a QR code or hitting a unique url. Start a game and text someone the link (game slugs are semi-memorable three word phrases), or "Play the computer" and you can play immediately against bots. Games are async-friendly with web push for turn notifications.
The stack is one Node process on a small VM, Next.js + Socket.IO, no database. The server and the client use the same engine so moves validate optimistically, and the bots go through the same functions, so bot moves are always legal. State is in memory and snapshots to a JSON file, so deploys don't kill games (I didn't have this initially, and was about one more deploy from having no more beta testers after the third game reset). No analytics/tracking/monetization now or planned - if it costs too much, I'll just shut down my server.
Disclosure: this was built almost entirely with Claude Code. I directed and reviewed; it wrote nearly all the code. Happy to talk about that workflow if folks are interested. Not really a proud coding project, but very fun for going from 0-playable in a day (plus two weeks of tweaks).
Known gaps: no turn timers for abandoned games yet, and push delivery is best-effort (Android battery optimization wins sometimes).