You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Filed as a single feature / tracking issue (owner decision — not an epic with
child issues). The phased breakdown below is tracked as sub-tasks within this issue.
Depends on #266 (on-demand per-team challenge instances): a hard dependency for
Attack-Defense per-team services, a soft dependency for the KotH "hold" variant. KotH
best-solution ranking ships early with little infrastructure.
Scope note: this is a multi-quarter effort. KotH lands value first; Attack-Defense
is substantially larger and lands later.
Summary
Flagpost, like CTFd and rCTF core, runs only jeopardy. This adds two further
competition modes as first-class citizens:
King-of-the-Hill (KotH): round/tick-based; each round ranks submissions or holders
by an objective and awards points to the leader(s); supports both a "best-solution
ranking" variant and a "hold the objective and accrue" variant.
Attack-Defense (A/D): tick engine; per-team defended service instances; per-tick
flag rotation and injection; service SLA/uptime checks; three scoring components
(attack, defense, SLA); stolen-flag submission with dedup and self-flag rejection.
Both reuse Flagpost's real-time board, presence, spectator/venue mode, automation engine,
and RBAC.
Motivation & competitive context
Jeopardy is increasingly commoditised by AI: at BSidesSF 2026 autonomous agents cleared
an entire challenge set within minutes of release, and agent frameworks post near-total
solve rates on standard benchmarks. A/D and KotH are far more discriminating and remain unserved by modern self-hosted platforms — DEF CON-tier events build bespoke tick
engines and infrastructure, and the only turnkey historical option (FBCTF) is legacy.
Being the modern, self-hostable platform that runs these natively is a
category-defining position, and it is the natural payoff of the instancing feature.
Goals
competition.mode as a first-class attribute: jeopardy (existing), king_of_the_hill, attack_defense.
A pluggable scoring engine per mode (generalise the existing static-vs-dynamic
logic into a ScoringEngine strategy).
An append-only score-event log as the source of truth for all point changes
(flag submissions, decay recomputes, tick awards) — audit trail + recompute basis. (Directly inspired by rCTF v2's score-events log.)
KotH: per-challenge objective with a ranking function; per-round leader points;
optional "hold/accrue" with overtaking.
A/D: long-lived, per-team, patchable service instances; organiser-supplied checkers (flag placement + SLA/functionality) run each tick in a sandbox; flag
validity window; stolen-flag submission; three-component scoring; configurable
scoreboard delay.
Mode-aware scoreboard & spectator views reusing venue mode (A/D and KotH are
excellent to broadcast).
Non-goals (initial)
Building a network fabric/VPN between competitor machines. Provide a documented
integration model and hooks (endpoint registry) rather than an SDN in v1.
Add competition.mode. Introduce a ScoringEngine strategy resolved per mode, with
jeopardy re-implemented against it (regression guard). Propose an ADR for the mode
abstraction and the score-event log.
Score-event log
Append-only score_event table: subject_id, source (submission | decay | tick |
capture | sla | adjustment), delta, competition_id, round_id?, metadata, ts.
Scoreboard reads become a projection/aggregate. This also cleans up dynamic-scoring
recomputes for jeopardy.
Tick/round engine
Scheduler with configurable tick length and window; persists a round row per tick;
emits round.started / round.ended on the event bus; respects competition pause.
Same concurrency caveat as instancing (single-process default) — run off the request
path; the worker-process question is shared with #266.
KotH design
Per-challenge objective + ranking function (e.g. best submitted metric, or
current holder token).
Best-solution variant: each round, submissions are scored/ranked; top team(s) earn
round points; cumulative via the score-event log. No instancing required if the
objective is a submitted artifact/flag/metric — this is the low-infra, AI-resistant
early slice.
Hold/accrue variant: a holder token transfers on overtaking; holding across ticks
accrues points. The "hold a shared box" flavour depends on instancing + networking.
Per-team service instances via the On-demand, per-team containerised challenge instances #266 provisioner, but with a distinct
lifecycle: long-lived for the event and team-writable/patchable (teams harden
their own copy), unlike ephemeral jeopardy instances. Call this out explicitly in the
provisioner work.
Checkers (SLA + flag placement): organiser-supplied scripts per service, run each
tick to (a) place a fresh flag and (b) verify functionality. Run sandboxed — this
is a major attack surface (organiser code, every tick, every team).
Flag rotation & validity: flags valid for N ticks; a submission endpoint accepts
stolen flags; reject own-team flags; dedup so each flag scores once per attacker.
Scoring components: attack (captures), defense (not being captured / uptime), SLA
(service up + functional). Configurable weights; configurable scoreboard delay
(default N ticks, per DEF CON convention).
Networking: teams need routed access to opponents' services. Provide a documented
model — platform exposes each team's service at a stable address via an endpoint
registry; recommend WireGuard/overlay or per-team ingress as the fabric. Treat the
fabric as deployment-provided with platform hooks in v1 (open question).
Frontend
KotH: round leaderboard, hold timer, round countdown.
A/D: team service-status panel (your services up/down), live capture feed,
per-component score breakdown, tick timeline.
Extend spectator/venue mode for both (broadcast-friendly).
Automation & events
New events: round.started, round.ended, flag.captured, service.down, service.recovered, koth.lead_changed. Actions can announce first capture, alert on
service-down, etc. (add to the §3.2 catalogue).
RBAC
Permissions for checker management, tick control (start/stop/advance), and viewing raw
captures. Add to the catalogue with default role grants.
Import/export
Extend the challenge schema for A/D service specs and checker references so mode-specific
challenges remain portable.
Networking integration model + endpoint registry (A/D) — documented fabric + platform hooks (needs its own ADR)
Import/export for mode-specific challenge specs
Docs: running a KotH event / running an A/D event
Overall acceptance criteria
An organiser can create a competition in king_of_the_hill mode, run ticked rounds,
and see leader points accrue correctly through the score-event log.
An organiser can create an attack_defense competition where each team receives a
patchable service, checkers place flags and measure SLA each tick, captures score, and
the delayed scoreboard reflects attack/defense/SLA components.
Jeopardy behaviour is unchanged and regression-tested after the scoring refactor.
Both modes broadcast via spectator/venue mode.
Risks & open questions
Network fabric ownership: how much does the platform own vs. document? (ADR)
Checker sandbox security: running organiser code every tick per team is a large
attack surface — isolation and resource limits are critical.
Summary
Flagpost, like CTFd and rCTF core, runs only jeopardy. This adds two further
competition modes as first-class citizens:
by an objective and awards points to the leader(s); supports both a "best-solution
ranking" variant and a "hold the objective and accrue" variant.
flag rotation and injection; service SLA/uptime checks; three scoring components
(attack, defense, SLA); stolen-flag submission with dedup and self-flag rejection.
Both reuse Flagpost's real-time board, presence, spectator/venue mode, automation engine,
and RBAC.
Motivation & competitive context
Jeopardy is increasingly commoditised by AI: at BSidesSF 2026 autonomous agents cleared
an entire challenge set within minutes of release, and agent frameworks post near-total
solve rates on standard benchmarks. A/D and KotH are far more discriminating and remain
unserved by modern self-hosted platforms — DEF CON-tier events build bespoke tick
engines and infrastructure, and the only turnkey historical option (FBCTF) is legacy.
Being the modern, self-hostable platform that runs these natively is a
category-defining position, and it is the natural payoff of the instancing feature.
Goals
competition.modeas a first-class attribute:jeopardy(existing),king_of_the_hill,attack_defense.logic into a
ScoringEnginestrategy).(flag submissions, decay recomputes, tick awards) — audit trail + recompute basis.
(Directly inspired by rCTF v2's score-events log.)
competition pause; emits round lifecycle events.
optional "hold/accrue" with overtaking.
checkers (flag placement + SLA/functionality) run each tick in a sandbox; flag
validity window; stolen-flag submission; three-component scoring; configurable
scoreboard delay.
excellent to broadcast).
Non-goals (initial)
integration model and hooks (endpoint registry) rather than an SDN in v1.
Proposed design
Mode & scoring abstraction
Add
competition.mode. Introduce aScoringEnginestrategy resolved per mode, withjeopardy re-implemented against it (regression guard). Propose an ADR for the mode
abstraction and the score-event log.
Score-event log
Append-only
score_eventtable:subject_id,source(submission | decay | tick |capture | sla | adjustment),
delta,competition_id,round_id?,metadata,ts.Scoreboard reads become a projection/aggregate. This also cleans up dynamic-scoring
recomputes for jeopardy.
Tick/round engine
Scheduler with configurable tick length and window; persists a
roundrow per tick;emits
round.started/round.endedon the event bus; respects competition pause.Same concurrency caveat as instancing (single-process default) — run off the request
path; the worker-process question is shared with #266.
KotH design
current holder token).
round points; cumulative via the score-event log. No instancing required if the
objective is a submitted artifact/flag/metric — this is the low-infra, AI-resistant
early slice.
accrues points. The "hold a shared box" flavour depends on instancing + networking.
Attack-Defense design
lifecycle: long-lived for the event and team-writable/patchable (teams harden
their own copy), unlike ephemeral jeopardy instances. Call this out explicitly in the
provisioner work.
tick to (a) place a fresh flag and (b) verify functionality. Run sandboxed — this
is a major attack surface (organiser code, every tick, every team).
stolen flags; reject own-team flags; dedup so each flag scores once per attacker.
(service up + functional). Configurable weights; configurable scoreboard delay
(default N ticks, per DEF CON convention).
model — platform exposes each team's service at a stable address via an endpoint
registry; recommend WireGuard/overlay or per-team ingress as the fabric. Treat the
fabric as deployment-provided with platform hooks in v1 (open question).
Frontend
per-component score breakdown, tick timeline.
Automation & events
New events:
round.started,round.ended,flag.captured,service.down,service.recovered,koth.lead_changed. Actions can announce first capture, alert onservice-down, etc. (add to the §3.2 catalogue).
RBAC
Permissions for checker management, tick control (start/stop/advance), and viewing raw
captures. Add to the catalogue with default role grants.
Import/export
Extend the challenge schema for A/D service specs and checker references so mode-specific
challenges remain portable.
Phasing
engine + ranked-per-round scoring. Ships early, AI-resistant, minimal infra.
flavour once instancing Phase 3 lands.
flag rotation + capture submission; basic three-component scoring.
guidance/hooks, retire/colour mechanics, load hardening.
Proposed breakdown (sub-tasks tracked within this issue)
ScoringEngine)competition.modeattribute + mode plumbing (jeopardy default unchanged; surfaced in settings)score_eventlog + scoreboard projection (all jeopardy point changes flow through it; dynamic recompute preserved)roundmodel + lifecycle events (honours pause; off the request path)Overall acceptance criteria
king_of_the_hillmode, run ticked rounds,and see leader points accrue correctly through the score-event log.
attack_defensecompetition where each team receives apatchable service, checkers place flags and measure SLA each tick, captures score, and
the delayed scoreboard reflects attack/defense/SLA components.
Risks & open questions
attack surface — isolation and resource limits are critical.
ephemeral model in On-demand, per-team containerised challenge instances #266 — reconcile early.
Derived from an internal feature brief. This is the canonical tracking issue for the feature.