Arena Fighter

Read as Markdown

Event achievements

Open Event achievements from My Bots, choose an event, and sign in to see your progress. Conditions and schedules are public; awards and flag delivery use your verified browser account session. Agent keys, bot tokens and MCP tokens cannot reveal flags.

After revealing a flag, follow How to submit flags on the selected event's page. Event staff supply the instructions and submission link; submissions are handled by the event's scoring site.

SAINTCON 2026 uses the HC qualifier window: October 27, 2026 at noon through October 30 at 6:00 AM, America/Denver. A qualifying match must start at or after opening and finish strictly before closing. The shipped event is disabled until the operator configures accepted flags and enables it.

Precon playtest and preregistration

The first SAINTCON precon runs continuously from Monday, October 12, 2026 at noon through Friday, October 16 at 6:00 AM, America/Denver. It uses SAINTCON's opening and closing hours, on the requested October 12–16 dates. Its event and ladder ID is saintcon-precon-2026-10-12. Preregister at the precon ladder with your Arena account; open enrollment admits players immediately before play opens. Bot matches remain unavailable until noon on October 12. Develop with the starter bots and simulator in the meantime, then queue with ARENA_LADDER=saintcon-precon-2026-10-12.

This online playtest helps players learn the game and build bots, with separate standings and five unofficial testing flags. All five achievements, house opponent requests, private reveal/copy, evidence and revocation use the normal achievement system. Testing flags are not part of the official HC challenge and have no HC submission link. The precon is enabled by default and uses flag_mode: "testing", which supplies stable, deliberately predictable ARENA_TEST values without HC secrets. A second precon may follow the next week; it is not scheduled yet. Precon enrollment, ratings and results do not enroll players in the October 27–30 main event or count toward its achievements.

Official and testing flags

An event's optional flag_mode is official (the default) or testing. Testing events automatically get five distinct ARENA_TEST{<event ID>:<achievement ID>} values. They stay stable across restarts, differ between events and achievements, and are explicitly unofficial. Only an earned, nonrevoked award with active membership can be revealed through the verified account route. Testing flags never borrow configured HC values or enable an official event without its secrets. Flag values do not appear in public definitions, progress responses or audits. The flag mode freezes with the scoring definition once earning begins.

Compose and .env.example enable the first precon by default. On an existing deployment, include saintcon-precon-2026-10-12 in ARENA_ENABLED_EVENTS, or clear a saved admin pause with Enable achievement earning. An explicit empty ARENA_ENABLED_EVENTS still disables all manifest earning. Accounts, --house, champion weights and approved opponent policies are required for testing too; only the external HC flag secrets are unnecessary. Local anonymous practice can set ARENA_ENABLED_EVENTS= to keep earning disabled.

Public event metadata

Definitions may include a metadata object with kind (precon, main, or community), purpose, audience, format, and preparation. All five fields are required when metadata is supplied. The event page and enrollment directory show these public details, alongside the schedule in the event's timezone. Keep secrets out of metadata. Older definitions without metadata remain valid. A practice event sets submission_url to null to omit flag submission links; otherwise the URL must use HTTPS. Metadata is frozen with the event definition.

Branding images

Each event may have an image object (url, alt, width, height) or null. The URL must use HTTPS, alternative text is required (1–300 characters), and normalized dimensions fit within 1600 × 900. Images appear above the event details and on enrollment cards. A manifest can supply an existing public image URL; older definitions without an image remain valid.

Admins can open /admin#events, choose the event from the list, and upload, replace or remove its branding image. Uploads accept nonanimated PNG, JPEG and WebP, up to 5 MiB and 40 million pixels. The server decodes the image, applies orientation, resizes without cropping or enlarging, strips source metadata and writes a WebP copy to R2 under events/<event ID>/<content hash>.webp. A descriptive alternative text makes the branding accessible.

Artwork can change after an event opens: it does not alter the frozen scoring conditions, roster, dates or awards. Admin image choices, including removal, override manifest images and survive earning toggles, restarts and manifest reloads. Removing an image detaches it from the event; previous R2 objects remain available for cached pages and recovery.

Configure a separate public asset bucket and its public HTTPS domain using ARENA_ASSETS_R2_ENDPOINT, ARENA_ASSETS_R2_BUCKET, ARENA_ASSETS_R2_ACCESS_KEY_ID, ARENA_ASSETS_R2_SECRET_ACCESS_KEY, and ARENA_ASSETS_R2_PUBLIC_URL. These are runtime settings; keep credentials out of manifests, browser configuration and build arguments. The ladder starts normally without asset settings; upload controls report unavailable until storage is configured. A partial configuration fails startup with the missing setting name. Do not make the private backup bucket public. See DEPLOY.md.

Five achievements

ID Achievement Required win
first-blood First Blood Beat any standard house bot.
bank-shot Bank Shot Beat any official house bot and land a damaging ricochet in that match.
wall-breaker Wall Breaker Beat any official house bot and cause a damaging repulsor wall slam in that match.
house-sweep House Sweep Beat each standard kind: TypeScript starter, brawler and kiter.
champion-slayer Champion Slayer Beat the official champion.

Awards are independent: one match can earn several. Progress combines all bots and versions belonging to your account, with one award per achievement per event. Attach your bot to your account before the match starts; later claims do not import old results. New versions, retirement and handle changes preserve earned awards.

Only ranked wins on the event's ladder_id against its approved official house versions count (the champion at any version: it keeps training through an event). Active enrollment must exist at start and completion; later admission cannot qualify an earlier match. Revoking enrollment also blocks future flag reveals. See multiple ladders. Results must be last_standing, timeout_hp or timeout_damage, with neither fighter disqualified. Human games, local simulation, uploaded replays, player opponents, draws, forfeits and voided games do not qualify. The account must be eligible at start and completion. All four official opponents must be connected with approved versions for the event to accept new qualifying starts.

Earned cards link to match evidence. Reveal a flag, then copy it to the event's submission site. Flags are fetched on demand and held in page memory; reload, logout or a session/account change clears them. Closing or disabling an event stops earning, but earned flags remain revealable while their server secrets are available. Staff revocation blocks subsequent reveals.

Request an opponent

Join the event ladder at Events, receive admission, and queue with ARENA_LADDER=<event ladder ID>, then choose an official opponent on the event page. One pending request is allowed per account across events. It expires after ten minutes or at event closing, whichever is sooner. Requests and requested starts have a one-minute account cooldown, including after cancellation and restart. Disconnecting or leaving the queue cancels that bot's pending request.

A request reserves your selected bot; your other bots continue normal matchmaking. It is a preference: normal quotas, pacing, capacity, owner exclusions and rematch holds still apply. Requested games are ranked, with server-selected maps and seeds and balanced seats. The scheduler admits one initial preferred start; four ordinary ranked starts earn another, up to four credits. Requests can use otherwise idle capacity. Requests are considered in order, skipping unavailable selected bots or opponents.

Operator setup

The CLI loads events/saintcon-2026.json and ladders/default.json by default. The first precon uses open enrollment on saintcon-precon-2026-10-12. SAINTCON uses the separate saintcon-2026 ladder with staff approval. Its opponent names in the manifest belong to that ladder; Public practice wins do not count. Use serve --events <path> or ARENA_EVENTS_FILE for another public JSON manifest (an array of definitions). ARENA_ENABLED_EVENTS overrides each definition's enabled value with a comma-separated list of IDs; an empty value disables all listed events. Every enabled ID must exist in the manifest. Testing events need no ARENA_EVENT_FLAGS entry; official events still require all five distinct configured values. In Compose, the path is inside the container; leave it empty to use the bundled preset.

Configure Supabase accounts and start with --house. Approved versions for SAINTCON are starter-17, brawler-18 and kiter-17; the manifest names the champion's l3-6420, but the champion counts at any version. Set the runtime-only secret ARENA_EVENT_FLAGS to a JSON object with this structure, replacing each placeholder with a distinct accepted value supplied by HC:

{
  "saintcon-2026": {
    "first-blood": "<accepted First Blood flag>",
    "bank-shot": "<accepted Bank Shot flag>",
    "wall-breaker": "<accepted Wall Breaker flag>",
    "house-sweep": "<accepted House Sweep flag>",
    "champion-slayer": "<accepted Champion Slayer flag>"
  }
}

These are placeholders, not playable flags. Keep real values in the deployment secret store, never in the manifest, source, build arguments or a VITE_* variable. The CLI removes this variable from house bot child environments. Flags are absent from SQLite, replays, statistics, public views and audits. Back up the secret store separately: restoring the database alone cannot restore flags.

Set ARENA_ENABLED_EVENTS=saintcon-2026 after configuration. An enabled event without accounts, complete flags, --house or champion weights doesn't stop the ladder: it starts with that event's earning paused and logs why. Check GET /api/events: ready must be true and unavailable_opponents empty once all house bots are connected with approved versions. Opening and closing follow the manifest's UTC instants automatically.

Releases don't depend on events: an enabled event doesn't pin the code, rules or maps, so fixes and a retrained champion can ship while it runs. Combat changes are deliberate, announced releases (rules_hash and the engine version in /api show what's running). Definitions freeze once matches start or an enabled event opens; enabled can still toggle for an emergency pause. Use a new ID for changed conditions. Removing a definition preserves its stored event and awards, so explicitly disable it first. An enabled stored event still requires its secrets at startup; retain finished events' flag secrets for reveals.

For future events, add new IDs with their own dates, approved opponents and flag sets. The current revision, house-v1, uses these same five conditions. Distinct IDs keep awards and progress separate. New conditions need a reviewed revision and evaluator, rather than editing a running event.

Evidence and recovery

The server snapshots ownership, seats, house identity and versions before starting the runner. It commits match results, statistics, progress and awards together. Duplicate completion cannot double count matches or awards. Event facts remain available after replay trimming.

The admin console's Events section lists each ladder with the events that play on it. An event's controls show official opponent and flag readiness, and valid/revoked award counts per unique player account. Staff can enable or disable earning; these choices persist across manifest reloads. Enabling requires approved house policies, configured accounts and all five flags. Flag values remain server-only. Hidden ladders' events are omitted from public discovery; their account achievement routes remain usable.

An event's Schedule editor moves its opening and closing times, entered in the event's time zone. An event on an event ladder shares the ladder's window, so saving moves the ladder (and its other events) too. Staff can move only future times: once play opens the opening time is final, once it closes the window is final, and a new time must be in the future, so recorded matches and awards keep the window they were judged by. Moved windows outrank the manifest on restart.

The admin console's Events section also includes a Flag submission editor for the public submission URL and instructions, with a player preview. URLs must use HTTPS or be left blank. Instructions allow up to 4000 characters, line breaks, HTTP(S) URLs and [label](https://…) links; HTML stays plain text. Leave both fields blank to hide the submission section. Do not include secret flag values. Manifest submission_instructions is optional and defaults to an empty string. Admin edits override manifest submission details across restarts and remain editable after scoring definitions freeze.

The Events tab also inspects an account's evidence and revokes awards with a reason. Staff cannot reveal somebody else's flag. Reconciliation previews missing awards, then fills them from retained facts, preserving existing awards, original timestamps and revocations. It does not change matches, ratings or counters. It cannot import pre-event matches or invent start ownership evidence.

For offline recovery, stop the ladder and use its data directory:

npm run arena -- events list --data /path/to/data
npm run arena -- events reconcile saintcon-2026 --data /path/to/data
npm run arena -- events reconcile saintcon-2026 --apply --data /path/to/data

The first reconciliation is a dry run. Back up the database before applying a repair. The admin HTTP route supports the same preview/apply flow while running. SQLite schema 14 adds event records; rollback needs a compatible database backup.

Scoring modes

This release delivers one shared flag per achievement per event. Private delivery limits who can retrieve it from Arena; HC determines scoring for submitted copies. Arena revocation cannot retract a copied shared flag or reverse a score at HC.

EventFlagProvider separates evaluation from delivery. Contestant-bound flags remain a future adapter: HC must confirm verified enrollment and a validator tied to that identity, with stable event-specific flag derivation. Arena handles alone do not establish HC identity. Both options are described in the design; only shared mode ships here.