Skip to content

Game modes ​

A game mode tells vulnboxd which game is being played. It then knows what it would otherwise have to be told, and it can read the checker's verdict on each service from the gameserver.

yaml
game:
  mode: FAUST_2026
  team: 123
modegameserverflag formattimingvulnbox address
GENERICnonefrom the configfrom the configfrom the config
FAUST_2026https://2026.faustctf.net/FAUST_[A-Za-z0-9/+]{32}3 minute ticks from 13:00 UTC on 26 Septemberfd66:666:<team>::2
ECSC_2026http://10.60.249.1ECSC\{[A-Za-z0-9-_]{32}\}followed live from the gameserverfrom the gameserver

Anything set in the config itself wins over the mode: a flag, a tick.start, an init.vulnbox_ip. GENERIC is the default and asks nothing of any gameserver.

What a mode does ​

Fills in the game's facts. The flag format becomes what rules call {flag}. The timing lines the restart window up with the real ticks, and the vulnbox address goes into the capture tools' configuration. A mode that knows which ports its services are on also narrows the capture to them: ECSC_2026 captures 9000-9999, since everything else a vulnbox sends during the ECSC is internal traffic.

Follows the timing. For ECSC, the gameserver publishes when each round starts and ends. vulnboxd reads it with every poll, so its windows stay on the real ticks even if the game is paused or the rounds change length.

Reads the checker's verdicts. Every game.poll (15 seconds), vulnboxd asks the gameserver how the checker found each of your services in the latest tick. It then:

  • sends an alert when a service goes down, recovers, or comes back up;
  • sets vulnboxd_sla_up for each service;
  • marks the running code and rules as working when the checker says up;
  • rolls back a change when the checker says down after it.
gameserver saysvulnboxd reads it as
FAUST up, ECSC SUCCESSup
FAUST recovering, ECSC RECOVERINGrecovering: working, but earlier flags are gone
FAUST down, faulty, flag not found, timeout; ECSC MUMBLE, OFFLINE, TIMEOUTdown
FAUST not checked; ECSC CRASHED, REVOKEDnothing: the checker itself failed or the verdict was withdrawn, so it says nothing about the service

When the gameserver is quiet ​

On a game network the gameserver will be unreachable sometimes, occasionally for several ticks. When a poll fails, vulnboxd logs it once — not on every poll — and then does nothing at all with verdicts until one arrives:

  • No rollback can be triggered. Rolling back happens only on a fresh verdict, so a down from before the gap cannot come back later and undo an innocent change. This is the part worth knowing: silence is safe.
  • vulnboxd_sla_up keeps its last value. It is not zeroed and does not disappear, so a stale 1 means "the last thing we heard", not "up now". If you alert on it, alert on the metric's age too.
  • Nothing is alerted. A gameserver you cannot reach is usually your network, not your service, and it would fire every tick. gameserver: answering again is logged when it comes back.
  • The timing stays where it was. A mode that follows the gameserver's rounds keeps the last ones it was told about; if it was never reached at all, tick.interval from the config is all there is, so set it even under ECSC_2026.

Verdicts resume on the next successful poll, and a service that was down throughout is judged then.

Matching services ​

The gameserver names services as the organisers wrote them, "Interstellar Mission Control" say, and your service directories are called something like interstellar-mission-control. vulnboxd matches them ignoring case, spaces and punctuation. Where that is not enough, say which is which:

yaml
game:
  mode: FAUST_2026
  team: 123
  services:
    imc: Interstellar Mission Control   # directory: name on the scoreboard

A service on the scoreboard that matches no directory is logged once, with the setting that fixes it.

Settings ​

settingdefault
game.modeGENERICGENERIC, FAUST_2026 or ECSC_2026, in any case
game.teamyour team's number on the gameserver; without it the mode fills in the game's facts but reads no verdicts
game.gameserverthe mode'swhere the gameserver is, for a game played somewhere else
game.poll15show often the verdicts are read
game.services{}service directory to scoreboard name, where they are not alike

Next year's game ​

A mode is a preset over the API its gameserver speaks: FAUST's status.json or the ECSC scoreboard API. Next year's game on the same kind of gameserver is a preset of a few lines — a name, its gameserver, flag format, timing, the ports its services are on, and how the vulnbox address follows from the team number — so it is a small pull request rather than new code.

Until it is merged, use the existing mode whose gameserver speaks the same API and point it elsewhere:

yaml
game:
  mode: FAUST_2026              # for the API, not the year
  gameserver: https://2027.faustctf.net/
  team: 123
flag: 'FAUST_[A-Za-z0-9/+]{32}'   # and set the facts yourself

GENERIC cannot do this: with no preset there is no API to speak, so it reads no verdicts at all.