Skip to content

gl0bal01/ctfd-warboard

Repository files navigation

ctfd-warboard

License: MIT Bun TypeScript

ctfd-warboard is a practical teamwork tool for CTF students using a CTFd-based competition site.

It takes challenges from CTFd, saves them into a shared private git repository, and lets your team coordinate through either Discord slash commands or a terminal CLI. The goal is simple: everyone can see who is working on what, write notes, save files, submit solves, and keep the team workspace synchronized.

Useful guides:

What Problem Does It Solve?

During a CTF, teams often lose time because work is scattered:

  • One person has challenge files on their laptop.
  • Another person writes notes in Discord.
  • Someone solves a challenge but forgets to tell the team.
  • Multiple teammates attack the same challenge without knowing it.
  • The CTFd site gets spammed by many refreshes and downloads.

ctfd-warboard gives the team one shared working board.

It fetches challenges carefully from CTFd, stores the data locally, writes team activity to git, and keeps Discord and CLI users looking at the same state.

The Big Idea

There are five moving parts:

Part What it is Why students care
CTFd The competition website Source of challenges, files, points, and flag submission
ctfd-warboard core Shared TypeScript logic Handles CTFd, cache, validation, files, and git sync
Discord bot Team interface in Discord Best for live coordination during a CTF
CLI Terminal interface named ctfd Best for local work, scripts, writeups, and offline reading
Private git repo Shared workspace The source of truth for notes, claims, solves, files, and writeups

You can use only the Discord bot, only the CLI, or both. The git repo connects everything.

Schema

flowchart LR
    CTFd["CTFd competition site<br/>challenges, files, flags"]
    Core["ctfd-warboard core<br/>rate limit, cache, validation"]
    Git["Private git repo<br/>shared team state"]
    Bot["Discord bot<br/>slash commands, threads, polling"]
    CLI["CLI: ctfd<br/>terminal commands"]
    Students["Students<br/>claim, note, solve, writeup"]

    CTFd -->|"1 request/sec fetch<br/>refresh only what changed"| Core
    Core -->|"writes challenge folders<br/>claims, notes, solves"| Git
    Git -->|"pull/push sync"| Bot
    Git -->|"pull/push sync"| CLI
    Bot -->|"Discord workflow"| Students
    CLI -->|"terminal workflow"| Students
    Students -->|"flags"| Bot
    Students -->|"flags"| CLI
    Bot -->|"submit solve"| CTFd
    CLI -->|"submit solve"| CTFd
Loading

A Student-Friendly Mental Model

Think of one CTF as one project folder.

Inside that folder, each challenge has its own directory:

web/
  sql-injection/
    challenge.md     # description copied from CTFd
    notes.md         # team notes
    solve.md         # final explanation / writeup
    files/           # downloaded challenge attachments

When Alice claims a challenge, ctfd-warboard records that in git. When Bob adds a note in Discord, that note is saved in the same repo. When someone writes a solve explanation, it also lives in the repo.

That means the team does not need to search old Discord messages to reconstruct the solution later.

Choose Your Workflow

Question Use Discord bot Use CLI
I want the easiest team workflow Yes Optional
I work mostly in terminal Optional Yes
I want automatic polling for new challenges Yes No, manual ctfd refresh
I want to edit writeups in my editor Optional Yes
I do not have git access Yes, if bot is already set up No
I am the team admin Usually yes Useful too

Most student teams should start with the Discord bot, then let power users join the same repo with the CLI.

Quick Start for CLI Users

Use this path if you want to work from your laptop terminal.

1. Install Requirements

Install:

  • Bun v1.0 or newer
  • Git
  • Access to the team's private git repo

Then clone and install:

git clone https://github.com/gl0bal01/ctfd-warboard
cd ctfd-warboard
bun install
cd packages/cli && bun link && cd ../..

Check that the command exists:

ctfd --help

If you do not want to link the CLI globally, run it from source:

bun run cli -- status

2. Set Your Handle

Your handle is the name shown in claims and notes.

Create ~/.ctfd-warboard/config.json:

{
  "handle": "alice"
}

Replace alice with your CTF handle.

3. Start a New CTF

Use this if you are creating the team workspace.

First create an empty private repo on GitHub, GitLab, Gitea, or another git host. The repo must already exist and you must have write access.

Then initialize:

ctfd init https://ctf.example.com --repo git@github.com:team/example-ctf.git

ctfd-warboard will ask for credentials if needed.

You can also provide credentials directly:

ctfd init https://ctf.example.com \
  --repo git@github.com:team/example-ctf.git \
  --username myuser \
  --password mypass

Or use a CTFd API token:

ctfd init https://ctf.example.com \
  --repo git@github.com:team/example-ctf.git \
  --token ctfd_abc123

Environment variables work too:

export CTFD_TOKEN=ctfd_abc123
ctfd init https://ctf.example.com --repo git@github.com:team/example-ctf.git

4. Join an Existing CTF

Use this if a teammate or the Discord bot already created the repo:

ctfd join git@github.com:team/example-ctf.git

Now you have the same challenge files, notes, claims, and writeups as the rest of the team.

5. Daily CLI Commands

ctfd status

Shows all challenges, categories, points, solved state, and assignees.

ctfd claim web/sql-injection

Marks the challenge as yours so teammates know you are working on it.

ctfd note web/sql-injection "Login blocks single quotes. Trying hex encoding next."

Adds a timestamped note to the challenge.

ctfd writeup web/sql-injection

Opens solve.md in your $EDITOR.

ctfd solve web/sql-injection

Prompts for the flag, marks the challenge solved, and submits to CTFd.

ctfd unclaim web/sql-injection

Releases the challenge if you stop working on it.

ctfd refresh

Fetches the latest challenge state from CTFd.

More management commands:

ctfd list
ctfd use example-ctf
ctfd scoreboard
ctfd archive example-ctf

Quick Start for Discord Bot Users

Use this path if your team wants to coordinate inside Discord.

1. Create a Discord Application

  1. Go to the Discord Developer Portal.
  2. Click New Application.
  3. Open the Bot tab and create or reset the bot token.
  4. Copy the bot token. This becomes DISCORD_TOKEN.
  5. Open General Information and copy the Application ID. This becomes DISCORD_CLIENT_ID.
  6. In the Bot tab, enable these privileged gateway intents:
    • Presence Intent
    • Server Members Intent
    • Message Content Intent

2. Invite the Bot

The bot needs permission to create channels, create threads, send messages, attach files, and manage challenge discussions.

Use this invite URL and replace YOUR_CLIENT_ID:

https://discord.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&scope=bot+applications.commands&permissions=395405552656

3. Configure Local Development

git clone https://github.com/gl0bal01/ctfd-warboard
cd ctfd-warboard
bun install
cp .env.example .env

Edit .env:

DISCORD_TOKEN=your_bot_token
DISCORD_CLIENT_ID=your_application_id
CTFD_TOKEN=your_ctfd_token
POLL_INTERVAL_MINUTES=10

If the CTFd instance does not support API tokens, use username and password instead:

CTFD_USERNAME=your_username
CTFD_PASSWORD=your_password

4. Register Slash Commands

bun run bot:deploy

Discord global commands can take 1 to 5 minutes to appear.

5. Start the Bot

bun run bot

When the bot is online, start a CTF from Discord:

/ctf start url:https://ctf.example.com repo:git@github.com:team/example-ctf.git

The bot will:

  1. Fetch challenges from CTFd.
  2. Download challenge files.
  3. Create Discord structure.
  4. Save challenge folders locally.
  5. Commit and push the result to the private git repo.
  6. Poll for updates every 10 minutes by default.

6. Common Bot Commands

Admin commands:

/ctf start <url> <repo>
/ctf join <repo>
/ctf list
/ctf refresh
/ctf archive
/ctf status

Challenge thread commands:

/claim
/unclaim
/note <message>
/writeup <content>
/solve <flag>

Files uploaded into a challenge thread are saved into that challenge's folder in git.

What Happens During a CTF?

Here is a normal team flow:

  1. Team admin creates an empty private git repo.
  2. Admin starts ctfd-warboard with either CLI or Discord bot.
  3. ctfd-warboard fetches challenge data from CTFd at a safe speed.
  4. The bot creates Discord channels and threads, or CLI users work from terminal.
  5. A student claims a challenge before working on it.
  6. Students add notes as they test ideas.
  7. Challenge files, notes, writeups, and claim state are committed to git.
  8. When a flag is found, a student runs /solve or ctfd solve.
  9. The result is submitted to CTFd and saved for the team.
  10. After the CTF, the repo already contains the material needed for writeups and review.

File Layout on Your Computer

By default, ctfd-warboard stores state under ~/.ctfd-warboard/.

~/.ctfd-warboard/
  config.json
  repos/
    example-ctf/
      .ctfd.json
      claims/
      web/
        sql-injection/
          challenge.md
          notes.md
          solve.md
          files/
  ctfs/
    example-ctf/
      cache.json
      meta.json
  locks/
    example-ctf.lock

Important files:

File Purpose
config.json Your handle, editor, active CTF, and default settings
.ctfd.json CTF metadata committed to git, without secrets
challenge.md Challenge description from CTFd
notes.md Team notes, appended with timestamps
solve.md Final writeup
cache.json Local cache of CTFd data
meta.json Local CTF metadata

Notes are append-only to reduce git conflicts:

## [2026-03-21T14:30:00Z] @alice
Tried basic SQLi on login form. Single quotes are blocked.
---

Security Basics

Do not commit tokens, passwords, session cookies, or private keys.

Use .env for local bot development. It is git-ignored.

Use ~/.ctfd-warboard/bot.json or environment variables for CTFd credentials:

{
  "ctfs": {
    "example-ctf": {
      "token": "ctfd_abc123"
    }
  }
}

Then restrict the file:

chmod 600 ~/.ctfd-warboard/bot.json

For production, store bot secrets in /etc/ctfd-warboard/bot.env with 0600 permissions. See docs/deployment.md.

ctfd-warboard also includes validation for risky inputs such as private-network CTFd URLs, path traversal in filenames, unsafe repo paths, and unsafe commit messages.

Architecture for Students Who Want to Hack on the Code

This repository is a Bun + TypeScript monorepo.

packages/
  core/    # CTFd client, cache, git sync, config, validation, shared types
  cli/     # terminal command wrapper around core
  bot/     # Discord slash commands, polling, thread/file handling

The important design rule is:

core = real behavior
cli  = terminal interface
bot  = Discord interface

So if you fix how a challenge is fetched, cached, validated, or written to git, that code probably belongs in packages/core.

Development Commands

Install dependencies:

bun install

Run the CLI from source:

bun run cli -- status

Run the bot locally:

bun run bot

Deploy slash commands:

bun run bot:deploy

Check code quality:

bun run lint
bun run typecheck
bun test --recursive

Run the full project check:

make check

Production Deployment

For real CTF use, run the Discord bot on a VPS or always-on server. A laptop that sleeps will miss polling and Discord interactions.

Short version:

git clone https://github.com/gl0bal01/ctfd-warboard /opt/ctfd-warboard
cd /opt/ctfd-warboard
make install

Then create /etc/ctfd-warboard/bot.env, install the systemd service, and start it:

make service-install
make service-start

Read the full production guide before using this in a serious event:

Common Mistakes

The bot cannot push to git.

Create the remote repo before running /ctf start, then make sure the bot server's SSH key has write access. The bot only accepts SSH remotes (git@host:owner/repo.git); HTTPS remotes are rejected because the bot runs non-interactively and cannot supply credentials. (The CLI still allows HTTPS for interactive use.)

Slash commands are missing or stuck on "Loading...".

Wait 1 to 5 minutes after bun run bot:deploy. Re-run the deploy command if they never appear.

The CLI cannot see the CTF created by the bot.

The CLI and bot must use the same CTFD_WARBOARD_HOME, or the CLI user must run ctfd join <repo>.

CTFd token authentication fails.

Some CTFd instances do not support API tokens. Use CTFD_USERNAME and CTFD_PASSWORD.

A challenge was added but /solve fails.

Run /ctf refresh in Discord or ctfd refresh in the terminal so the cache has the latest challenge metadata.

Two people edit the same writeup.

Use git pull, resolve the conflict, and keep both useful parts. Notes are append-only, so they usually conflict less than writeups.

Known Limitations

  • Bun is required. Node.js is not a drop-in replacement.
  • The bot must stay online for polling and Discord slash commands.
  • The private git remote must already exist and be writable. For the bot it must be an SSH remote (git@...); HTTPS remotes are rejected.
  • Very large CTFs can make initial setup slow because files must be downloaded and committed.
  • One shared data root per bot host is expected. Do not run multiple bot instances against the same root.

License

MIT

About

A gentle CTFd parser designed to minimize load on CTF platforms. CLI + Discord bot for small-team collaboration, with automatic git sync to private repos.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors