No description
Find a file
Astra 3c62874f91 Verification: clan tag setting, GroupVerify 0.3.0 wording
Root sets GroupVerify's clan_tag (the scoreboard tag CS2-Tags gives verified players without a
rank) next to the Steam group. Validated like the plugin (12 UTF-16 units, no quotes, ; or \),
saved to gv_settings and sent as css_gv_clantag when the plugin is loaded. Only changed fields
are sent.

GroupVerify 0.3.0 checks players with the Steam Web API as they join, so the group list is now
"Confirmed group members" with first/last confirmed dates.
2026-10-02 18:44:44 +01:00
internal Verification: clan tag setting, GroupVerify 0.3.0 wording 2026-10-02 18:44:44 +01:00
sql Rank log: inactive ranks (Guardian) count as promotions to and from, demotion when lost; Guardian is staff 2026-09-29 17:19:53 +01:00
.gitignore simpleadmin-web 0.1.0: web panel for CS2-SimpleAdmin 2026-09-25 20:08:14 +01:00
Containerfile simpleadmin-web 0.1.0: web panel for CS2-SimpleAdmin 2026-09-25 20:08:14 +01:00
example.env simpleadmin-web 0.1.0: web panel for CS2-SimpleAdmin 2026-09-25 20:08:14 +01:00
go.mod simpleadmin-web 0.1.0: web panel for CS2-SimpleAdmin 2026-09-25 20:08:14 +01:00
go.sum simpleadmin-web 0.1.0: web panel for CS2-SimpleAdmin 2026-09-25 20:08:14 +01:00
main.go Public rank change log: sa_rank_log, written on rank changes, listed on the Staff page 2026-09-29 16:56:37 +01:00
README.md Verification: clan tag setting, GroupVerify 0.3.0 wording 2026-10-02 18:44:44 +01:00
site.example.json Rank log: inactive ranks (Guardian) count as promotions to and from, demotion when lost; Guardian is staff 2026-09-29 17:19:53 +01:00

simpleadmin-web

A SourceBans-style web panel for CS2-SimpleAdmin (built against 1.8.2b's MySQL schema).

  • Public site (/): the live server (name, map, players, team score and a scoreboard), bans, gags and mutes, the staff list and its rank changes (promotions, demotions, who made them). Read-only. It never shows who issued a penalty, IP addresses or internal immunity values.
  • Staff panel (/admin): sign in through Steam. Ban, gag, mute, silence, warn and kick; lift bans and blocks; manage ranks, permissions and staff; change server cvars and SimpleAdmin's punishment settings.
  • Verification (optional): who our GroupVerify plugin verifies, with overrides to give and remove, and its settings. Only appears when GroupVerify is installed (see below).

Live data comes from the WebPanelBridge plugin (plugins/WebPanelBridge in this repo) over RCON. Without it the panel still works, but shows the server as offline and can't kick or warn.

How it works

  • Reading: straight from SimpleAdmin's MySQL tables (sa_bans, sa_mutes, sa_warns, sa_admins, sa_groups and their flag, unban and unmute tables, sa_players_ips).

  • Penalties: sent over RCON as SimpleAdmin's own commands (css_addban, css_addgag, css_unban, css_warn, …), so they take effect in game at once, with SimpleAdmin's usual announcements, Discord messages and IP handling. SimpleAdmin records console commands as "Console", so the panel then rewrites that row to credit the staff member who did it.

  • Game server unreachable: bans and blocks are written to the database directly, the way SimpleAdmin writes them. They apply when the player next joins (bans are also picked up by SimpleAdmin within about a minute). Kicks and warnings need the server.

  • Ranks and staff: written to the database, then css_reloadadmins. A rank is a SimpleAdmin group (#rank/mod); staff are admins holding exactly one such group. The rank editor offers the @css/... flags and our @zio/... ones (from the server's admin_overrides.json and our SimpleAdmin fork); a new @zio/ flag needs adding to knownFlags in internal/web/admin.go.

  • Permissions: commands sent over RCON run as Console, which skips all of SimpleAdmin's own checks, so the panel enforces them itself with the same flags:

    Action Needs
    Use the panel @css/generic
    Ban @css/ban; permanent or longer than MaxBanDuration also @css/permban
    Unban @css/unban
    Gag, mute, silence, lift them @css/chat; permanent or longer than MaxMuteDuration also @css/permmute
    Kick, warn @css/kick
    See IP addresses @css/showip
    Change map @css/changemap
    Server cvars @css/cvar
    See the Staff & ranks page @zio/webpanel
    Give and remove verification overrides @zio/verify (not on themselves, unless Root)
    Ranks, staff, SimpleAdmin settings, reload admins, GroupVerify's group and on/off @css/root

    @css/root grants everything. Nobody can act on a player whose immunity is higher than theirs, or on themselves, or give a rank with more immunity than their own.

Setup

  1. Install WebPanelBridge on the game server (see its README).
  2. Make sure RCON works: rcon_password set, and the panel can reach the game port over TCP. SimpleAdmin stores the server's address and RCON password in sa_servers, and the panel uses those unless SAW_RCON_ADDR / SAW_RCON_PASSWORD are set.
  3. Copy example.env to simpleadmin-web.env and fill it in. SAW_BASE_URL must be the exact public address; Steam returns signed-in users there.
  4. Put the panel behind HTTPS (a reverse proxy). Session cookies are marked Secure when SAW_BASE_URL is https.

Run it with podman:

podman build -t simpleadmin-web .
podman run -d --name simpleadmin-web --env-file simpleadmin-web.env \
  --user 1000:1000 \
  -v /srv/cs2/game/csgo/addons/counterstrikesharp/configs/plugins/CS2-SimpleAdmin:/data:Z \
  -p 127.0.0.1:8080:8080 simpleadmin-web

--user 1000:1000 matches the game container's user, which owns CS2-SimpleAdmin.json, so the panel can edit it. Mount it with :ro instead to make those settings read-only. Changes to that file apply the next time SimpleAdmin loads (a server restart). The panel keeps the previous version as CS2-SimpleAdmin.json.bak. It writes keys in alphabetical order, so expect a reordered file after the first save.

Or build and run it directly: go build && ./simpleadmin-web (Go 1.25, settings from the environment).

Rank names and colours

SimpleAdmin groups have no display name or colour, so those come from a site config (SAW_SITE_CONFIG, see site.example.json). The built-in defaults cover #rank/owner, senioradmin, admin, trialadmin, mod, trialmod, helper and guardian. Ranks marked "supporter": true (VIPs) are listed apart from staff and don't count as staff online. Ranks marked "inactive": true (Guardian: staff who are stepping back) are listed with the staff; they only change how the rank log reads (see below). Nobody can sign in without @css/generic.

Rank changes

Every change to a player's rank (their highest-immunity group) is recorded in sa_rank_log and listed under the public Staff page: who, from which rank to which, who changed it and when. A change is a promotion or demotion by the ranks' immunity, except for inactive ranks: moving between an inactive rank and any other rank is a promotion either way, and losing an inactive rank is a demotion. The panel creates the table at startup (sql/rank-log.sql, embedded; run it by hand if the database user can't create tables). It logs changes made in the panel (giving, moving or removing a rank, and deleting a rank that people hold). Our SimpleAdmin fork (1.8.2b-zio10+) logs css_addadmin, css_deladmin and the in-game admin menu to the same table, marked "in game". Not logged: ranks that lapse when their expiry passes, css_delgroup, and edits made straight in the database. History starts when the table is created.

Verification (GroupVerify, optional)

GroupVerify (plugins/GroupVerify in the cs2 repo) verifies players who are in the server's Steam group or have a manual override, and other plugins give verified players perks. It keeps everything in SimpleAdmin's database (gv_verified, gv_settings), so the panel needs no setup for it:

  • Staff get a Verification page as soon as GroupVerify's tables are in the database: whether it's on, the plugin's state and version, the Steam group, its saved member list, and the overrides. The player drawer shows why a player is or isn't verified. With @zio/verify (give it to the ranks that should have it in the rank editor), staff give overrides for a day up to a year or with no expiry, change them and remove them. Root sets the group and turns verification on and off.
  • Everyone sees a "Verified" tag on the scoreboard and on player pages, but only while verification is on (enabled, with a group set) and GroupVerify isn't missing from the server's plugin list. The public site never shows why someone is verified, or who gave an override.
  • Clan tag: Root can set the scoreboard tag verified players without a rank get (GroupVerify's clan_tag, at most 12 characters). CS2-Tags (1.0.4c-zio6+) applies it, and gives unverified players without a rank no tag at all. Blank turns it off.
  • Whether it's loaded comes from css_plugins list over RCON, read once a minute alongside the live status. While the server is down it's unknown, and that doesn't hide anything.
  • Changes: overrides are written to gv_verified and GroupVerify picks them up on its next reload (db_reload_seconds, 60 by default). Settings are written to gv_settings and, when the plugin is loaded, also sent as its own css_gv_group/css_gv_enable/css_gv_disable, so they apply at once.
  • Times in its tables are in GroupVerify's own timezone setting, which the panel follows. Keep it the same as SimpleAdmin's Timezone.

GroupVerify (0.3.0+) checks each player with the Steam Web API as they join, so "Confirmed group members" is everyone Steam has said is in the group when they joined, not the group's full member list. The panel never asks Steam itself.

Name history (optional)

SimpleAdmin keeps one row per SteamID + IP in sa_players_ips and overwrites its name on each connect, so a rename on the same IP loses the old name. sql/players-ips-name-history.sql adds name to that table's primary key, so every name a SteamID has joined with is kept and shows up on its player page and in name search. SimpleAdmin needs no changes. Run it once against the SimpleAdmin database; the file has the revert. Names are still only recorded at connect.

Limitations and caveats

  • Tested against a real MySQL 8.4 with SimpleAdmin 1.8.2b's migrations applied, and a fake game server that speaks Source RCON and imitates SimpleAdmin's console commands. Not yet tested against a live CS2 server or live Steam sign-in. Things to check on first run: that RCON returns WebPanelBridge's output (it prints through Server.PrintToConsole, like css version), and that the cvar values on the settings page read correctly.
  • In-game announcements for panel actions name "Console", because that's who SimpleAdmin sees. The database records the real staff member.
  • Unban and lift act on the player: css_unban <steamid> lifts every active ban on that SteamID, as it does in game.
  • Bans without a SteamID (IP-only bans from css_banip) are listed but can only be lifted in game.
  • One server per panel. Set SAW_SERVER_ID if sa_servers lists several.
  • Times are read in SimpleAdmin's Timezone (UTC by default), matching how it writes them.

Development

go vet ./... && go test ./...

internal/web/verify_db_test.go also runs the Verification endpoints against a real MySQL when SAW_TEST_DSN is set. It empties SimpleAdmin's admin and group tables and GroupVerify's, so give it a scratch database holding SimpleAdmin 1.8.2b's migrations and GroupVerify's Migrations/001_CreateTables.sql:

podman run -d --rm --name sa-test -e MYSQL_ROOT_PASSWORD=test -e MYSQL_DATABASE=sa -p 127.0.0.1:33061:3306 mysql:8.4
# apply the migrations, then:
SAW_TEST_DSN='root:test@tcp(127.0.0.1:33061)/sa?parseTime=true&loc=UTC' go test ./internal/web -run VerifyDB

The pages are plain HTML, CSS and JS in internal/web/assets, embedded into the binary. The design mockup they came from is in docs/mockup at the repo root.