Skip to content

mazala-cybersec/mazala-ctf-bracket

Repository files navigation

  ╔══════════════════════════════════════════════════════════════╗
  ║                                                              ║
  ║    ██████╗████████╗███████╗  ██████╗ ██████╗  █████╗  ██████╗║
  ║   ██╔════╝╚══██╔══╝██╔════╝  ██╔══██╗██╔══██╗██╔══██╗██╔════╝║
  ║   ██║        ██║   █████╗    ██████╔╝██████╔╝███████║██║     ║
  ║   ██║        ██║   ██╔══╝    ██╔══██╗██╔══██╗██╔══██║██║     ║
  ║   ╚██████╗   ██║   ██║       ██████╔╝██║  ██║██║  ██║╚██████╗║
  ║    ╚═════╝   ╚═╝   ╚═╝       ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝ ╚═════╝║
  ║                                                              ║
  ║              1 v 1   T O U R N A M E N T                     ║
  ║                                                              ║
  ╚══════════════════════════════════════════════════════════════╝

Real-time 1v1 CTF tournament bracket platform with a cyberpunk UI.

Players register, get matched in a single-elimination bracket, and race head-to-head to capture flags. First correct submission wins.

Built for live on-stage cybersecurity competitions.

MIT License Next.js TypeScript Tailwind


Preview

Homepage Live Bracket
Homepage Bracket

Tip: The /bracket page is designed for projector/stream display during live events.


Features

Tournament Engine

  • Single-elimination bracket with play-in rounds (3-16 players, zero byes)
  • Random seeding, random per-match challenge assignment (no repeats)
  • Auto-advance winners through the bracket
  • 10-minute match timer with overtime alerts + random tiebreaker

Live Experience

  • Real-time bracket view with 3-second polling
  • Player dashboard with VS display, challenge description, flag submission
  • Victory / defeat / champion overlays with animations
  • Sound effects (match start, correct, wrong, win, defeat, champion) with mute toggle

Admin Dashboard

  • Manage players, challenges, bracket, and matches from one page
  • Start matches on demand (auto-spins up Docker challenge containers)
  • Manual winner override for overtime situations
  • Full tournament reset

Customization

  • 4 color themes: Cyberpunk (gold/cyan), Hacker (matrix green), Synthwave (pink/purple), Blood (red/orange)
  • Configurable event name, player cap, and secrets via environment variables
  • Glitch text effects, neon glow buttons, HUD-style terminal cards
  • Animated matrix rain background on landing page

Quick Start

git clone https://github.com/mazala-cybersec/mazala-ctf-bracket.git
cd mazala-ctf-bracket
make setup    # install deps, copy .env, init database
make dev      # start dev server

Open http://localhost:3000

Manual setup (without Make)
npm install
cp .env.example .env    # edit with your secrets
npx prisma db push
npm run dev

First Run

  1. Open http://localhost:3000/admin
  2. Login with the ADMIN_SECRET from your .env
  3. Add challenges (title, description, flag, category, points)
  4. Have players register at http://localhost:3000/register
  5. Generate the bracket, start matches, and watch the tournament unfold

Configuration

Variable Description Default
DATABASE_URL SQLite database path file:./dev.db
JWT_SECRET Secret key for signing JWT tokens (required)
ADMIN_SECRET Password for admin login (required)
MAX_PLAYERS Maximum player registrations allowed 16
CTF_ROOT Directory containing your docker-compose.yml cwd
NEXT_PUBLIC_EVENT_NAME Event name displayed in UI CTF BRACKET
NEXT_PUBLIC_EVENT_HIGHLIGHT Word highlighted with glitch effect CTF

Themes

Click the colored dot in the top-right corner of any page to switch themes. Preference is saved per browser.

Theme Colors Vibe
Cyberpunk Gold + Cyan Default, classic CTF
Hacker Green on black Matrix / terminal
Synthwave Pink + Purple Retro neon
Blood Red + Orange Dark and aggressive

How It Works

 REGISTRATION          BRACKET             MATCH LOOP            CHAMPION
 ────────────        ──────────          ─────────────         ──────────
                                         For each match:
  Players            Admin               1. Admin clicks
  register    --->   generates    --->      "Start"       ---> Tournament
  via web            bracket             2. Challenge spins      complete!
                     (random seed)          up in Docker
                                         3. Players race
                                            to submit flag
                                         4. First correct = win
                                         5. Winner auto-advances

Pages

Route Description
/ Landing page — animated background, status, registration link
/register Player registration (handle + password)
/login Player login
/player Player dashboard — VS display, challenge info, flag submission
/admin Admin dashboard — players, challenges, bracket, match controls
/bracket Live bracket view — ideal for projector or stream overlay

Challenge Runner

When admin starts a match, the platform runs docker compose up -d <service> to spin up the challenge container automatically.

1. Define your challenges in docker-compose.yml:

services:
  my-web-challenge:
    build: ./challenges/web
    ports:
      - "4001:5000"
    environment:
      - FLAG=flag{my_secret_flag}

2. Map titles to service names in src/lib/challenge-runner.ts:

const CHALLENGE_SERVICES: Record<string, string> = {
  'Web Injection': 'my-web-challenge',
  'Binary Bomb': 'my-reversing-challenge',
};

3. Add the challenge via admin dashboard or API.

See examples/ for a complete working setup.


Adding Challenges

Via Admin Dashboard

Go to /admin > Challenges tab > fill in the form.

Via API

# Login as admin
curl -c cookies.txt -X POST http://localhost:3000/api/admin/login \
  -H 'Content-Type: application/json' \
  -d '{"password": "your-admin-secret"}'

# Add a challenge
curl -b cookies.txt -X POST http://localhost:3000/api/admin/challenges \
  -H 'Content-Type: application/json' \
  -d '{
    "title": "SQL Injection 101",
    "description": "The search bar looks vulnerable...\n\nTarget: http://localhost:4001",
    "flag": "flag{sqli_master}",
    "category": "web",
    "points": 100
  }'

Via Seed Script

# Edit examples/seed-challenges.py with your challenges, then:
python3 examples/seed-challenges.py [HOST]

Makefile Commands

Command Description
make setup Install deps, copy .env, initialize database
make dev Start development server
make build Production build
make reset Wipe database and recreate schema
make seed Seed example challenges (server must be running)
make clean Remove all build artifacts and dependencies

Project Structure

mazala-ctf-bracket/
  src/
    app/
      api/
        admin/          Admin API (login, players, challenges, bracket, matches)
        auth/           Player auth (register, login, logout, session)
        bracket/        Public bracket data endpoint
        player/         Player match info + flag submission
      admin/            Admin dashboard page
      bracket/          Live bracket view page
      player/           Player dashboard page
      login/            Login page
      register/         Registration page
    components/
      bracket/          BracketView, ChampionDisplay, ConnectorLines,
                        MatchCard, PlayerSlot, RoundColumn
      ui/               GlitchText, NeonButton, TerminalCard, CyberGrid,
                        ThemeSwitcher, SoundToggle, ScanlineOverlay
    lib/
      auth.ts           JWT + bcrypt authentication
      bracket.ts        Tournament generation + bracket logic
      challenge-runner.ts  Docker container lifecycle management
      config.ts         Environment-based configuration
      sfx.ts            Sound effects via Web Audio API
      player-colors.ts  Neon color palette for player avatars
      ratelimit.ts      In-memory rate limiter
  prisma/
    schema.prisma       Database schema (Player, Challenge, Tournament, Match, Submission)
  examples/
    docker-compose.yml  Example challenge container setup
    seed-challenges.py  Example challenge seeding script

Tech Stack

Layer Technology
Framework Next.js 16, React 19
Language TypeScript 5
Styling Tailwind CSS 4 with CSS custom properties
Database SQLite via Prisma 6
Auth JWT (jose) + bcryptjs
Audio Web Audio API
Containers Docker Compose

Security

  • Race-condition-safe flag submission (Prisma transactions + optimistic locking)
  • Input validation and type checking on all API endpoints
  • Rate limiting on auth and submission endpoints
  • Flags masked in admin API responses
  • Player handle sanitization (alphanumeric + underscore/hyphen only)
  • httpOnly, sameSite cookies for JWT tokens
  • No raw SQL — all queries via Prisma ORM

Contributing

Contributions are welcome! Feel free to open issues or submit pull requests.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/something)
  3. Commit your changes
  4. Push to the branch
  5. Open a Pull Request

License

MIT - Built by Mazala Cybersec

About

Real-time 1v1 CTF tournament bracket platform with cyberpunk UI

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages