Files
HIPCTF2/docs/database/challenges.md
T

5.0 KiB

type, title, description, tags, timestamp
type title description tags timestamp
database Challenge Tables category, challenge, challenge_file, and solve tables — how CTF challenges and scoring are stored.
database
challenge
category
solve
2026-07-21T14:18:00Z

Tables

category

Column Type Description
id TEXT PK UUID v4.
system_key TEXT One of crypto, forensics, pwn, web, misc, osint (unique where NOT NULL) or NULL for user-created categories.
name TEXT Display name (e.g. Cryptography).
abbreviation TEXT Short label (e.g. CRY).
description TEXT Optional description.
icon_path TEXT Default icon URL (e.g. /uploads/icons/crypto.svg).

The seed migration inserts one row per SYSTEM_CATEGORY_KEYS value from backend/src/config/env.schema.ts.

challenge

Column Type Description
id TEXT PK UUID v4.
name TEXT Display name.
description_md TEXT Markdown description.
category_id TEXT FK → category.id.
difficulty TEXT 'low' / 'med' / 'high'.
initial_points INTEGER Starting point value.
minimum_points INTEGER Floor after decay.
decay_solves INTEGER Number of solves at which decay reaches the minimum.
flag TEXT Submission string expected from players.
protocol TEXT 'nc' (netcat-style connection) or 'web' (browser-based).
port INTEGER TCP port when relevant (nullable).
ip_address TEXT IP the challenge listens on (defaulted from setting.defaultChallengeIp).
created_at TEXT ISO 8601 timestamp.

challenge_file

Column Type Description
id TEXT PK UUID v4.
challenge_id TEXT FK → challenge.id.
original_filename TEXT Filename from upload.
stored_path TEXT Path on disk under UPLOAD_DIR/challenges/.

solve

Column Type Description
id TEXT PK UUID v4.
challenge_id TEXT FK → challenge.id.
user_id TEXT FK → user.id.
solved_at TEXT ISO 8601 timestamp the solve was recorded.
points_awarded INTEGER Points actually awarded (after decay).
base_points INTEGER Snapshot of challenge.initial_points at solve time.
rank_bonus INTEGER Extra points from rank (e.g. first-blood).

Uniqueness is enforced by uq_solve_challenge_user on (challenge_id, user_id) — a user can solve a given challenge only once.

Scoring formula

points_awarded = clamp(initial_points - decay, minimum_points, initial_points) where decay = max(0, base_points - minimum_points) * solvesBefore / decay_solves, and additional rank_bonus may be added for early solves.

(The exact scoring algorithm lives in the future solve-recording service and is consumed by SseHubService whenever a new solve is published.)

See also