Secret Santa
Updated October 2026: Secret Santa now lives at santa.reb.run, with an EnoughUI interface and a Cloudflare Worker backed by KV for new games. The AWS deployment below describes the earlier version.
Every year, organizing Secret Santa is the same hassle. Drawing names, making sure couples don't get each other, sending individual messages. I went looking for tools to automate it and found a graveyard of ad-ridden websites that looked like they hadn't been updated since 2005. The ones that weren't ugly charged money for basic features like exclusions, or required everyone to create accounts. I just wanted to set up a game, add some constraints, share links, and be done with it.
JWTs
The first version was a pure SPA. All the data lived in JWTs embedded in URLs: the organizer would set up participants and constraints, the app would generate assignments, and each person would get a unique link with their assignment encoded in the token.
It worked for small groups, but the tokens got long fast. The more participants and exclusions you added, the longer the URLs grew. My extended family's exchange produced URLs that would wrap in messaging apps, get truncated, or just look suspicious enough that people wouldn't click them. The tokens also contained all the assignment data, so if someone shared their link publicly, the whole game's structure was visible to anyone who bothered to decode it.
The second version added a backend. A Lambda function with DynamoDB stored the game data. It introduced short nanoid identifiers in URLs and the ability to regenerate links if someone lost theirs.
Constraints
The interesting problem is constraint satisfaction. People specify who can't give to whom, typically partners or family members. The algorithm needs to find a valid assignment that respects all constraints while ensuring everyone gives to exactly one person and receives from exactly one person.
The AI implemented a randomized backtracking algorithm. It shuffles participants, attempts assignments, and backtracks when it hits a dead end. For typical group sizes it finds a solution instantly. I haven't tested what happens with adversarial constraint sets, but I suspect it would just fail gracefully.
The monorepo
This came together quickly because my monorepo already has shared configs and deployment patterns. I've been building it out over time, and at this point spinning up a new mini-project doesn't require much setup. The AI follows the patterns already in the codebase, so I describe what I want and review what it produces.
The app also supports English and Spanish via i18next. Setting up i18n is tedious but it's a solved problem; the AI just did it.
That version deployed via Pulumi. The Lambda published as a function URL, and the frontend went to Amplify. The current version is at santa.reb.run for your next gift exchange.