Licensing

Seats, activations, and transfers.

Modern Control — Licensing

Honest summary: client-distributed Lua cannot be made impossible to copy. Security comes from per-installation credentials and server-side license activation — not from obfuscation.

Architecture

Organization
 └── License (status, plan, serverAllowance)
      └── LicenseActivation (serverId, installationId, status)

Dashboard/backend is authoritative.

States

| Status | Agent auth |

|--------|------------|

| ACTIVE | Allowed |

| GRACE | Allowed (post-expiry grace window) |

| SUSPENDED | Denied |

| REVOKED | Denied |

| EXPIRED | Denied (unless GRACE) |

Customer data is not deleted on expiry/revoke.

Installation identity

At successful pairing the backend mints installationId (inst_… random).

This is not player HWID and not a brittle hardware fingerprint.

Optional supporting signal: lastHostHint from agent hostname (non-authoritative).

What possession of addon files grants

Nothing operational. Without a valid pairing → permanent secret + ACTIVE license activation, the dashboard rejects the agent.

Copied config

A copied permanent secret can authenticate until rotated/disconnected. Mitigations:

  • unique secrets per install
  • disconnect / rotate / transfer workflows
  • conflict flags when the same installationId is seen on multiple ACTIVE activations

IP changes alone do not revoke a license.

Host migration

Owner uses License → Transfer host (or POST /api/licenses/transfer):

  1. Old credentials invalidated
  2. Seat retained
  3. New pairing code issued
  4. Paste config on new host → pair → online

See docs/CUSTOMER_INSTALL.md for the full step-by-step transfer procedure.

Outages

If Modern Control is unreachable, GMod gameplay continues. Telemetry/commands pause. Temporary unreachability is not treated as piracy.

Operator procedures

Platform SUPERADMIN with x-mdn-operator-access: 1:

  • GET/POST/PATCH /api/operator/licenses — issue / suspend / revoke / restore

Customers never receive these capabilities.

Cache

License resolution cached ~15s per server (LICENSE_CACHE_TTL_MS). Status changes clear relevant cache entries.

Privacy

Stored: org, license metadata, server id, installation id, optional host hint, activation timestamps.

Not stored: player hardware IDs, invasive machine fingerprints.

Agent version compatibility

The dashboard evaluates each connected agent's version and surfaces one of four labels:

| Label | Meaning |

|-------|---------|

| CURRENT | Agent is on the expected major.minor — no action needed |

| UPDATE AVAILABLE | Agent is 1–2 minor versions behind — update recommended |

| INCOMPATIBLE | Agent is too old — full functionality not guaranteed; update required |

| UNKNOWN | Version could not be determined or agent is ahead of expected |

The compatibility baseline is controlled by EXPECTED_AGENT_VERSION_PREFIX on the dashboard server. Current default: 0.9.