Strife
Discord game platform: threaded matches, plugin games, Postgres history, crash-safe live resume. PlayCord rewrite.
Built
Slash lobbies, matches in threads, and a plugin API so the same play() runs live, in replay, and on a CLI. Postgres 16 holds guilds, matches, the move log, and live-match integrity. Games are in-process plugins — fault isolation, not a security sandbox. Builtins: Tic-Tac-Toe, Connect Four, Chess (incl. Chess960), Coup, Mafia, Spyfall, Liar's Dice.
Hard parts
- Live matches resume after a restart by replaying the move log; abandon if the plugin build changed.
- Lobby/thread timeouts and signed msgpack
custom_idrouting were the long tail. - Hang caps and a thread pool keep a stuck
play()off the event loop. - Public invite exists; the bot is not hosted yet.
Learned
A game host is a durability problem: deterministic simulation, an append-only log, and 'live' as a first-class match state.
Also
- Seven builtin games plus git-installable plugins
- Postgres migrations through live-match integrity constraints
- Replay / CLI / live share one play() implementation
- Docker Compose self-host; ~27k LOC in the bot package
A game host, not a chess bot
Strife is a rewrite of PlayCord: slash lobbies, matches in threads, and a plugin API so the same play() runs live, in replay, and on a CLI. Games are in-process plugins — fault isolation, not a security sandbox. I keep saying that because “sandbox” is the word people want and not the thing I built.
Builtins: Tic-Tac-Toe, Connect Four, Chess (including Chess960), Coup, Mafia, Spyfall, Liar's Dice. Plugins can be git-installed. Public invite exists; the bot is not hosted yet. Docker Compose is how you run it.
Durability is the product
Postgres 16 holds guilds, matches, the move log, and live-match integrity. A restart resumes a live match by replaying the log. If the plugin build changed, it abandons instead of lying. That is the whole trick: deterministic simulation plus an append-only log, with “live” as a first-class match state.
The long tail was not the games. It was lobby/thread timeouts, signed msgpack custom_id routing, hang caps, and a thread pool so a stuck play() stays off the event loop. About 27k LOC in the bot package, most of it host machinery.
What I would not hide in a cog
Piranha was the earlier chess bot: one file, Stockfish, a PNG board server. Fine for a weekend. Useless as a platform. The moment you want Coup and Chess to share resume, you need a host, not another cog.
I would still not call the plugin loader a sandbox. Process isolation is a different project. This one is honest about the boundary it actually has.