> For the complete documentation index, see [llms.txt](https://tyrix.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tyrix.gitbook.io/docs/script-resources/tyrix-frames/configuration.md).

# Configuration

Two files. `config.lua` is a shared script that every connecting client downloads. `config_sv.lua` is server-only and is where anything players must not read belongs.

Both are outside escrow and safe to edit. **Every value is validated on start** — a bad one prints a warning to the server console and falls back to a safe default rather than breaking the resource.

The config is deliberately short. Canvas sizes, prop variants, render resolution, camera framing, and rate limits are all fixed because they are tied to the shipped prop pack and the compiled UI; changing them would break rendering rather than tune it.

## `config.lua`

### Framework

```lua
---@type 'auto'|'esx'|'qb'|'qbox'|'standalone'
Config.Framework = 'auto'
```

`auto` detects by started resource, in the order `es_extended` → `qbx_core` → `qb-core`. Set it explicitly only if your framework starts *after* this resource and detection runs too early.

`standalone` is **never** auto-detected and must be set by hand — see [Standalone](/docs/script-resources/tyrix-frames/standalone.md).

### Interaction

```lua
---@type 'auto'|'ox_target'|'qb-target'|'textui'
Config.Interaction = 'ox_target'
```

`auto` uses `ox_target` if it is running, then `qb-target`, then falls back to `textui` — a built-in `[E]` prompt that needs no target resource at all. Pin a specific one only if you run both and want to force the choice.

Detection happens client-side, which is reliable because client scripts start well after every server resource is up.

### Inventory

```lua
---@type 'auto'|'ox_inventory'|'qb-inventory'|'esx'|'none'
Config.Inventory = 'auto'
```

`auto` picks `ox_inventory`, then any qb-inventory fork, then ESX native. `none` runs [itemless](/docs/script-resources/tyrix-frames/standalone.md).

| Value          | Photos survive a pickup               |
| -------------- | ------------------------------------- |
| `ox_inventory` | ✅ stored in item metadata             |
| `qb-inventory` | ✅ stored in item `info`               |
| `esx`          | ❌ ESX native has no per-item metadata |
| `none`         | ❌ there is no item                    |

The two that cannot store a photo force `Config.WipeOnPickup = true` and say so in the console.

### Locale

```lua
Config.Locale = 'en'
```

Shipped: `en`, `es`, `fr`, `pt`, `de`. See [Localization](/docs/script-resources/tyrix-frames/localization.md).

### Debug

```lua
Config.Debug = false
```

Enables `/frameplace`, `/framedel`, `/tf_debug` and `/tf_render`, plus verbose console output. All of them are admin-gated regardless, but leave this off in production — see [Commands](/docs/script-resources/tyrix-frames/commands.md).

### Admin access

```lua
Config.Admin = {
    ace = 'tyrix.frames.admin',
    groups = {
        esx  = { 'admin', 'superadmin', 'god' },
        qb   = { 'admin', 'god' },
        qbox = { 'admin', 'god' },
    },
}
```

Either path grants admin — the ace **or** a framework group. Admins get `/frameadmin` plus edit, move, and pickup on any canvas at any range.

Grant the ace in `server.cfg`:

```
add_ace group.admin tyrix.frames.admin allow
```

QBox reads `groups.qbox` if you fill it in and falls back to `groups.qb` otherwise. Standalone ignores groups entirely and uses the ace only.

### Allowed image hosts

```lua
Config.AllowedHosts = {
    'i.imgur.com', 'imgur.com',
    'api.fivemanage.com', 'r2.fivemanage.com',
    'i.postimg.cc', 'i.ibb.co',
}
```

HTTPS only, direct image links only, no GIFs. Matched on the exact host or one subdomain level — no wildcards, no patterns.

{% hint style="warning" %}
**Discord CDN is excluded on purpose.** `cdn.discordapp.com` and `media.discordapp.net` attachment links now carry signed expiry parameters and die within about 24 hours, which would blank every canvas using one. Hosts that perform open redirects also weaken the whitelist, since redirects cannot be fully controlled server-side. Add either at your own risk.
{% endhint %}

An empty list is not a footgun so much as a disabled feature — no photo can pass validation. The resource warns if you leave it empty.

### Gallery cap

```lua
Config.GalleryCap = 50
```

Photos a player may keep in their collection. At the cap they are asked to delete one; nothing is ever silently removed. Deleting a photo from a collection also blanks any of that player's own canvases showing it.

### Wipe on pickup

```lua
Config.WipeOnPickup = false
```

`true` discards the photo when a canvas is picked up. The default keeps it in the item, so re-placing restores both the photo and its adjustment. Forced to `true` on inventories that cannot store metadata.

### Placement limit

```lua
Config.PlacementLimit = {
    default     = 10,     -- 0 = unlimited
    adminBypass = false,
}
```

Counted **per character**. A canvas stops counting the moment it is picked up or deleted, and players see their count while aiming (`Place Canvas (7/10)`).

**Lowering the limit never deletes anything.** Players already above the new number keep every canvas; they simply cannot place more until they pick some up.

Higher allowances for specific players live in `config_sv.lua` — see below.

### Performance

```lua
Config.SpawnDistance  = 60.0   -- 20 - 150
Config.RenderDistance = 25.0   -- clamped to SpawnDistance
Config.MaxActiveDuis  = 8      -- 1 - 32
```

| Setting          | Effect of raising it                                                                                                                |
| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `SpawnDistance`  | More canvas props spawned per client. They are client-local and frozen, so this is cheap.                                           |
| `RenderDistance` | Photos start rendering from further away. Clamped to `SpawnDistance`, because a photo cannot render on a prop that has not spawned. |
| `MaxActiveDuis`  | More simultaneous photo renderers. Each is a browser instance holding the decoded image, so raise it deliberately.                  |

Cost does not grow with how many canvases exist server-wide — see [Performance](broken://pages/492bf28cbd39452837f0fb6f85598920092320cf).

## `config_sv.lua`

Server-only, never sent to a client. Webhook URLs and player identifiers belong here.

### Placement overrides

```lua
Config_SV.PlacementOverrides = {
    ['discord:123456789012345678']                     = 25,
    ['license:abcdef0123456789abcdef0123456789abcdef'] = 50,
    ['char1:abcdef0123456789abcdef0123456789abcdef01'] = 0,  -- unlimited
}
```

Higher canvas allowances for specific people — VIP tiers, staff, event builders. Everyone else gets `Config.PlacementLimit.default`.

A key may be **either** the character identifier a canvas is stored against (the ESX identifier, or the QB/QBox citizenid) **or** any raw identifier the account carries: `license:`, `discord:`, `steam:`, `fivem:`. Use whichever you have on hand — a `discord:` entry grants the perk to every character on that account.

If several entries match, the highest wins. `0` means unlimited for that person. Malformed entries are dropped individually with a console warning, so one bad line cannot break placement for everybody.

This lives server-side deliberately: it holds player identifiers, and `config.lua` is downloaded by every connecting client.

### Discord logging

Covered on its own page — see [Discord Logging](/docs/script-resources/tyrix-frames/discord-logging.md).

## What is not configurable

Fixed internally, and why:

| Value                                | Reason                                                                  |
| ------------------------------------ | ----------------------------------------------------------------------- |
| Canvas sizes and their aspect ratios | Derived from the size key, and compiled into the admin panel's filters. |
| Prop variants per size (10)          | Fixed by the shipped prop pack.                                         |
| DUI resolution, idle reaper timer    | Tuned against VRAM cost; no useful range to expose.                     |
| Editor camera FOV and framing        | Solved at runtime against each prop's real dimensions.                  |
| Rate limits                          | Anti-abuse. A missing entry silently disables the limit it protected.   |
| Placement and interaction distances  | Coupled to the client raycast and the server's forgery threshold.       |
