DEV Community

Carsten VerHaagh
Carsten VerHaagh

Posted on AI-assisted

I Built a Phishing Trainer Solo in 24 Hours and Lost. Here’s What I Learned.

This is a submission for the MLH x DEV Writing Challenge

What I Built

I built Hooked, a game that teaches middle and high school students to spot phishing. Students sort 15 real-looking texts, DMs, and emails into Phish or Legit. After each answer, the red flags light up inside the message with a one-line explanation. Legit messages show why they're safe, so it doesn't just teach kids to distrust everything.

Teachers can run it as a class. They project a QR code, students join with just a nickname, and a live dashboard shows each student's first try next to their best try. There's also a password check that tells you if a password has shown up in a data breach without ever sending the password anywhere.

Why I built it: I'm an IT intern at a school district. The phishing reports I see are almost all aimed at staff. Students barely show up, but that doesn't mean they're safe. They get hit somewhere else: texts asking for a login code, "free gems" DMs, a friend's hacked account sending a link. So I built a trainer around the scams teens actually get.

I built it solo, using Claude and Claude Code as AI assistants. Claude Code wrote most of the code. I chose the idea and the stack, wrote and reviewed every message, tested on my phone, and made the calls when something didn't work.

What I learned along the way:

  • The password check uses k-anonymity. The browser hashes the password with SHA-1 and sends only the first 5 characters to the Have I Been Pwned API. It sends back about 2,000 hashes that start the same way, and the browser checks them locally. The service never knows which password you checked.
  • Classrooms break rate limits. My first rate limiter keyed on IP address. Then I realized a whole classroom shares one IP. Worse, behind Railway's proxy every request looked like it came from the same address. I moved limits to per-student and fixed which proxy hop the server trusts.
  • Race conditions are real at 60 students. My first class-size cap locked the class while counting. With 70 simultaneous joins, 50 got errors. One SQL statement that claims a seat and creates the player at the same time fixed it: exactly 60 in, 10 told the class is full.

Demo

Partner Technologies

GoDaddy Registry. I registered dontgethooked.study for the project. The .study extension fit an education tool, and it read as more trustworthy than a generic cheap domain. That matters when your app teaches kids to read web addresses carefully.

Setup taught me something about DNS. Railway gave me a CNAME record for the root domain, but a CNAME can't live at the root next to the NS and SOA records. I moved the app to www, pointed that CNAME at Railway, added Railway's TXT verification record, and forwarded the bare domain to www.

Then the real lesson: the campus wifi blocked my domain because it was brand new. School web filters block newly registered domains because scam sites are usually only days old. It's a real phishing defense, and I'd built an app about phishing. I ended up demoing on the Railway URL so judges on campus wifi could reach it. Next time, I'd register the domain right at kickoff so it has time to age, and test it on the venue network early.

GitHub. The project lives in a public repo, and I used it to keep work organized while I ran more than one Claude Code session at once. Each session got its own git worktree on its own branch, so they never edited the same files. That let one session audit the code while another built a feature.

Hackathon Experience

I attended HackGB 2026, the first 24-hour hackathon at UW-Green Bay, held at the Brown County STEM Innovation Center. The overnight vibe was awesome. All of us hackers in the same building working on our projects with no sign of sleep incomming.

I entered solo. I met many other hackers with ambitious projects, unfortunately I didn't get to work with them because I went in wanting to go solo. However, next time I am definitely joining a team.

I didn't win a track, and digging into why taught me more than winning would have. Out of 23 projects, at least four were security-awareness trainers, including mine. The winners each had a moment that made judges lean in: drawing in the air with a webcam, a tool that finds the first wrong line in your handwritten calculus, live 1v1 coding duels.

I spent my overnight hours on things judges never saw: a security audit, edge-case fixes, accessibility checks. They made the app better. They didn't make it memorable. The rubric gave 20% to "innovation" and explicitly asked for an "aha moment," and I didn't read it until after judging.

What I'll take to my next hackathon:

  1. Read the judging rubric before picking an idea. Write the "aha" in one sentence before writing any code.
  2. Check what other teams are building in the first few hours, and change your angle if you're one of four.
  3. Build the surprising feature first. Polish comes after.
  4. Sleep. I got about two hours, and my Q&A answers showed it.

What I'll remember most is the 3am slog of wanting to fall asleep but still needing to code.

I spent my overnight hours on things judges never saw: a security audit, edge-case fixes, accessibility checks. They made the app better. They didn't make it memorable. The rubric gave 20% to "innovation" and explicitly asked for an "aha moment," and I didn't read it until after judging.

What I'll take to my next hackathon:

  1. Read the judging rubric before picking an idea. Write the "aha" in one sentence before writing any code.
  2. Check what other teams are building in the first few hours, and change your angle if you're one of four.
  3. Build the surprising feature first. Polish comes after.
  4. Sleep. I got about two hours, and my Q&A answers showed it.

What I'll remember most is [your answer: a moment, a person, the 3 AM feeling, scanning the QR at the expo, anything real].

Top comments (0)