
Hackuten
A CTF platform for learning cybersecurity
Hackuten is a capture-the-flag platform covering several areas of cybersecurity — web, reverse engineering, cryptography, forensics and more. It exists to make the first steps into the field less discouraging: challenges that teach something specific, in an order that makes sense, with infrastructure that does not get in the way.
- My role
- Founder — design, development and infrastructure
- Built with
- Next.js
- TypeScript
- TailwindCSS
- PostgreSQL
- Docker
- Linux
- REST API

Vision
Why this exists
Most people who want to learn offensive security hit the same wall. The free resources are scattered, the paid ones assume you already know the vocabulary, and the challenge platforms throw you at a box with no indication of what you were supposed to have learned. You end up copying a walkthrough, flag validated, nothing retained.
Hackuten is built on the opposite bet: a challenge should teach exactly one thing, and you should know what that thing was by the time you solve it. Categories cover web, reverse engineering, cryptography, forensics, network and system, so someone can find where they fit before committing to a specialism.
The other half of the project is the infrastructure. Isolated, disposable environments per challenge, spun up fast and torn down cleanly. That part is invisible when it works, and the entire product when it does not.
Objectives
What it has to get right
Constraints stated up front, so the design decisions further down have something to be measured against.
Lower the barrier to the first flag
A beginner should reach a working solve in their first session — with a real understanding of why it worked, not a copied payload.
Cover breadth before depth
Several domains side by side so people can discover what they actually enjoy instead of defaulting to whatever the first tutorial covered.
Make the infrastructure boring
Challenge environments that start quickly, stay isolated from each other, and never leak state between players.
Keep it sustainable
Run costs that a student can carry, with sponsors supporting the platform rather than advertising inside it.
Interface
What it looks like
/projects/hackuten/challenges.png
/projects/hackuten/challenge.png
/projects/hackuten/scoreboard.png
/projects/hackuten/profile.png
Features
What it does
Multi-domain challenges
Web, reverse, crypto, forensics, network and system tracks, each with its own progression and its own difficulty ladder.
Isolated challenge instances
Containerised environments per player where the challenge requires one, so exploitation never touches shared state.
Flag validation and scoring
Server-side validation with per-challenge points, first-blood tracking and a scoreboard that recomputes without a full table scan.
Guided hints
Optional, progressively revealed hints that cost points — designed to unblock without handing over the answer.
Player profiles
Solve history, category breakdown and progress over time, so people can see the shape of what they know.
Author tooling
A defined format for contributing a challenge: metadata, files, Docker definition and flag handling in one place.
Architecture
How it is put together
Hackuten is a fairly ordinary web application wrapped around a decidedly unordinary requirement: it runs code written to be attacked. Almost every architectural decision follows from keeping that blast radius small.
Web application
Server-rendered pages for anything public and cacheable, client components only where interaction demands it. Session handling, scoring and flag checks never leave the server.
- Next.js
- TypeScript
- TailwindCSS
API layer
A REST surface for submissions, instance lifecycle and profile data. Rate limited per account and per IP, because a flag endpoint is a brute-force target by construction.
- REST API
- Zod validation
- Rate limiting
Data
Relational storage for users, challenges, solves and scoring events. Solves are append-only events rather than mutable counters, so the scoreboard can always be rebuilt from history.
- PostgreSQL
- Migrations
Challenge runtime
Per-challenge containers with no outbound network unless the challenge needs it, strict resource limits and a hard TTL. Every instance is disposable by design.
- Docker
- Linux
- Reverse proxy
Decisions worth defending
Flags are validated server-side, always
The client never learns a flag, not even hashed. Comparison is constant-time. It sounds obvious until you look at how many small CTF platforms ship the check to the browser.
Solves are events, not counters
Storing an immutable solve event per player-challenge pair means the scoreboard is derived state. Recomputation is cheap, and a scoring bug is fixable retroactively instead of permanently baked into a counter.
One container per instance, no sharing
Shared challenge instances are the single biggest source of 'someone broke it for everyone' in community CTFs. Isolation costs more resources and removes an entire category of support tickets.
Content lives outside the code
Challenges are described declaratively, so adding one is a content change. That is what makes external contribution realistic.
Challenges
The parts that were actually hard
Problem, what I did about it, and where that landed. Including the ones still open.
Problem
Players run untrusted input against services that are deliberately vulnerable — and some of them will try to escape the challenge and reach the platform itself.
Approach
Treat every challenge container as already compromised. No credentials in the environment, no outbound network by default, non-root execution, dropped capabilities, read-only filesystems where the challenge allows it, and hard CPU/memory ceilings.
Outcome
A successful escape from a challenge lands somewhere with nothing worth having and no route back to the application tier.
Problem
The scoreboard has to stay correct and fast during the exact window when everyone submits at once.
Approach
Append-only solve events with a uniqueness constraint on player-challenge, plus a cached projection refreshed on write rather than recomputed per request.
Outcome
Duplicate submissions cannot double-score, and the leaderboard reads from a projection instead of aggregating the whole solves table.
Problem
Running per-player containers on a student budget does not scale by throwing hardware at it.
Approach
Instances only for challenges that genuinely need isolation, aggressive TTLs, and reclaiming idle containers instead of waiting for a session to expire.
Outcome
Most challenges cost nothing to serve, and the ones that do get torn down within minutes of going idle.
Problem
Difficulty labels written by the author are almost always wrong — you cannot judge a puzzle you already solved.
Approach
Anchoring ratings to solve data rather than intuition, and writing hints as a ladder from 'what should I be looking at' to 'here is the technique'.
Outcome
Difficulty reflects how people actually experience a challenge instead of how hard it felt to build.
Learned
What it taught me
Threat modelling changes completely when hostile input is the product rather than an edge case.
Container isolation is not a checkbox — capabilities, filesystem mode, network policy and resource limits each remove a different class of escape.
Designing for contributors forces cleaner boundaries than designing for yourself. If a challenge cannot be added without touching application code, the abstraction was wrong.
Running infrastructure teaches you which of your architectural opinions were actually free and which ones you pay for monthly.
Writing a good hint is harder than writing the challenge. It has to move someone forward without removing the part they were meant to work out.
Roadmap
Where it goes next
Shipped, in progress and planned — kept honest about which is which.
Foundations
- Core platform: accounts, challenges, flag validation, scoring
- First challenge sets across web, crypto and forensics
- Containerised instances with lifecycle management
- Public launch
Depth
- Guided learning paths per domain
- Author tooling and a documented contribution format
- Team accounts and private competitions
- Write-up submissions attached to solved challenges
Scale
- Season-based events with a reset ladder
- Public API for profiles and solve history
- Accessibility pass across the whole platform
- Self-hostable challenge runner for schools
Sponsors
Who supports Hackuten
Hackuten is supported by companies that build the tools and do the work this platform is trying to teach. Their support covers infrastructure and licences, and gives players access to professional tooling they would not otherwise be able to try.
These organisations sponsor the platform. I am not employed by, affiliated with, or speaking for any of them.
Next
Other projects
Orbyte
An end-to-end encrypted messenger
Orbyte is an encrypted messaging application in the spirit of Session: private conversations that do not require a phone number to start, and a server that is designed to know as little as possible. It is in active development — the parts that are built are built carefully, and the parts that are not are honestly labelled as such.
Read moreBatelys
Lone worker safety, one button at a time
Batelys protects people who work alone. You check in when you arrive, then press a button now and then to confirm you are fine. Miss a check-in — because you fell, collapsed, or simply cannot reach your phone — and an alert leaves on its own, with your position, escalating through your chain until someone responds.
Read more
