ATG Link

Connects a server to the ATG API — identity, account linking and moderation.

What the mod does

ATG Link connects an Arma Reforger server to the ATG API — the service behind ATG’s Discord integration. Three things rest on that connection:

  • Identity. The API establishes that the server at a given address really is the one its owner enrolled. Everything else rests on this, so nothing works until it succeeds.
  • The gate. A server can require that players have linked their game account to Discord before they are allowed to deploy, or merely tell them how.
  • Moderation. A ban issued from Discord reaches the server, and the server hands it to Bohemia’s ban service.

The mod ships inert. With nothing configured it registers its commands, reports that it is not configured, and does nothing else. It never contacts anything on its own — the API opens the connection, over RCON, and this mod only answers.

Installing it

Load ATG Core and ATG Commands before this mod. Both are found by load order rather than by being declared as dependencies, so a server that orders this mod ahead of either comes up with no commands at all — and with no commands, nothing can reach it.

ATG UI is optional and is found the same way. Loaded before this mod, the gate and the nag appear on screen as a card as well as in chat — which matters, because a player sitting on a deploy screen does not read chat. Loaded after it, or not at all, this mod says the same things in chat alone and nothing else changes.

In your mod list the order should read ATG Core, ATG Commands, ATG UI, ATG Link.

The server writes one line at startup saying which side of ATG UI it landed on, so a mod list in the wrong order is visible rather than being mistaken for the feature not working:

[ATG_Link] ATG UI is loaded ahead of this mod - the gate is shown on screen as well as in chat
[ATG_Link] ATG UI is not loaded ahead of this mod, so the gate speaks in chat alone.

Settings

Every setting lives in one JSON file under the server’s profile directory, at ATG/atg-link.json:

{
  "m_sRegistrationSecret": "00000000-0000-0000-0000-000000000000",
  "m_sGateMode": "off",
  "m_bGateFailOpen": true
}
SettingDefaultMeaning
m_sRegistrationSecretnoneThe value you were issued when you enrolled this server.
m_sGateMode"off"What the server does about a player who has not linked.
m_bGateFailOpentrueAdmit a player when the check itself could not be completed.

This is not an ATG Core mod config, and that is deliberate: a config registered there is sent to every joining client, and the registration secret is the server’s own credential. Reading the file directly keeps it off the wire by construction.

The whole file is read once at startup, on the server only.

The registration secret

The value is a UUID in 8-4-4-4-12 form, exactly as it was issued to you. A value in any other form is refused rather than used, because a mistyped secret would otherwise produce a well-formed answer that proves nothing. Whitespace either side of it is trimmed, so a stray space pasted in with the value is harmless.

It is never sent to a client.

The gate

m_sGateMode takes one of three words. It is a word rather than a number because you are editing this file by hand and should be able to read your own config back.

  • "off" — admit everybody and ask nothing.
  • "nag" — admit everybody, and tell the ones who have not linked how to.
  • "block" — turn away anybody who is not linked and in good standing.

Anything else is treated as "off" and reported as a warning naming what you wrote. The two states worth defaulting to are the two that cannot lock anybody out, and silently picking one over the other would hide the typo.

The gate applies at the spawn, not at the connection. A player who is turned away can still join the server and look around; what they cannot do is deploy. The check is subtractive — whatever the base game or another mod already refuses stays refused, and this can only ever take a spawn away.

What the player is told

Everything the gate says goes to chat, which is the record a player can scroll back to. Where ATG UI is loaded ahead of this mod, the same words also appear on screen as a card.

Three cards, by key:

KeyShown when
link-nagA player who has not linked is being told how, and admitted anyway.
link-gateA player is being turned away.
link-unavailableThe check could not be completed and m_bGateFailOpen is off.

Write those keys in ATG UI’s card list and the gate uses them; leave them out and it stays in chat. The wording of the card is yours, and a registration code the API supplies is appended to whatever you wrote.

A card already on screen is replaced in place when the code arrives a moment later, rather than a second card stacking behind it.

A card is presentation, not enforcement. The refusal happens on the server whatever the player does with the card, so a card authored with no dismiss button is not what stops them spawning.

The nag is shown once per session. A refusal is shown on every spawn attempt.

With the gate set to "nag" or "block", ATG UI’s message of the day stops carrying the server’s Discord QR code. The players reading it are already being sent to Discord to register, so the invite would be putting the same thing in front of them twice.

m_bGateFailOpen decides what happens when the API cannot be reached or does not answer in time. It ships true: a server that locks its whole membership out because a web service blinked is worse off than one that briefly admits somebody unregistered. Set it false only if you would rather nobody played than somebody unregistered did.

A bool here is written true or false. A 1 is silently ignored and the setting keeps the value it already had.

Where the file goes

$profile: resolves to a profile folder inside the directory you pass to -profile, not to that directory itself. If you launch with -profile C:\servers\atg, the file belongs at C:\servers\atg\profile\ATG\atg-link.json.

A file one level up is simply not found, and the server reports that it holds no registration secret without saying why — so this is the first thing to check when the secret appears to have been ignored.

Checking it was read

The server writes one line at startup saying what it found:

[ATG_Link] Registration secret read from $profile:ATG/atg-link.json
[ATG_Link] No $profile:ATG/atg-link.json - this server holds no registration secret
[ATG_Link] $profile:ATG/atg-link.json holds no registration secret

It then states the gate settings on the line after, whether or not you set them:

[ATG_Link] Gate mode off, fail-open 1

Both are stated at startup rather than only when the gate first refuses somebody, so an owner who has set them wrongly finds out while they are still looking at the console.

A file that exists but cannot be parsed is reported as a warning naming the file — a trailing comma or an unquoted key is the usual cause, and none of the file is applied when that happens.

How much this mod writes to the log follows ATG Core’s own verbosity setting, in ATG/atg-core.json alongside this file. There is no separate dial for this mod.

Commands

These appear under /atg, from ATG Commands.

CommandWho can run itWhat it does
/atg linkGame MasterReport that the mod is loaded and reachable
/atg verifyAdministratorAnswer an identity challenge from the API
/atg endpointRCON onlyTake the API’s address and a short-lived token
/atg banAdministratorBan a player through Bohemia’s ban service

/atg verify takes the challenge as its argument and replies with the answer alone. It is there for the API to call rather than for a person to type, but it is worth knowing it exists: an administrator running it by hand is how you confirm the secret is being read at all.

/atg endpoint takes a URL and a credential, and is refused from chat entirely — not merely as a privilege question. A chat panel is a shared surface, and a credential typed into one is a credential in whatever records that traffic. Both values are held in memory and go no further. The credential is short-lived, so the API re-pushing it is ordinary rather than a sign of anything wrong.

/atg ban takes a Bohemia GUID, a duration in whole seconds (0 for permanent) and a reason, which is everything after the duration:

#atg ban 0123456789abcdef0123456789abcdef 3600 Shooting civilians in the market

The duration has to be a whole number of seconds — 30m and 7d are refused rather than read as 30 and 7. The reply says the ban was dispatched, not applied: Bohemia’s backend answers on its own schedule, and what confirms the ban is what the mod posts back to the API once the backend accepts it.

Run from chat, the ban records the administrator who issued it. Run over RCON the engine says nothing about who is asking, so it goes out with no actor named and the API records who asked from its own side. That matters more than attribution: the API refuses to let whoever imposed a sanction hear the appeal against it, and that check compares identities.

Over RCON

Every command above is reachable over RCON at monitor level, which means they work whichever way your server’s rcon permission is set. Use the keyword that matches it — #atg on a server configured "permission": "admin", #atgmonitor on one configured "permission": "monitor". See ATG Commands for why the keyword has to match rather than being outranked.

Monitor level throughout, including for ban and endpoint, and that is a deliberate choice rather than an oversight. A keyword’s declared level has to match the listener’s rather than being outranked by it, so an administrator-level claim would be reachable only on a server whose RCON is configured for admin — and a server configured for monitor could never prove its identity or act on a moderation request at all.

What stands between a stranger and any of it is your RCON password, which is the same thing standing in front of the game’s own ban command.

This matters more here than elsewhere: the API reaches the server over RCON and nothing it does is permitted until the server has proved its identity, so a server that cannot answer a challenge cannot be driven at all.

What crosses the connection for a challenge is an answer derived from the secret — never the secret. The caller already holds your RCON password before they can ask, so answering a challenge tells them nothing they could not otherwise obtain.