# FiveM resource (fivedash)

Architecture, install, every option in config.lua, loja.lua and logs.lua, convars, authentication, sync, protocol and updates of the fivedash resource.

Source: https://fivedash.fivex.gg/docs/en/developers/fivem-resource

`fivedash` is the resource that links your city to FiveDash. It runs on the FXServer server side, detects the framework on its own and applies in game what the panel decides: it grants and removes whitelist, delivers store purchases and sends your scripts' logs. This page is for server admins and script developers who want to understand how it works inside.

## Before you start

* **FXServer with Node 22:** `fxmanifest.lua` asks for `node_version "22"`. Use a recent FXServer artifact.
* **MySQL database (for whitelist and store):** one of these drivers started: `oxmysql`, `ghmattimysql` or `mysql-async`. Without a driver, the panel connection works, but the whitelist stays on Discord and the store doesn't deliver.
* **Server key:** generated in **Server → Overview → Game connection → Connect server → Generate key**. Only the organization's **Owner** and **Admin** can generate it.
* **Folder name:** keep the folder named `fivedash`. Scripts call `exports.fivedash:…` and check `GetResourceState("fivedash")`.

## Architecture

```text
City (FXServer)                                    FiveDash
┌───────────────────────────────┐   HTTPS (out)   ┌──────────────────────┐
│ fivedash/server/main.js       │ ──────────────▶ │ POST /api/fivem/sync │
│  ├─ framework detection        │ ◀────────────── │  pending commands    │
│  ├─ Discord module (whitelist) │                 └──────────────────────┘
│  ├─ Store module (deliveries)  │   HTTPS (out)   ┌──────────────────────┐
│  └─ Logs module (export)       │ ──────────────▶ │ log service          │
│ City MySQL (oxmysql…)          │                 └──────────────────────┘
└───────────────────────────────┘
```

* **Server only:** all the code runs in `server/main.js` (a single bundle). `client/main.js` is empty: nothing runs in players' games, and the resource registers no network events (`RegisterNetEvent`). Players can't send it commands or logs.
* **Always outbound:** the resource calls the panel; the panel never calls the city. No ports to open, no IPs to allow.
* **Framework-independent:** the resource depends on no framework. It finds out which one the city runs and talks to it through exports, events and the database. See [Frameworks](https://fivedash.fivex.gg/docs/en/developers/frameworks).
* **Modules:**
  * **Discord:** applies whitelist unlocks (`whitelist.allow`) and removes it from whoever leaves Discord (`whitelist.revoke`).
  * **Store:** writes every delivery to the city's MySQL before answering, delivers exactly once (never twice) and waits for the player to join when they need to be online.
  * **Logs:** receives `exports.fivedash:log(...)` from scripts and sends them in batches.

### Files in the folder

| File             | What it is                                                                        | Updated automatically?                     |
| ---------------- | --------------------------------------------------------------------------------- | ------------------------------------------ |
| `fxmanifest.lua` | Resource manifest (`fx_version "cerulean"`, `game "common"`, `node_version "22"`) | Yes                                        |
| `server/main.js` | All the server code                                                               | Yes                                        |
| `client/main.js` | Client side (empty for now)                                                       | Yes                                        |
| `config.lua`     | Key and general options. Read by the server only                                  | Never. New options are appended at the end |
| `loja.lua`       | Store delivery options                                                            | Never. Created on first boot if missing    |
| `logs.lua`       | Options for logs sent through the export                                          | Never. Created on first boot if missing    |

The `.lua` settings files aren't listed in `files` in the manifest: they never reach players.

## Install

### Generate the key and download the resource

In the panel, go to **Server → Overview**, on the **Game connection** card click **Connect server**, then **Generate key**. Click **Download fivedash.zip**: its `config.lua` already has the key.

### Extract it into resources

Extract the `fivedash` folder into `resources` (or a subfolder like `resources/[fivedash]`).

### Start it after the framework

In `server.cfg`, put `ensure fivedash` after the database driver and the framework:

```bash
ensure oxmysql
ensure qb-core
ensure fivedash
```

### Restart and check

Restart the server (or type `refresh` and `ensure fivedash` in the console). The console shows `[FiveDash] Connected to FiveDash as "Example City".` and the panel shows **Connected**.

Already had the resource installed? Just paste the new key into `fivedash/config.lua`:

```lua
FiveDash = {
	Key = "fdk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
	Framework = "auto",
	AutoUpdate = true,
	Debug = false,
}
```

> The key gives access to your server on FiveDash. Don't publish `config.lua` on GitHub, don't send it in tickets and don't paste it in chats or AIs. If it leaks, click **Generate new key**: the old one stops working right away.

> Resources that start after `fivedash` are noticed too: when the database driver, the framework or the inventory (`ox_inventory`, `qb-inventory`) starts, the resource detects again on its own. Boot waits up to 30 seconds for the database.

## config.lua

Read by the server only. After changing something, run `restart fivedash`.

| Option       | What it does                                                                                                                                                                                | Default  |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `Key`        | The server's key in FiveDash (starts with `fdk_`). Empty, the resource doesn't connect and warns in the console                                                                             | `""`     |
| `Framework`  | `"auto"` detects on its own. To force one: `"creative-enchanted"`, `"qbox"`, `"qbcore"`, `"esx"`, `"ox-core"` or `"vrp"`. Forced and not found, none is used and the console shows an error | `"auto"` |
| `AutoUpdate` | Installs new resource versions on its own (the `.lua` settings files stay as they are). Off, it only warns in the console                                                                   | `true`   |
| `Debug`      | Prints every sync and command in the console, plus the detection evidence                                                                                                                   | `false`  |

Two options aren't in the file, but are accepted:

| Option   | What it does                                                                                                                                                                                    | Default                                       |
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `Url`    | The panel's address. The resource you download from the panel already has this line set to `https://fivedash.fivex.gg`; leave it as it came                                                     | `https://fivedash.fivex.gg` (in the download) |
| `Locale` | Console language before the first connection: `"pt-BR"` or `"en"`. After that, the panel's **City language** wins. The download already includes this line for cities that aren't in Portuguese | Portuguese                                    |

### How updates touch the settings files

When a new version brings an option your file doesn't have, the resource appends that option (with its comment and default value) before the closing `}`, without changing or removing what you wrote. Before that, it keeps a copy in `config.lua.bak` (or `loja.lua.bak`, `logs.lua.bak`). The console says which options were added. If the file doesn't have the `FiveDash = { ... }` shape, it isn't touched and new options use their defaults.

## loja.lua

Store delivery options in the city. Details on each delivery type in [In-game delivery](https://fivedash.fivex.gg/docs/en/store/in-game-delivery).

| Option            | What it does                                                                                                                                   | Default  |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| `Enabled`         | Turns store deliveries on in this city. Off, the store's site stops selling until you turn it back on                                          | `true`   |
| `Notify`          | Shows the player a notice when the purchase arrives                                                                                            | `true`   |
| `NotifyTitle`     | The notice's title (up to 40 characters). `"Loja"` shows in the city's language ("Store" in English cities)                                    | `"Loja"` |
| `Announce`        | Announces to the whole city who bought what (once per order; gifts aren't announced)                                                           | `false`  |
| `SpawnDelay`      | Seconds between the player picking their character and the delivery (0 to 120)                                                                 | `10`     |
| `AllowedCommands` | Console commands the store may run. Empty allows any, except the blocked ones. To lock it, list the start of each: `{ "givecoins", "addcar" }` | `{}`     |

Commands always blocked in command deliveries, even with `AllowedCommands` empty: `quit`, `stop`, `start`, `restart`, `ensure`, `refresh`, `exec`, `set`, `sets`, `setr`, `seta`, `rcon_password`, `sv_licensekey`, `sv_hostname`, `sv_maxclients`, `endpoint_add_tcp`, `endpoint_add_udp`, `load_server_icon` and `fivedash`. One line is one command: `;` and line breaks are refused. Each command runs at most 100 times per delivery.

## logs.lua

Options for logs sent through the `exports.fivedash:log` export. Webhooks don't depend on this file.

| Option             | What it does                                                                                                             | Default                    |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------ | -------------------------- |
| `Enabled`          | Turns sending logs through the export on. Off, calls are ignored                                                         | `true`                     |
| `PlayerInfo`       | Reads the character ID and name of each player in the log, through the framework                                         | `true`                     |
| `Identifiers`      | Player identifiers sent with each log. Accepted: `license`, `license2`, `discord`, `steam`, `fivem`, `xbl`, `live`, `ip` | `{ "license", "discord" }` |
| `Coords`           | Keeps the first player's position when the script sends no `coords`                                                      | `true`                     |
| `Disabled`         | Log keys that never leave the city. E.g. `{ "chat" }`                                                                    | `{}`                       |
| `AllowedResources` | Resources allowed to send logs. Empty lets any. E.g. `{ "vrp", "inventory" }`                                            | `{}`                       |
| `Echo`             | Prints each log in the console as it comes in (testing only)                                                             | `false`                    |

The send pace is fixed: one batch a second, up to 250 logs per batch, and up to 20,000 logs kept in the queue while the panel is away. Old lines like `FlushInterval`, `BatchSize` and `MaxQueue` stay in the file but are ignored.

## Convars

Convars win over `config.lua`. Use `set` in `server.cfg`, never `sets` (`sets` publishes the value to the server list, and the key would be visible).

| Convar               | Same as                      | Example                                                                                 |
| -------------------- | ---------------------------- | --------------------------------------------------------------------------------------- |
| `fivedash_key`       | `Key`                        | `set fivedash_key "fdk_xxxxxxxxxxxxxxxx"`                                               |
| `fivedash_framework` | `Framework`                  | `set fivedash_framework "qbox"`                                                         |
| `fivedash_debug`     | `Debug` (use `1` to turn on) | `set fivedash_debug 1`                                                                  |
| `fivedash_locale`    | `Locale`                     | `set fivedash_locale "en"`                                                              |
| `fivedash_url`       | `Url`                        | `set fivedash_url "https://fivedash.fivex.gg"` (only if your `config.lua` has no `Url`) |

Convars must be in `server.cfg` before `ensure fivedash`.

## How it authenticates

* **One key per server:** the key looks like `fdk_` + 32 characters. The panel stores only its hash and shows just the last 4 characters (**Resource** `…a1b2`). It's shown in full once, when generated.
* **Header:** every call sends `Authorization: Bearer fdk_…`.
* **One city per key:** each boot of the resource creates an instance ID (`X-FiveDash-Instance`). If the same key is running on two servers at once, the panel keeps the one already syncing and refuses the other (`409`) until the first has been quiet for 10 seconds (a restart). That way no delivery goes to the wrong city or happens twice.
* **Replace or turn off:** **Generate new key** replaces the key right away. **Disconnect** deletes the link and drops the commands not delivered yet.

## How it syncs

The resource runs one sync after another with `POST /api/fivem/sync`. Each carries the city's state and the results of the previous commands; the answer brings new commands. When there's nothing, the panel holds the answer for up to 3 seconds (long polling), so an unlock or a purchase reaches the city within a few seconds.

| Rule                  | Value                                                                     |
| --------------------- | ------------------------------------------------------------------------- |
| Max frequency         | 1 sync per second per server (faster gets `429`)                          |
| Hold without commands | up to 3 seconds                                                           |
| Call timeout          | 45 seconds                                                                |
| Offline               | retries from 2 s, doubling up to 60 s, and warns in the console only once |
| Parallel commands     | up to 8                                                                   |

### What each sync sends

```json
{
  "protocol": 1,
  "resource": "0.6.0",
  "framework": {
    "id": "qbcore",
    "name": "QBCore",
    "version": "1.3.0",
    "features": ["whitelist", "store"],
    "idKind": "discord",
    "warnings": []
  },
  "players": 48,
  "maxPlayers": 64,
  "results": [{ "id": "cmd_example", "ok": true }],
  "store": { "enabled": true, "problem": null, "kinds": ["group", "vehicle", "money", "item", "command"], "idLabel": "Citizen ID", "waiting": 2 },
  "deliveries": [{ "id": "<order>:0:0", "state": "done", "reason": null, "at": "2026-11-09T18:00:00.000Z" }],
  "hold": true
}
```

| Field                   | What it is                                                                                                                                                                                     |
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `protocol`              | The resource's protocol version (`1` today)                                                                                                                                                    |
| `resource`              | The resource version                                                                                                                                                                           |
| `framework`             | Detected framework: `id`, `name`, `version`, `features` (`whitelist`, `store`), `idKind` (`account`, `token` or `discord`) and `warnings` (in the city's language). `null` without a framework |
| `players`, `maxPlayers` | Players now and `sv_maxclients` (default 32)                                                                                                                                                   |
| `results`               | Each command's result: `{ "id", "ok": true }` or `{ "id", "ok": false, "code", "message" }`                                                                                                    |
| `store`                 | Store state in the city: on, problem, what it delivers, ID name and waiting deliveries                                                                                                         |
| `deliveries`            | Deliveries that changed state (`done`, `waiting`, `failed`), up to 200 per sync                                                                                                                |
| `hold`                  | `false` on the first sync (and after an outage), so the panel answers right away                                                                                                               |

### What comes back

```json
{
  "server": { "name": "Example City" },
  "commands": [
    { "id": "cmd_example", "type": "whitelist.allow", "gameId": "1234567", "discordId": "123456789012345678" }
  ],
  "update": null,
  "locale": "en"
}
```

| Command type       | What it does in the city                                                                                |
| ------------------ | ------------------------------------------------------------------------------------------------------- |
| `whitelist.allow`  | Unlocks the ID (and links the Discord, when sent) in the framework's whitelist                          |
| `whitelist.revoke` | Removes whitelist by ID and/or Discord and, if `kickMessage` is set, drops the player with that message |
| `store.deliver`    | Receives an order's deliveries (with `retry: true` on **Send again**, only what failed runs again)      |
| `store.cancel`     | Order refunded: drops what's still waiting in the city                                                  |
| `store.lookup`     | Asks who a purchase would go to (character ID and name, and whether they're online)                     |

Every command is idempotent: if a result gets lost and the command comes back, the resource answers what it already did, without running it again. Error codes: `not_found` (the ID doesn't exist in the database), `unsupported` (the framework can't do it), `invalid` (wrong ID format) and `failed` (anything else, with `message`).

### Panel responses

| Status | Error         | What the resource does                                                      |
| ------ | ------------- | --------------------------------------------------------------------------- |
| `200`  |               | Applies the commands and goes on                                            |
| `400`  | `bad_request` | Invalid body or larger than 256 KB. Retries with backoff                    |
| `401`  | `invalid_key` | Invalid or replaced key. Shows the error and retries in 60 s                |
| `409`  | `in_use`      | The key is running on another server now. Retries in 10 s                   |
| `426`  | `outdated`    | Resource version no longer accepted. Retries in 10 min                      |
| `429`  | `too_many`    | Too fast (or too many wrong keys from the same IP). Waits for `Retry-After` |

## Script logs

Export logs don't go through the sync: they take a separate path, so a busy city never slows commands down.

1. The resource fetches the log map at `GET /api/fivem/logs/channels` (with the same key). The map says which log keys exist and where to send them, and it's valid for a short time.
2. Every second, it sends up to 250 logs to `POST /api/fivem/logs`, gzipped when the batch is over 1 KB.
3. Offline, logs stay in the queue (up to 20,000) and go when the panel is back.

Logs with a key that doesn't exist in the panel yet wait in the queue until you create the log. See [Logs API](https://fivedash.fivex.gg/docs/en/developers/logs-api) and [Exports, events and commands](https://fivedash.fivex.gg/docs/en/developers/exports-and-events).

## Protocol version

The resource and the panel agree on the message format through the `protocol` field (`GAME_PROTOCOL`, `1` today). When the panel changes something that breaks old versions, it bumps that number and answers `426` to older resources, which print "this resource version is no longer accepted" in the console and ask you to download the new one. Changes that don't break anything don't touch the number.

## Updates

With `AutoUpdate = true` (default), the resource updates itself:

1. The sync says a new version exists.
2. The resource downloads each file and checks its size and SHA-256 before writing anything. Only `fxmanifest.lua`, `server/*.js` and `client/*.js` can be updated (up to 10 MB each). The `.lua` settings files are never touched.
3. It writes the files and checks each one. If one fails, the ones already written go back to their previous contents.
4. The new version runs from the next server restart, or with `restart fivedash` in the console. The resource never restarts itself.

The panel shows on the **Game connection** card: "Version X of the resource is available…". If an update fails, the resource tries again in 10 minutes. With `AutoUpdate = false`, it only warns in the console: `FiveDash X is available (this is Y). AutoUpdate is off in config.lua.`

To update by hand: on the **Game connection** card, click **Connect server**, then **Download the latest version**. That zip comes without a key: replace only `fxmanifest.lua`, `server/` and `client/` and keep your `.lua` settings files.

## Tables in the city's database

The resource creates these tables in the city's MySQL (with `CREATE TABLE IF NOT EXISTS`). Don't delete them: they hold what must survive a restart.

| Table                       | What for                                                                                                                                                  |
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `fivedash_identities`       | The license, license2, Discord, Steam and FiveM of whoever tries to join. Links a Discord to its licenses (whitelist by Discord and purchases by Discord) |
| `fivedash_whitelist_grants` | Unlocks FiveDash gave in the QBCore, Qbox and esx_whitelist whitelists, re-applied on every boot                                                          |
| `fivedash_ace_groups`       | ACE groups given by the store (`group.vip`, for example), re-applied on every boot                                                                        |
| `fivedash_store_deliveries` | Every store delivery and its state. Makes sure nothing is delivered twice                                                                                 |
| `fivedash_store_groups`     | Timed groups bought in the store, to remove them when they expire                                                                                         |

## Console messages

Every line from the resource starts with `[FiveDash]`.

| Message                                                                 | What to do                                                                                     |
| ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| "No dashboard key: this server isn't connected to FiveDash."            | Paste the key into `Key` in `fivedash/config.lua`                                              |
| "The dashboard key doesn't look right (it starts with fdk\_)."          | Check that you copied the whole key                                                            |
| "key refused by the dashboard"                                          | The key was replaced or the server was disconnected. Generate a new one and replace `Key`      |
| "this key is already in use by another server that's running right now" | Each server has its own key. If you just restarted, it gets back in by itself in a few seconds |
| "this resource version is no longer accepted"                           | Download the new version from the panel                                                        |
| "No connection to the dashboard (…). Retrying automatically."           | Network or panel problem. The resource retries on its own                                      |
| "No known framework found…"                                             | Run `fivedash detect` to see the evidence, or force one with `Framework`                       |

## FAQ

**Do I need to open a port on the server?**
No. The resource only makes outbound HTTPS calls.

**Can I use the same key on two servers (test and production)?**
No. Each FiveDash server has its own key and only one city is linked at a time. Create another server in the panel for the test environment.

**Does auto-update erase my settings?**
No. `config.lua`, `loja.lua` and `logs.lua` are never overwritten. New options are only appended, with a `.bak` copy first.

**Does it work without a framework?**
Yes. The connection works, the whitelist stays on Discord and the store only delivers console commands.

## Next steps

[Frameworks](https://fivedash.fivex.gg/docs/en/developers/frameworks)How each framework is detected and what works on it.
[Exports, events and commands](https://fivedash.fivex.gg/docs/en/developers/exports-and-events)The logs export, the event and the console commands.
[Logs API](https://fivedash.fivex.gg/docs/en/developers/logs-api)The Discord-webhook compatible endpoint.
[In-game delivery](https://fivedash.fivex.gg/docs/en/store/in-game-delivery)How purchases reach the city.
