> For the complete documentation index, see [llms.txt](https://organized-chaos.gitbook.io/organized-chaos-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://organized-chaos.gitbook.io/organized-chaos-docs/the-lost-treasure/security-and-maintenance.md).

# Security and maintenance

How the machine protects itself, how to close the floor, and who counts as staff.

## How the machine protects itself

The client is treated as hostile. It sends the number of a bet step, an optional feature buy and a one-time code, never an amount or an outcome. The server:

* **Controls every spin.** A spin needs an open session with no round in progress. It must pass a minimum interval and a rate limit, and use a valid bet within the machine's cap.
* **Takes the stake first.** It debits the stake before running the round, pays exactly what the round won, and refunds only a round that never completed.
* **Checks machines itself.** It verifies distance and job locks using its own copy of the player's position.
* **Blocks abusers across reconnects.** Repeated violations block the player, tracked by licence.
* **Keeps rounds unpredictable.** The key for the random numbers is built from fresh operating-system randomness at every start. Nothing needs configuring.

## Closing the floor

Close the machines without stopping the resource. This is in `config/server/server.lua`:

```lua
Config.Maintenance = {
    Enabled      = true,
    AllowStaff   = true,    -- staff can still play, to verify a change
    AllowCashout = true,    -- players can still take their credits back out
}
```

Spins, feature buys and inserts are refused. Credits already on a machine are untouched, and by default can still be cashed out.

## Anti-abuse

```lua
Config.Security = {
    MinSpinIntervalMs = 350,
    RateLimitBurst    = 5,
    RateLimitPerSec   = 2,
    UiRateLimitBurst  = 10,    -- opening, refreshing state, moving the bet
    UiRateLimitPerSec = 5,
    MaxViolations     = 20,
    BlockSeconds      = 300,   -- false = blocked for good
    LogRounds         = true,
}
```

| Setting                                 | Effect                                                                                                        |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| `MinSpinIntervalMs`                     | The shortest time between two accepted spins                                                                  |
| `RateLimitBurst`, `RateLimitPerSec`     | The sustained limit on spins                                                                                  |
| `UiRateLimitBurst`, `UiRateLimitPerSec` | A looser limit for interface requests. Requests over it are dropped quietly and are not counted as violations |
| `MaxViolations`                         | Invalid requests before the player is blocked                                                                 |
| `BlockSeconds`                          | How long a block lasts                                                                                        |
| `LogRounds`                             | Log every accepted round to the server console                                                                |

{% hint style="warning" %}
Keep `BlockSeconds` finite. A player on a bad connection can earn a block through no fault of their own, and nobody can clear it for them by hand.
{% endhint %}

Violations and blocks are kept against the player's licence, so reconnecting does not clear them.

`RecheckDistance` and `RecheckSlack` belong to the same block but are in `config/config.lua`. They make the client check the distance to the machine again while it is open.

## Who counts as staff

Staff may play during maintenance, use the console commands in game and, by default, open the staff dashboard. A player is staff if either applies:

```lua
Config.DebugEsxGroups     = { 'admin' }
Config.DebugQbcoreGroups  = { 'god' }
Config.DebugAcePermission = 'lost_treasure.debug'
```

To grant it through the ace, add this to your `server.cfg`:

```cfg
add_ace group.admin lost_treasure.debug allow
```

{% hint style="danger" %}
Staff can force outcomes while `Config.Debug` is `true`. Never switch it on on a live server.
{% endhint %}

## An extra secret for the random numbers

This is optional. A secret of your own can be mixed into the key on top of the system randomness:

```cfg
set lost_treasure_rng_secret "any long random string, kept private"
```

Handle it like a webhook URL. Changing it is safe. It only reshuffles which rounds come next.
