MoH Arena GUARD
Login
Server Owner Guide

Add MoH Arena Guard to your existing server.

You already run an AA, Spearhead, or Breakthrough server on Linux. This guide adds Guard on top of it: drop in the HUD PK3, paste a few cvars into server.cfg, add two lines to your precache script, and run one installer that sets the Python sidecar up as a systemd service — it survives reboots, restarts itself, and logs to journald. Nothing about your existing server changes — not the launcher, not the binaries, not your firewall.

What this guide assumes

Your server runs

You already start the game and it accepts players. Guard doesn't manage the game process for you.

It's Linux

The sidecar is Python and the optional firewall layer uses nft. Ubuntu 24.04 is the tested target.

You have RCON

rconPassword "..." is set in server.cfg. The sidecar uses it to kick; the password never leaves your box.

Where the files should live

Where things end up

You extract the bundle anywhere (your home directory is fine) and run the installer from there. The installer copies the sidecar to a fixed system location and keeps your secrets in a root-only config file — never inside the game folder.

/home/your-user/
  moharena-guard-server-owner/        # extracted bundle — run the installer from here
    install-systemd.sh                # the one script you run (as root)
    server-owner-install.sh           # apt packages helper (the installer calls apt too)
    sidecar.py                        # copied to /opt by the installer
    moharena-guard@.service           # systemd unit template

  your-existing-game-server/
    main/ | mainta/ | maintt/
      qconsole.log                    # game log stays with the game
      zzzzzz_stats*.pk3               # optional private stats
      zz_moharena_ac_hud_server*.pk3  # copied here

/opt/moharena-guard/sidecar.py        # installed sidecar (self-updates live here)
/etc/moharena/guard-<game>.env        # your settings — root-only, mode 600
/etc/systemd/system/moharena-guard@.service

The handoff with mtc

You mtc

Send mtc a DM with these five values

  • Your Discord user IDSo mtc can grant you owner access on the Guard website.
  • A name for the serverShown in the Guard console — "VSUK Spearhead Public #1", etc.
  • Game + runtimeOne of aa-original, aa-openmohaa, sh-original, sh-openmohaa, bt-original, bt-openmohaa.
  • Public IP + game porte.g. 203.0.113.10:12205. The token mtc generates is bound to this exact address.
mtc You

mtc sends back three secrets

  • Guard server IDAn integer like 7. You'll paste it as MOHARENA_GUARD_SERVER_ID.
  • Sidecar tokenStarts with mohg_…. Shown on the website — treat it like a password.
  • Server addressThe same <ip>:<port> you sent — mtc echoes it back so you paste the exact string.

The five steps

  1. Download the bundle and put it on your box

    The bundle contains the installer (install-systemd.sh), the systemd unit template, sidecar.py, and the server PK3s. Download it straight onto the VPS (as whatever user already runs your game):

  2. Drop the Guard PK3s into your game

    Copy zz_moharena_ac_hud_server.pk3 into the same folder as your existing paks — the main/ folder for AA, mainta/ for SH, or maintt/ for BT (use the OpenMoHAA equivalents if you run that). Add zzzzzz_stats.pk3 only if you want private server stats.

  3. Wire the scripts into your precache

    Open your precache pk3 (the one that holds maps/dm/precache.scr or your equivalent) and append the lines below. The HUD script is the important Guard piece; the stats script is optional and only needed for private server stats.

  4. Add the Guard cvars to your server.cfg

    These default to a sensible "AC visible, no kicks" posture — you can change the policy later from the Guard website without restarting anything. Paste them at the end of your existing server.cfg.

    Logging cvars Guard depends on. The sidecar reads qconsole.log, so the engine has to actually write the connect / chatter lines it parses. Add these too (the first three are required; logfile just controls flushing):

    logfile values: 0 = off, 1 = on but buffered (the newest lines can be lost if the server crashes), 2 = on and flushed after every line. Any value above 1 behaves the same as 2, so 2 and 3 are equivalent. Use 2 (or 3) so Guard sees events in real time — 1 works but can lag behind.

    qconsole.log must be where Guard expects it. Point MOHARENA_QCONSOLE (next step) at the file the engine actually writes — usually your game's main folder (main/ for AA, mainta/ for SH, maintt/ for BT). If your engine writes it somewhere else (e.g. the server root), symlink it into place so both paths point at the same file:
    ln -s /home/mohaa/server2/qconsole.log /home/mohaa/server2/main/qconsole.log

  5. Run the installer — the sidecar becomes a systemd service

    Pass the three values from mtc plus your existing RCON password, qconsole.log path, and game as flags. It installs and starts moharena-guard@<game> — which survives reboots, restarts itself after crashes and self-updates, and logs to journald.

    --server-cfg-path points at your server.cfg. Set it now — it's what powers the Config tab on the website (view and edit your live config from the dashboard). Without it that tab can't fetch anything.

    Recovery copies. The first time you save an edit from the Config tab, Guard keeps two backups next to your server.cfg so you can recover over SSH if an edit ever breaks the server: server.cfg.moharena-bak (the pristine original) and server.cfg.moharena-prev (the version from just before your last edit).

    --runtime is original for the retail game (the default) or openmohaa if you run the OpenMoHAA engine. It must match how you registered the server with mtc.

    Prefer not to type flags? Open install-systemd.sh, fill in the CONFIG block at the top (same field names), and run sudo ./install-systemd.sh with no flags. Migrating from the old tmux wrapper? See MIGRATION.md in the bundle.

    Several servers on one box? Run the installer once per server. Different games are automatic (each --game is its own service). For two servers of the same game (e.g. two aa servers), give each a distinct --instance name — otherwise the installer stops to stop you overwriting the first:
    sudo ./install-systemd.sh --game aa --instance aa1 --server-id 7 …
    sudo ./install-systemd.sh --game aa --instance aa2 --server-id 8 …

    About the firewall layer. Guard blocks players two ways: Layer 1 RCON-kicks (always on, no setup), and Layer 2 kernel-level packet drops via nftables. With the systemd service this just works: the unit grants the sidecar the one kernel capability nft needs (CAP_NET_ADMIN), so there is no sudoers entry and no running as root. Set MOHARENA_GUARD_FIREWALL_BLOCKS=0 in the env file if you want RCON kicks only.

    All your settings live in /etc/moharena/guard-<game>.env (root-only, mode 600) — the same options the old wrapper had, now with comments. Edit any value, then apply it with sudo systemctl restart moharena-guard@<game>:

  6. Check it on the Guard website

    Open your server's page on Guard. The Overview tab shows the last heartbeat (should refresh every few seconds). Connect a player and the Players tab fills in within 10 seconds. The in-game HUD reads AC Active — N/M.

    Open Guard servers Heartbeat refreshes every few seconds Policy changes apply within ~30 s, no restart

Day-to-day commands (optional)

Here are some useful commands that you might want to use.

systemctl status moharena-guard@shIs the sidecar running? Swap sh for aa/bt on those games.
journalctl -u moharena-guard@sh -fFollow the live sidecar log.
journalctl -u moharena-guard@sh | grep FIREWALL_LAYERDid the Layer 2 nftables probe succeed at startup?
sudo nft list table inet moharena_guardShow the live kernel set of dropped IPs/CIDRs.
sudo systemctl restart moharena-guard@shApply changes after editing /etc/moharena/guard-sh.env or rotating the token.
sudo systemctl stop moharena-guard@shStop the sidecar. start brings it back; it also starts on boot.

FAQ

What exactly does Guard need from me, in one sentence?

Two PK3s in your pak folder, ~10 lines in server.cfg, 4 lines in your precache script, and one installer run (sudo ./install-systemd.sh) that sets the sidecar up as a systemd service with your RCON password.

Do I need to give mtc shell access to my box?

No. Setup is entirely self-serve. mtc only registers a Guard record on the website and DMs you three values. Nothing on your machine talks to mtc directly — the sidecar talks only to guard.moharena.com.

Does Guard touch my RCON password?

No. The password stays in /etc/moharena/guard-<game>.env on your VPS — readable by root only. The sidecar uses it locally to issue kicks. The website never sees it.

Will Guard kick non-Guard players?

Only if you tell it to. The defaults in this guide are "AC visible, no kicks". You can change the policy on the website's Policy tab. Changes apply within ~30 seconds without restarting anything.

What's the difference between Blocks and Rules?

Blocks are exact-match, per-player or per-IP, manual entries (one button on the Players tab). Rules are patterns — IP exact, IP range, CIDR, wildcard prefix, country code, ASN, or "every VPN/proxy IP". A matching rule with action Permanent block auto-creates a block.

The in-game HUD reads "AC Inactive" even though the sidecar is running.

That means the sidecar is up but the website's live policy isn't asking for AC enforcement. Open the server's Policy tab and tick Show "AC Active" badge in-game. The HUD flips within ~30 seconds.

The HUD shows my Mac as AC-verified even though I'm not running Guard.

Your client has the auth_token cvar saved from a previous launcher session. The sidecar now only marks players as verified when the launcher is actively heartbeating — if you see this, make sure you are on the latest sidecar (mtc can push it remotely, or update by hand) and run sudo systemctl restart moharena-guard@<game>.

Does the sidecar run as root?

No. The installer runs as root once (to write the system files), but the service itself runs as your game-server user. The nftables firewall layer works anyway: the systemd unit grants the one kernel capability nft needs (CAP_NET_ADMIN) — no sudoers entry, no root process.

What's MOHARENA_GUARD_FIREWALL_LAYER mode=rcon-only?

The sidecar tried nft at startup and couldn't call it. RCON-kick still works fine; you just don't get the kernel-level packet drop. With the systemd service this shouldn't happen (the unit grants the capability and the right PATH) — check nft --version is installed, or set MOHARENA_GUARD_FIREWALL_BLOCKS=0 in the env file to silence the message.

How do I update the sidecar later?

Usually you don't have to do anything — mtc pushes releases from the website, the sidecar swaps its own file, and systemd restarts it on the new code within seconds. To update by hand: replace /opt/moharena-guard/sidecar.py with the latest from the bundle and run sudo systemctl restart moharena-guard@<game>. To refuse remote pushes entirely, set MOHARENA_SIDECAR_AUTO_UPDATE=0 in the env file.

How do I roll back if something breaks?

Run sudo systemctl disable --now moharena-guard@<game>, delete the Guard PK3s from your pak folder, and comment out the Guard exec lines you added to precache. Your server is back to its pre-Guard state. The Guard cvars in server.cfg are harmless when the sidecar isn't running.

I'm stuck. What should I send mtc?

Send the output of journalctl -u moharena-guard@<game> -n 50 --no-pager, the Guard server ID, and whatever the in-game HUD reads. Don't paste your mohg_... token or the RCON password.