rCTF Docs
Overview

Upgrading from v1

Migration guide from rCTF v1 to v2, covering database changes, API differences, and configuration updates.

This guide covers what changed between rCTF v1 and v2, and what you need to do to upgrade.

In short, swap the image and restart#

The upgrade is automated. Point your existing deployment at the v2 image and start it. Drizzle migrations run on boot, and v1 recaptcha / globalSiteTag config gets converted to the new shape at startup. No manual SQL, no config rewrite needed to get back online.

compose.yml
services:
rctf:
image: ghcr.io/otter-sec/rctf-new:latest
Warning (Back up the database first)

Migrations are forward-only and modify the schema in place. Take a PostgreSQL dump (e.g. pg_dump) before swapping the image so you have a way back if something goes wrong.

If you want to know what actually changes (new tables, new columns, API differences, and config sections worth rewriting by hand), read on. None of it is required to upgrade.

Database migration#

Drizzle ORM handles schema changes and runs them automatically when database.migrate is before (the default in the bundled container). To run them by hand:

Terminal window
bun run db:migrate

New tables#

TablePurpose
admin_bot_jobsTracks admin bot job queue (status, inputs, logs)
settingsStores runtime-editable settings (CTF name, client timing, sponsors, etc.)

New columns on users#

ColumnTypeDescription
avatar_urltextURL to user’s avatar image
country_codetextISO 3166-1 alpha-2 country code
status_texttextUser status message (max 60 chars)
scoreintegerCached current score
global_rankintegerCached global leaderboard rank
division_rankintegerCached division-specific rank
last_solve_attimestampTime of last solve (for tiebreaking)
last_tiebreak_solve_attimestampTime of last tiebreak-eligible solve

New columns on challenges#

ColumnTypeDescription
scoreintegerCached current challenge score
solve_countintegerCached number of solves

API changes#

All v1 routes still work. The v2 API lives at /api/v2/... and adds several improvements and new endpoints.

Captcha field rename#

V2 routes use captchaCode instead of recaptchaCode in request bodies. This applies to every endpoint that supports captcha validation.

File uploads#

The v2 admin upload endpoint (POST /api/v2/admin/upload) takes binary files via multipart/form-data rather than the base64-encoded data URIs v1 used.

New v2 endpoints#

EndpointDescription
GET /v2/auth/verify-infoReturns info about a verification token
GET /v2/leaderboard/challsChallenge metadata with first 3 solvers per challenge
GET /v2/leaderboard/with-graphCombined leaderboard and graph in one request
PATCH /v2/users/me/avatarUpload and moderate avatar images
GET/POST /v2/admin/usersList all users with search, filters, and pagination
GET/PUT /v2/admin/settingsRuntime settings management
POST /v2/admin/users/:id/tokenGenerate login token for a team
DELETE /v2/admin/challs/:id/solves/:userIdDelete a specific solve
GET /v2/admin/instancer/schemaInstancer config JSON schema
GET/POST /v2/admin/admin-bot/*Admin bot job management
GET/PUT/PATCH/DELETE /v2/integrations/challs/:id/instanceChallenge instance lifecycle
GET/POST /v2/integrations/challs/:id/admin-bot/*Admin bot submission and status

Response differences#

  • Challenge files in v2 include a size field (v1 did not track file sizes)
  • Challenge solves in v2 include user profile data (avatar, country, status) and blood status
  • Leaderboard in v2 supports a search query parameter for team name search (rate limited)

Configuration changes#

Captcha provider#

The v1 recaptcha top-level config gets converted to the new captcha.provider format at startup automatically. We still recommend migrating to the new format by hand:

v1
recaptcha:
siteKey: your-site-key
secretKey: your-secret-key
protectedActions:
- register
v2
captcha:
provider:
name: captcha/recaptcha
options:
siteKey: your-site-key
secretKey: your-secret-key
protectedEndpoints:
- register

The new format also supports captcha/hcaptcha and captcha/turnstile providers.

Analytics provider#

The v1 globalSiteTag config gets converted to the analytics.provider format automatically. Migrate to:

v2 (recommended)
analytics:
provider:
name: analytics/google
options:
siteTag: G-XXXXXXXX

Additional analytics providers are also available, such as analytics/cloudflare.

New configuration sections#

v2 adds the following configuration sections:

SectionPurpose
adminBotAdmin bot provider and settings
avatarsModerationAvatar content moderation (OpenAI)
bloodBotFirst blood notifications (Discord/Telegram)
analyticsAnalytics provider
proxyReverse proxy and Cloudflare settings
maxAvatarSizeMaximum avatar upload size (default 1 MB)

See the Configuration reference for details on every option.

Esc

Start typing to search the docs.