Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/flagForgeCTF/flagForge/llms.txt

Use this file to discover all available pages before exploring further.

FlagForge is built on Next.js 16 with the App Router, written entirely in TypeScript. The platform uses MongoDB for persistent storage, NextAuth v4 for Google OAuth authentication, Tailwind CSS with shadcn/ui for styling, Upstash Redis for token blacklisting, and the Notion API to serve blog and resources content. Understanding this stack helps you navigate the codebase and make contributions confidently.

Technology stack

Next.js 16 (App Router)

All routing, server components, and API routes use the Next.js App Router with TypeScript throughout.

MongoDB + Mongoose

Mongoose schemas define all data models. The database connection is managed in utils/db.ts.

NextAuth v4

Google OAuth authentication with session-based cookies. Configuration lives in lib/authOptions.ts.

Tailwind CSS + shadcn/ui

Utility-first styling with shadcn/ui primitives. Global styles are in app/globals.css.

Upstash Redis

Used for token blacklisting to invalidate sessions on logout. Logic lives in lib/tokenBlacklist.ts.

Notion API

Blog posts and resources are sourced from Notion via @notionhq/client and notion-to-md.

Project structure

The repository follows the Next.js App Router convention. Here is a breakdown of the key directories:
.
├── app/                        # Next.js App Router routes
│   ├── (footer)/               # Footer page routes (about, privacy, terms)
│   ├── (main)/                 # Main application routes (home, problems, leaderboard)
│   ├── api/                    # REST API route handlers
│   │   ├── admin/
│   │   ├── auth/
│   │   ├── problems/
│   │   ├── leaderboard/
│   │   └── ...
│   └── user/[username]/        # Dynamic user profile routes
├── components/                 # React components
│   ├── ui/                     # shadcn/ui primitives
│   ├── Navbar.tsx
│   ├── Footer.tsx
│   └── ...
├── models/                     # Mongoose schemas
│   ├── qustionsSchema.ts       # Challenge/question model
│   ├── userSchema.ts           # User model
│   ├── userQuestionSchema.ts   # Solved challenge tracking
│   └── ...
├── interfaces/                 # TypeScript type definitions
│   └── index.ts
├── middleware/                 # Auth middleware
│   ├── adminToken.ts           # Admin token verification
│   └── tokenBlacklist.ts       # Blacklist check on requests
├── utils/                      # Shared utilities
│   ├── db.ts                   # MongoDB connection
│   ├── auth.ts                 # Auth helpers
│   └── discordNotifier.ts      # Discord webhook notifications
├── lib/                        # Library configuration
│   ├── authOptions.ts          # NextAuth config
│   └── tokenBlacklist.ts       # Upstash Redis blacklist
└── middleware.ts               # Next.js root middleware
The /app/api/ directory contains all REST API route handlers. Every handler validates the user session before processing a request.

Directory reference

DirectoryPurpose
/appNext.js App Router pages and layouts
/app/apiREST API endpoints as Next.js route handlers
/componentsReusable React components
/modelsMongoose schemas for MongoDB collections
/interfacesShared TypeScript interfaces and types
/middlewareAuth middleware (token blacklist, admin checks)
/utilsDatabase connection, auth helpers, Discord notifier

Dynamic flag system

The dynamic flag system is FlagForge’s core feature. It generates a unique, user-specific flag for each challenge so that players cannot share flags with each other.

Architecture diagram

┌─────────────────────────────────────────────────────────────────┐
│                        👤 USER LAYER                             │
│                                                                   │
│    CTF Player  →  [Start Challenge]  →  [Submit Flag]           │
└────────────┬──────────────────┬──────────────────────────────────┘
             │                  │
             │ ① Request Flag   │ ⑤ Submit Flag
             ↓                  ↓
┌─────────────────────────────────────────────────────────────────┐
│                       🔌 API LAYER                               │
│                                                                   │
│  POST /api/problems/dynamic-flag  │  POST /api/problems/[id]    │
│  (Generate Flag)                   │  (Validate Flag)            │
└────────────┬──────────────────────────────┬─────────────────────┘
             │                               │
             │ ② Generate                    │ ⑥ Validate
             ↓                               ↓
┌─────────────────────────────────────────────────────────────────┐
│                  ⚙️ BUSINESS LOGIC LAYER                         │
│                                                                   │
│  Flag Generator Utility:          Flag Types:                    │
│  • generateGUID()                 • GUID                         │
│  • generateTeamHash()             • TEAM_HASH                    │
│  • applyLeetSpeak()               • LEET                         │
│  • validateFlag()                 • CLEET                        │
│  • calculateEntropy()                                            │
└────────────┬──────────────────────────────┬─────────────────────┘
             │                               │
             │ ③ Store (24h)    ⑦ Check     │ ⑧ Fallback
             ↓                  ↓            ↓
┌─────────────────────────────────────────────────────────────────┐
│                    💾 DATABASE LAYER (MongoDB)                   │
│                                                                   │
│  DynamicFlag         Question            UserQuestion            │
│  • userId            • _id               • userId                │
│  • questionId        • flag (template)   • questionId            │
│  • flag              • points            • solved                │
│  • expiresAt (24h)   • category          • pointsEarned         │
└─────────────────────────────────────────────────────────────────┘
             │                               │
             │ ④ Return Flag                 │ ⑨ Save Solution
             ↓                               ↓
             └───────────────┬───────────────┘

                             │ ⑩ Success!

                        👤 USER LAYER

Flag generation

When a user starts a challenge, the frontend calls POST /api/problems/dynamic-flag. The API validates the session, checks whether a flag already exists for that user and question, and either returns the existing flag or generates a new one. Generation reads the challenge’s flag template and applies one of four strategies:
TypeDescriptionExample
GUIDRandom UUIDflag{1bab71b8-117f-4dea-a047-340b72101d7b}
TEAM_HASHSHA256(salt + userId + questionId)flag{hello_world_5418ce4d815c}
LEETLeet-speak transformationflag{H3ll0_W0r1d}
CLEETComplex leet with special charactersflag{H3!!0_W0r!d}
All generated flags are validated for a minimum entropy of 32 bits before storage.
The FLAG_SALT environment variable is required for TEAM_HASH generation. Set it to a secure random value in your .env file.

Storage and TTL

Generated flags are saved to the DynamicFlag MongoDB collection with a expiresAt field set 24 hours in the future. A TTL index on expiresAt automatically removes expired documents. A compound index on (userId, questionId) ensures fast lookups during validation.

Validation flow

When a user submits a flag, POST /api/problems/[id] runs the following checks:
  1. Session check — reject unauthenticated requests
  2. Duplicate check — reject if the user already solved this challenge
  3. Dynamic flag check — query the DynamicFlag collection for a matching, non-expired document
  4. Fallback to static — if no dynamic flag is found, compare against the challenge’s static flag stored on the Question document
If the flag is correct, the API creates a UserQuestion record, updates the user’s total score, awards points minus any hint penalties, and deletes the used dynamic flag.
Dynamic flags are one-time use. The flag is deleted from the database immediately after a successful submission.

API layer

All API route handlers live in /app/api/ and follow the Next.js App Router convention (route.ts files). Every handler begins with a session check — unauthenticated requests receive a 401 response. Key API groups:
Route prefixResponsibility
/api/authNextAuth session endpoints
/api/problemsChallenge listing, flag generation, flag submission
/api/leaderboardLeaderboard data
/api/adminAdmin-only operations
/api/profileUser profile reads and updates
/api/blogsNotion-backed blog content

Middleware

FlagForge uses two middleware layers that run on every request:
  • Token blacklist middleware (middleware/tokenBlacklist.ts) — checks Upstash Redis for the request’s session token. If the token has been blacklisted (e.g., after logout), the request is rejected.
  • Admin token middleware (middleware/adminToken.ts) — verifies a secondary admin token for privileged routes under /api/admin/.
The root middleware.ts file composes these checks and applies them to the appropriate route matchers.

Build docs developers (and LLMs) love