Configuration Reference
This page is the authoritative list of every knob the A&D / KotH fork exposes, grouped by where it lives:
- App config — process-level settings read from
appsettings.jsonor environment variables at startup (database, Redis, container backend, theAd:*infrastructure keys,XorKey). - Per-game — event-wide A&D / KotH policy stored on the
Gamerow; most are editable in the admin Game → Info form. - Per-challenge — the
ad:block on an individual A&D challenge.
Every default below is taken from the code. Where a value is read straight from configuration with no strongly-typed model, that is stated.
The A&D and KotH engine is a fork addition. The upstream jeopardy keys (account policy, container lifetime, telemetry, mail, captcha) are unchanged and only the ones relevant to running an A&D/KotH event are summarised here. See also /guide/features/attack-defense, /guide/features/king-of-the-hill, and /guide/features/scoring.
App-level configuration
App config binds from appsettings.json (and appsettings.{Environment}.json) or, in containerised deploys, from environment variables using the standard .NET double-underscore nesting convention (Section__SubKey). The fork's reference deploy sets everything via the environment: block in docker-compose.yml; the JSON shapes below are equivalent.
Connection strings
As environment variables (compose style):
XorKey
XorKey is the master obfuscation key. It XOR-protects every secret stored at rest — the per-game Ed25519 signing private key (see Game.GenerateKeyPair / Game.Sign), the API encryption / token key pairs, and the XOR-obfuscated passwords in the admin config (build registry, SMTP, pull registry, captcha secret).
The platform refuses to start if XorKey is unset (PrelaunchHelper exits fatally with Init_XorKeyNotSet). Set it to a strong random value and treat it like a database master key: rotating it invalidates every secret encrypted under the old key, including game signing keys.
Container provider
The ContainerProvider section selects and configures the backend that launches challenge containers. It maps to the ContainerProvider model in Models/Internal/Configs.cs.
Docker sub-config (ContainerProvider:DockerConfig)
On the Docker backend the A&D /flag file is delivered by a read-only bind mount from the host (the ad-flags volume), so container-root can't tamper with it. No pull sidecar is involved. See /guide/deployment/provider.
Kubernetes sub-config (ContainerProvider:KubernetesConfig)
On the Kubernetes backend the /flag is delivered by a pull sidecar that fetches it over HTTP, so Ad:FlagPullBaseUrl (below) must be set to an IP — challenge pods use external DNS and can't resolve cluster/host names. There is also no in-place retain-restart and only egress challenges work end-to-end on virtual nodes.
Ad:* infrastructure keys
These configure the A&D supporting sidecars (flag delivery, WireGuard VPN, SSH jump host, checker concurrency). They are read directly from IConfiguration at runtime (e.g. IConfiguration["Ad:Vpn:ConfigDir"], IConfiguration["Ad:FlagPullBaseUrl"]) — there is no strongly-typed config class, so set them exactly as named.
Ad:Ssh:InternalSecret is the only thing standing between the ssh-jump sidecar and gzctf's internal lookup/exec endpoints. The shipped default (dev-only-rotate-me-before-prod) must be replaced for any real deployment — generate one with openssl rand -hex 32 and set the same value on both the gzctf and ssh-jump containers.
For the full deployment topology of these sidecars, see /guide/deployment/docker.
Per-game A&D settings (admin → Game → Info)
These are stored as columns on the Game row (Models/Data/Game.cs) and are event-wide: every A&D service in the game shares one tick, one flag lifetime, one warm-up, and so on. Rounds span the whole game.
Each field is editable in the admin Game → Info form via GameInfoModel. The fields are nullable in the edit model and only overwrite the stored value when the caller actually supplies one (Game.Update does if (model.X is { } v) X = v;), so leaving a field blank keeps the existing value.
The XML doc comment on Game.AdTickSeconds mentions 120 as an industry norm, but the actual code default is 60 (AdTickSeconds = 60). The other defaults match their comments. Trust the table above — these values are read straight from the field initialisers in Game.cs.
Runtime-only A&D state (not in the Info form)
Two Game fields are toggled by the engine / operator at runtime rather than configured ahead of time and are not part of GameInfoModel:
KotH scoring knobs (DB-only)
The two King of the Hill tuning parameters live on the Game row but are not exposed in GameInfoModel / the admin Game → Info form, and are not carried in the .gzevent manifest. Changing them requires a direct edit of the Game row in the database.
Because these are DB-only, they survive .gzevent re-scans untouched and cannot be set from the admin UI. To change them, update the Game row directly, e.g.:
See /guide/features/king-of-the-hill for how these feed into scoring, and /guide/features/scoring for SLA scoring details. Unlike SLA (which scales by sqrt(active teams)), KotH hold points are flat per held tick — KotH is a zero-sum race for one marker, so field size doesn't dilute it.
Per-challenge A&D settings
A&D challenge knobs live on the GameChallenge row and are authored in the ad: block of a challenge's YAML (alias mapping in ChallengeYamlModel.AdSection), or edited per challenge in the admin challenge form (ChallengeUpdateModel). They are only consulted when the challenge type is AttackDefense.
Tick length, flag lifetime, reset cooldown, snapshot-download, and the checker timing knobs (getflag jitter window + min grace period) are event-wide and live on the Game row / .gzevent manifest — they are deliberately not per-challenge, because rounds span the whole game. Set those in the game-level ad: block, not the challenge ad: block.
For the full challenge YAML schema (containers, flags, hints, provide files) and the .gzevent event manifest, see /guide/authoring/challenge-yaml and /guide/authoring/repo-bindings.