Why this platform exists
For Kriya 2026 at PSG College of Technology, I needed more than a static challenge board. As convenor, I had to run a full competition stack that could support story-driven gameplay, real-time scoring, and admin-grade event controls under live load.
This platform was built to do four things reliably:
- Run a complete CTF event with team-based progression.
- Teach practical cyber skills through narrative missions.
- Operate as a real competition system with controls, exports, and leaderboard logic.
- Work across use cases: classroom labs, college fests, bootcamps, and internal drills.

What I built
The application is a mission-oriented CTF platform with two clear surfaces:
- A participant interface for timeline progression, challenge solving, hints, and ranking.
- An admin command center for rounds, challenge lifecycle, moderation, and exports.
The result is a system that is cinematic for players but operationally strict for organizers.


Technology stack
The platform is intentionally split into focused services:
- Frontend: Next.js App Router
- Backend: NestJS REST API
- Database: PostgreSQL with Prisma ORM
- Runtime cache: Redis
- Orchestration: Docker Compose
- Optional operations GUI: pgAdmin container
High-level flow:
- Browser serves frontend on port 43117.
- Frontend calls backend API on port 43118.
- Backend persists users, teams, rounds, challenges, submissions, scores, and story state in PostgreSQL.
- Redis supports fast runtime operations.
- Leaderboard live updates stream through SSE.
This separation kept deployability straightforward and debugging clean during event readiness testing.
Data model and competition state
The competition is backed by explicit domain models instead of ad-hoc state:
- User: identity, role, team relation
- Team: membership, currentLevel, qualification status
- Round: order and lifecycle status
- Challenge: points, flag hash, hints, difficulty, attempt logic
- Submission: each attempt with correctness and awarded points
- Score: aggregate team score
- StoryProgress: mission progression by team
- StoryState: singleton global winner and final outcome state
- Activity: timeline actions like solve and hint usage
- SystemConfig: feature toggles such as scoreboard freeze
Because these are modeled directly, event logic remains auditable and deterministic.
Authentication and authorization model
Authentication is JWT-based with role-aware authorization:
- Register creates a user and bootstraps team, score, and story progress.
- Login issues a JWT.
- Frontend stores token and sends Bearer auth on protected calls.
- JwtStrategy validates and injects identity.
- RolesGuard restricts admin-only operations.
This allows regular participants to move quickly while keeping event-critical actions tightly restricted.
Gameplay and progression engine
The challenge system is linear and state-driven by design.
- Seed initializes 3 rounds and 9 challenges with story context.
- Team starts at level 1 and unlocks one active challenge at a time.
- Submissions are validated against the current active level.
On correct solve:
- Points are awarded.
- Team currentLevel increments.
- StoryProgress advances.
- Activity event is recorded.
Hint usage is tiered and penalized so teams trade certainty for score.
Final winner control is lock-safe:
- First team to clear final level claims round3Winner.
- Later successful teams receive an already-won response.

Scoring and leaderboard behavior
Ranking logic is explicit:
- Sort by totalPoints descending.
- Apply tie-breaker by earlier lastSolved timestamp.
Live ranking updates are emitted every 5 seconds via SSE, with frontend polling as fallback to preserve robustness under network variation.
This dual strategy kept visibility stable during active gameplay windows.
Story-first learning experience
The platform was not designed as a random set of disconnected puzzles. It was structured as a guided mission so participants could learn with context and momentum.
- Story pages set the stakes.
- Mission timeline presents progression across acts.
- Challenge pages connect narrative and technical tasks.


Admin operations during live event
As convenor, the operational layer mattered as much as the challenge layer. Admin capabilities include:
- CRUD for rounds and challenges
- User role updates
- Global stats and submission review
- Manual score adjustments
- Team qualify/disqualify controls
- Competition reset controls
- Scoreboard freeze toggle in config
- JSON and CSV result export
This gave the organizing team the control surface needed to run the event safely and transparently.
Security controls implemented
The platform ships with practical protections:
- bcrypt password hashing
- JWT-protected API routes
- Role-based authorization for admin scope
- Global request validation
- Throttling, including submission-specific throttles
- Health and readiness endpoints for runtime operations
Important implementation notes:
- Documentation mentions email OTP, but current registration marks users verified immediately.
- There are two submission flows in code; frontend gameplay uses the challenge-centric progression flow.
- SSE leaderboard endpoint is intentionally open because EventSource does not send custom auth headers.
- Scoreboard freeze is stored in config and exposed in admin controls; current scoreboard service does not yet enforce freeze blocking in rank output.
These are known and documented so future contributors can reason about behavior without ambiguity.
Outcome
Operation Cipher Strike became more than a themed UI. It was a production-ready event platform with deterministic progression, measurable scoring, and operator controls designed for real competition conditions.
From an engineering perspective, the key win was trust: participants trust fairness, organizers trust operations, and contributors trust system behavior because the core logic is explicit, testable, and auditable.