RE:CORD
REC_PlayerManager

Browser route

Welcome to REC_PlayerManager documentation.
Added
Updated
This screen is served on the same port your players connect to (30120 by default). You cannot close that port, so what actually protects the panel is allowedAddresses (this machine only out of the box) and the token.

Reach the panel from an address you have allowed. Any one of these works:

  • Reach it over a VPN (Tailscale, WireGuard)
  • Reach it through an SSH port forward
  • Put a TLS reverse proxy in front of it (see trustedProxies)
  • Only reach it from the same LAN

If you are on shared hosting and can do none of these, leave the browser route off and use the in-game screen.

Enabling it

config.web.http.enabled = true

Tokens come from convars, and one pair covers every RE : CORD panel at once:

  # every RE:CORD panel
  set REC:adminToken "a-random-string-of-32-chars-or-more"
  set REC:supportToken "another-one-for-read-only-access"

  # this panel only, overriding the shared one
  set REC_PlayerManager:supportToken "a-third-one"

48 characters, generated in your browser. Nothing is sent anywhere.

adminToken carries *, supportToken carries read. An empty token makes every /api/* call return 401. A token shorter than 32 characters prints a warning on startup.

For something narrower than those two, add a named token and give it its own convar:

customTokens = {
    { name = "playersOnly", scopes = { "players" }, },
}
  set REC_PlayerManager:token:playersOnly "yet-another-random-string"
Every write the panel let through, and every one it refused, is recorded in rec_web_audit with the token label behind it. That table ships with REC_Library as well. Without it the trail stays in memory and a warning is printed on startup.

allowedAddresses

Requests from an address that is not on this list get 404 — before authentication, so the route stays invisible from the outside. The default allows this machine only.

  # every RE:CORD panel
  set REC:allowedAddresses "192.168.1.0/24"

Or replace the list outright in the config, keeping loopback in it:

allowedAddresses = {
    "127.0.0.0/8",        -- loopback
    "::1",
    -- "100.64.0.0/10",   -- Tailscale (CGNAT range)
    -- "192.168.1.0/24",  -- LAN
    -- "172.18.0.0/16",   -- Docker bridge
}

Entries are IPv4 CIDR blocks or literal addresses. IPv6 has no CIDR support, so write the address in lowercase canonical form. An entry that can never match is reported on startup.

Adding 0.0.0.0/0 publishes the panel to the entire internet, leaving the token as the only protection.

trustedProxies

Behind a reverse proxy every request arrives from the proxy itself, so the address check can no longer see the real client. Register the proxy instead:

trustedProxies = {
    "127.0.0.1",
}

Requests from a registered proxy skip the address check, which means access control becomes the proxy's job — configure authentication there (Caddy basic_auth, nginx allow / deny, Cloudflare Access). X-Forwarded-For is only written to the debug log, never used to authorise, because any client can forge it.

If you run a tunnel such as cloudflared or ngrok on the same host, every visitor arrives as 127.0.0.1. The default loopback entry then no longer means "this machine only" — add authentication at the tunnel.

Common setups

SetupConfigNotes
In-game onlyDefaultsNothing to configure
SSH port forwardenabled = true onlySource is loopback, already allowed
Tailscale / WireGuardAdd 100.64.0.0/10Or the single device address
Same LANAdd your LAN rangee.g. 192.168.1.0/24
Reverse proxy + HTTPStrustedProxies = { "127.0.0.1" }Authentication must be set on the proxy
The browser route speaks plain HTTP — FiveM cannot terminate TLS. The token travels unencrypted, so only use this route inside an encrypted path (VPN, SSH tunnel, or a TLS reverse proxy).

Endpoints

MethodPathScope
GET/REC_PlayerManager/api/sessionWhat your token may read and write
GET/REC_PlayerManager/api/summaryplayers:read
GET/REC_PlayerManager/api/playersplayers:read
GET/REC_PlayerManager/api/players/<playerId>players:read, identifiers included
POST/REC_PlayerManager/api/players/<playerId>/kickplayers:write, { reason }
POST/REC_PlayerManager/api/players/<playerId>/messageplayers:write, { message }
POST/REC_PlayerManager/api/players/<playerId>/bucketplayers:write, { bucket }
GET/REC_PlayerManager/api/historysessions:read
GET/REC_PlayerManager/api/sessionssessions:read
GET/REC_PlayerManager/api/playtimesessions:read
GET/REC_PlayerManager/api/heatmapsessions:read
GET/REC_PlayerManager/api/actionsserver:read, the admin log
POST/REC_PlayerManager/api/maintenanceserver:write, { enabled }
GET/REC_PlayerManager/api/settingssettings:read
POST/REC_PlayerManager/api/settingssettings:write
POST/REC_PlayerManager/api/settings/resetsettings:write
GET/REC_PlayerManager/locale.jsonUI strings, address check only

Everything under /api/* needs the address check and a token carrying the scope. A token that passes the address check but not the scope gets 403 rather than 404: the route exists, your token is just not allowed to use it. Every POST also needs readOnly to be off.

©2026 RE:CORD Development Group. All rights reserved.