~/work/steward
Steward
Steward runs game nights from Discord. One slash command launches a match and every player gets a private seat link. Results post back to the channel and count toward server stats.
GAMES ▪ ONE SLASH COMMAND EACH2 TITLES
- ▣ MERIDIAN ▸
The Catan base game with original names and art, on a 3D board in the browser. Up to four players. The server owns the rules, so nobody can cheat.
- ▣ WORDY CHAMPIONS ▸
Wordle as a race for two to eight players. Same word each round, six guesses, three minutes. Fewest guesses wins the round and points add up over six.
01 ▪ THE PROBLEM
Getting a group into an online game from Discord is usually a copy-pasted link and a lot of "who's in?" Steward is a Discord game-platform bot: a library of games launchable with one slash command, personal seat links so every player lands in the right chair, and per-server stats and leaderboards once the results come back.
02 ▪ ARCHITECTURE
One Node process: the discord.js gateway bot plus a small HTTP server for result webhooks, over Postgres with Drizzle. The game registry is a static config module rather than a table, because a new game is a code change anyway.
The core is a documented game-adapter contract. Steward launches a match with a bearer token and a per-match callback. The match embed carries a Join button that mints a seat token bound to the clicker's Discord ID and replies with their private link. When the game ends it posts a signed result webhook, Steward credits the seats, posts the result embed, and updates stats. Meridian was the first game to implement the contract, and any game that does can join the library.
03 ▪ DECISIONS
Identity binds at join. The host's seat is minted with the launch; everyone else's when they click Join, so late joiners work naturally and there is no second sync call between Steward and the game. Bare-code joins still play, they just earn no stats.
Idempotent results. Seats upsert by token, and the first transition to completed wins. A retried webhook can't double-credit anyone.
Disposable process. All state lives in Postgres, so the bot can be restarted or redeployed at any time. Slash commands register themselves on startup.
Tests run without Discord or a database server. Database tests run on in-process Postgres via PGlite and Discord interactions are mocked, so the full suite runs on any machine in seconds.