# Configuration

Koja Inventory adds five config files in `data/koja/`. They return plain Lua tables and are merged into one config on start.

| File | What is in it |
| --- | --- |
| `data/koja/core.lua` | Slot layout, default player settings, clothing, weight, throwing, plates, fast actions, ped preview |
| `data/koja/interact.lua` | Give, trade, search animation, ox_target options, discovery, shops, weapon attachments |
| `data/koja/admin.lua` | Admin command, roles, addon toggles, history and security defaults |
| `data/koja/rarity.lua` | The default rarities |
| `data/koja/staff.lua` | Staff identifiers (server only, never sent to clients) |

The upstream files in `data/` (`items.lua`, `weapons.lua`, `shops.lua`, `stashes.lua`, `crafting.lua`...) keep their ox_inventory format. Koja only adds optional fields, listed at the end of this page and in [Items](/new/inventory/items).

Restart the resource after changing a file.

:::hint{type="info" title="The admin panel wins"}
Most of these values are only defaults. What you change in the [admin panel](/new/inventory/admin-panel) — addon toggles, rarities, items, shops, stashes, benches, clothing, world containers, security, locales — is stored in the database and overrides the files.
:::

## Slot layout

```lua title="data/koja/core.lua"
baseSlots = 25,
pockets = 5,
```

| Option | Default | What it does |
| --- | --- | --- |
| `pockets` | `5` | Slots `1..pockets` are the **Pockets** row (hotbar) |
| `baseSlots` | `25` | Slots up to this number are always unlocked. Slots above it, up to `inventory:slots`, need a backpack |

## Default player settings

Every player can change these in the **Settings** tab (see [Player settings](/new/inventory/player-settings)). Their choice is saved on their PC (client KVP `koja_inv_settings`); these are the values for new players and for the **Reset** button.

```lua title="data/koja/core.lua"
defaults = {
    accent = '#F5A623',
    secondary = 'rgba(25,26,26,0.98)',
    rows = 6,
    scale = 100,
    clothingPanel = 'center',
    theme = 'v2',
    font = 'Poppins',
    clothingInventory = true,
    pedCamera = true,
    notifyStyle = 'compact',
    sharp = false,
    streamer = false,
    sounds = true,
},
```

| Option | Values | What it does |
| --- | --- | --- |
| `accent`, `secondary` | CSS colour | Main colour and panel background |
| `rows` | 4–8 | Visible rows of the inventory grid |
| `scale` | 70–130 | UI scale in percent |
| `clothingPanel` | `left`, `center`, `right` | Where the clothes panel and ped preview sit |
| `theme` | `v2`, `ferrofluid`, `minimal`, `dotted`, `antigravity` | Inventory style |
| `font` | `Poppins`, `Inter`, `Montserrat`, `Rajdhani`, `Barlow` | UI font |
| `clothingInventory` | boolean | Shows **Your clothes** next to the inventory |
| `pedCamera` | boolean | `false` keeps the gameplay camera; the ped is shown inside the UI instead |
| `notifyStyle` | `compact`, `minimal` | Item added / removed popups while the inventory is closed |
| `sharp` | boolean | Square corners |
| `streamer` | boolean | Streamer mode |
| `sounds` | boolean | UI sounds |

`settingsLimits` holds the allowed ranges and values used to clean saved settings (`rows = { 4, 8 }`, `scale = { 70, 130 }`, the panel positions and themes). Change it only if you add a theme to the UI yourself.

## Clothing

```lua title="data/koja/core.lua"
clothing = {
    enabled = true,
    imageResource = 'illenium-appearance',
    saveAppearance = true,
    adoptAppearance = true,
    externalAppearance = true,
    hiddenSlots = { 'underwear_top', 'underwear_bottom' },
    slots = { ... },
    nakedMale = { ... },
    nakedFemale = { ... },
    genericItem = 'clothing',
    hangerItem = 'clothing_hanger',
    undressAnim = { dict = 'clothingtie', clip = 'try_tie_negative_a', duration = 1200 },
},
```

| Option | What it does |
| --- | --- |
| `enabled` | Clothing as items. Also switchable live as the **Clothing as items** addon |
| `imageResource` | Resource that exports `getClothingImage(gender, category, drawable, texture)` — illenium-appearance with [Koja Appearance](/new/appearance) |
| `saveAppearance` | Saves the ped appearance through illenium-appearance / fivem-appearance after every change |
| `adoptAppearance` | Clothes the ped already wears become clothing items when the matching slot is empty |
| `externalAppearance` | An appearance saved by a skin menu or outfit wins over the worn items: changed pieces are rewritten, removed or named pieces go back to the inventory. Bag and vest stay items |
| `hiddenSlots` | Slots tracked as worn but not shown in the UI |
| `slots` | The clothing slots, in UI order. See below |
| `nakedMale`, `nakedFemale` | `[componentId] = { drawable, texture }` applied when a slot is empty (freemode peds). Props are cleared |
| `genericItem` | Item given when you undress a piece that has no item of its own |
| `hangerItem` | The hanger item used by the **Put on hanger** fast action |
| `undressAnim` | Animation played while dressing / undressing |

### Clothing slots

Each entry of `slots`:

```lua title="data/koja/core.lua"
{ key = 'torso', label = 'Torso', icon = 'layers', column = 'left', kind = 'component', id = 11, category = 'jackets' },
```

| Field | What it is |
| --- | --- |
| `key` | Slot key used in the code and in the locale (`clothing_slot_<key>`) |
| `label`, `icon` | Fallback label and Lucide icon name |
| `column` | `left` or `right` of the ped |
| `kind`, `id` | `component` + component id, or `prop` + prop id |
| `category` | Category key used for the image lookup (`hats`, `masks`, `jackets`...) |

The default slots: `hat`, `mask`, `glasses`, `ears`, `chain`, `tshirt`, `torso`, `arms`, `vest`, `bag`, `pants`, `shoes`, `watch`, `bracelet`, `decals`, plus the hidden `underwear_top` and `underwear_bottom`.

## Weight and overload

```lua title="data/koja/core.lua"
weight = {
    enabled = true,
    overloadPct = 0.85,
    clipset = 'move_m@hobo@a',
    moveRate = 0.85,
    disableSprintPct = 0.97,
    staminaDrain = 2.5,
    sweat = true,
    sweatAmount = 80.0,
},
```

| Option | What it does |
| --- | --- |
| `overloadPct` | Effects start at this fraction of the max weight |
| `clipset` | Movement clipset while overloaded. `false` = none |
| `moveRate` | Move rate while overloaded |
| `disableSprintPct` | No sprint or jump from this fraction of the max weight |
| `staminaDrain` | Stamina points lost per second while running overloaded |
| `sweat`, `sweatAmount` | Sweat on the ped while overloaded |

Live switch: the **Weight effects** addon.

## Throwing

```lua title="data/koja/core.lua"
throw = {
    enabled = true,
    anyItem = true,
    maxDistance = 30.0,
    minForce = 6.0,
    maxForce = 22.0,
    forcePerKg = 1.2,
    defaultProp = 'prop_cs_cardbox_01',
    landTimeout = 7000,
    aimTimeout = 15000,
    keys = { throw = 24, cancel = 25 },
    anim = nil,
    weapons = { ... },
},
```

| Option | What it does |
| --- | --- |
| `anyItem` | Every item can be thrown unless its definition says `throwable = false`. With `false`, only items with `throwable = true` |
| `maxDistance` | The server rejects landings further than this from the thrower |
| `minForce`, `maxForce`, `forcePerKg` | Throw force; heavier stacks fly shorter |
| `defaultProp` | Prop thrown when the item has no `prop` |
| `landTimeout` | Milliseconds before the prop position is taken as the landing point |
| `aimTimeout` | Milliseconds of aiming before the throw is cancelled |
| `keys` | Control ids while aiming: `throw` (24, left mouse) and `cancel` (25, right mouse) |
| `anim` | `{ dict, clip, release }` to force one throw animation. `nil` uses the built-in list |

### Throwing the weapon in hand

```lua title="data/koja/core.lua"
weapons = {
    enabled = true,
    mapper = 'mouse_button',
    key = 'MOUSE_MIDDLE',
    minHold = 250,
    chargeTime = 1200,
    minCharge = 0.45,
    maxDistance = nil,
    denyProjectiles = true,
    allow = nil,
    deny = { 'WEAPON_UNARMED', 'WEAPON_PETROLCAN', 'WEAPON_HAZARDCAN', 'WEAPON_FERTILIZERCAN', 'WEAPON_FIREEXTINGUISHER', 'WEAPON_STUNGUN_MP' },
},
```

| Option | What it does |
| --- | --- |
| `mapper`, `key` | Default key of the `koja_throw_weapon` keybind. Players can rebind it in the FiveM key bindings |
| `minHold` | Releasing earlier cancels (no accidental throws) |
| `chargeTime`, `minCharge` | Holding charges the force from `minCharge` to 100% over `chargeTime` ms |
| `maxDistance` | `nil` = `throw.maxDistance` |
| `denyProjectiles` | Grenades, molotovs and other thrown weapons cannot be thrown this way |
| `allow` | `nil` = every weapon, or a list of weapon names |
| `deny` | Weapons that can never be thrown |

## Armour plates

```lua title="data/koja/core.lua"
plates = {
    enabled = true,
    maxPerVest = 3,
    vests = { armor_vest = true },
    types = { plate_light = 25, plate_medium = 50, plate_heavy = 100 },
},
```

| Option | What it does |
| --- | --- |
| `maxPerVest` | Plates one vest can hold |
| `vests` | Items that accept plates, besides any item whose `clothing.id` is 9 |
| `types` | Armour per plate item, used when the item has no `plate = { armor = n }` |

Ped armour is the sum of the plates, capped at 100.

## Fast actions

```lua title="data/koja/core.lua"
fastActions = {
    { id = 'handsup', label = 'Hands up',      icon = 'hand' },
    { id = 'undress', label = 'Undress',       icon = 'shirt' },
    { id = 'hanger',  label = 'Put on hanger', icon = 'archive' },
    { id = 'trade',   label = 'Trade',         icon = 'repeat' },
    { id = 'give',    label = 'Give',          icon = 'hand-helping' },
},
```

The buttons of the **Fast actions** row. Remove an entry to hide it. Labels can be translated with the locale key `fast_action_<id>`.

## Item text limits

```lua title="data/koja/core.lua"
item = { renameMax = 32, describeMax = 160 },
```

Maximum length of a renamed item label and of a custom description.

## Ped preview

```lua title="data/koja/core.lua"
pedMode = 'clone',
ped = {
    distance = 2.6,
    height = 0.15,
    fov = 38.0,
    offset = { left = 0.95, center = 0.0, right = -0.95 },
},
status = { interval = 500 },
```

| Option | What it does |
| --- | --- |
| `pedMode` | `clone` = a camera frames your ped where the clothes panel is. `pause` = the pause-menu ped on a dark background. Also switchable on the admin **Addon Management** page |
| `ped.distance`, `height`, `fov` | Camera of `clone` mode |
| `ped.offset` | Sideways camera offset per clothes panel position |
| `status.interval` | Milliseconds between health / armour / hunger / thirst updates in the clothes panel |

## Give

```lua title="data/koja/interact.lua"
give = {
    distance = 3.0,
    marker = { type = 25, scale = 1.1, color = { 196, 255, 64, 180 } },
},
```

`distance` is the max distance between giver and receiver. `marker` is the ground circle drawn under the targeted player.

## Trade

```lua title="data/koja/interact.lua"
trade = {
    distance = 3.0,
    timeout = 30,
    sessionTimeout = 300,
    maxOffers = 12,
    keys = { accept = 'Y', decline = 'N' },
},
```

| Option | What it does |
| --- | --- |
| `distance` | Max distance to send and keep a trade |
| `timeout` | Seconds a trade request stays valid |
| `sessionTimeout` | Seconds of inactivity before a running trade is cancelled |
| `maxOffers` | Max different slots one player can offer |
| `keys` | Default keys of the `koja_trade_accept` / `koja_trade_decline` keybinds (used while the inventory is closed) |

## World containers

```lua title="data/koja/interact.lua"
search = {
    enabled = true,
    dumpster = { dict = 'amb@prop_human_bum_bin@base', clip = 'base' },
    trunk = { dict = 'mini@repair', clip = 'fixing_a_ped' },
},

target = {
    enabled = true,
},
```

| Option | What it does |
| --- | --- |
| `search.enabled` | Looping search animation while a dumpster or trunk inventory is open. Not played in a vehicle |
| `search.<type>` | Animation per inventory type |
| `target.enabled` | Extra ox_target options (search dumpster, open trunk, stash and evidence zones) while the server runs **without** `inventory:target`. Needs ox_target started. Ignored when `inventory:target` is on |

Both switches can be changed live on the admin **World containers** page, together with the loot tables.

## Discovery

```lua title="data/koja/interact.lua"
discovery = {
    enabled = true,
    types = { 'player', 'drop', 'stash', 'trunk', 'glovebox' },
    skipOwned = true,
    memory = 600,
},
```

| Option | What it does |
| --- | --- |
| `types` | Inventory types where items have to be discovered |
| `skipOwned` | Stashes owned by the player are never "discovered" |
| `memory` | Seconds the server remembers what a player already searched |

The search time per item comes from its rarity (`discoveryTime` in `data/koja/rarity.lua`).

## Shops

```lua title="data/koja/interact.lua"
shop = {
    defaultPayment = { 'cash', 'card' },
    cashItem = 'money',
    interaction = {
        enabled = true,
        distance = 2.0,
        drawDistance = 8.0,
        key = 38,
        keyLabel = 'E',
    },
    dui = {
        enabled = true,
        size = 0.3,
        maxHeight = 0.11,
    },
},
```

| Option | What it does |
| --- | --- |
| `defaultPayment` | Payment methods of shops that do not set `paymentMethods`. `cash` = the `cashItem` item, `card` = the framework bank account |
| `cashItem` | Item used for cash payments |
| `interaction.enabled` | The built-in floating "press E" prompt for shops that do not set it themselves |
| `interaction.distance`, `drawDistance` | Distance to press the key / to see the prompt |
| `interaction.key`, `keyLabel` | Control id and label |
| `dui.enabled` | Draws the prompt as a 3D card in the player's inventory style. Falls back to a text prompt when disabled or when the card fails to load |
| `dui.size` | Card height in metres |
| `dui.maxHeight` | On-screen height cap (fraction of the screen) when standing next to it |

## Weapon attachments

```lua title="data/koja/interact.lua"
attachments = {
    interval = 30,
    fov = 32.0,
    distance = 1.1,
    rotateSpeed = 0.35,
    pitchSpeed = 0.25,
    maxPitch = 45.0,
    minFov = 14.0,
    maxFov = 50.0,
    zoomStep = 2.0,
    smoothing = 0.07,
    bones = { ... },
    fallback = { ... },
    types = { 'muzzle', 'flashlight', 'scope', 'magazine', 'grip', 'barrel', 'skin', 'tint' },
    labels = { ... },
},
```

| Option | What it does |
| --- | --- |
| `interval` | Milliseconds between marker position updates |
| `fov`, `distance` | Camera FOV and distance (scaled by the weapon length) |
| `rotateSpeed`, `pitchSpeed`, `maxPitch` | Mouse rotation of the weapon |
| `minFov`, `maxFov`, `zoomStep` | Mouse wheel zoom |
| `smoothing` | Easing of rotation and zoom in seconds (`0` = instant) |
| `bones` | Weapon bones searched, in order, for each marker type |
| `fallback` | Marker position (fraction of the weapon bounding box) when no bone exists |
| `types`, `labels` | Marker types in UI order and their labels |

## Admin

```lua title="data/koja/admin.lua"
admin = {
    command = 'invadmin',
    key = nil,
    superAce = 'group.admin',
    rateLimit = { calls = 30, window = 3000 },
    roles = { ... },
},
```

| Option | What it does |
| --- | --- |
| `command` | Chat command that opens the admin panel. `false` disables it |
| `key` | Optional keybind, e.g. `'F10'` (registered as `koja_invadmin`) |
| `superAce` | Ace that grants every admin page |
| `rateLimit` | Max admin calls per window (ms) per player |
| `roles` | Default roles, created in the database on first start: `owner`, `admin`, `moderator`, `support`. After that, edit them on the **Permissions** page |

Role `perms` map a page key to `true` (`['*'] = true` = every page). Page keys: `home`, `addons`, `items`, `rarity`, `players`, `history`, `security`, `clothing`, `shops`, `stashes`, `permissions`, `locales`.

## Addon toggles

```lua title="data/koja/admin.lua"
features = {
    clothing = { default = true, label = 'Clothing as items', description = '...' },
    -- ...
},
```

`default` is used until an admin changes the toggle on **Addon Management**; after that the database value wins. Disabled features fall back to stock ox_inventory behaviour.

| Key | Feature |
| --- | --- |
| `clothing` | Clothing as items |
| `backpack` | Backpacks |
| `ministash` | Mini stashes |
| `throw` | Throwing |
| `weightEffects` | Weight effects |
| `plates` | Plate armour |
| `discovery` | Item discovery |
| `trade` | Trading |
| `give` | Hover & give |
| `shops3d` | 3D shop interaction |
| `attachments` | Weapon 3D attachments |
| `multiselect` | Multi-select (SHIFT + click) |
| `rename` | Rename & describe |
| `history` | Inventory history |

## History

```lua title="data/koja/admin.lua"
history = {
    enabled = true,
    retentionDays = 30,
    flushInterval = 5000,
    maxBuffer = 500,
    ignore = {},
},
```

| Option | What it does |
| --- | --- |
| `retentionDays` | Rows older than this are removed on start and every 6 hours. `0` keeps them forever |
| `flushInterval` | Milliseconds between buffered database inserts |
| `maxBuffer` | Forces a flush when the buffer gets this big |
| `ignore` | Actions never logged, e.g. `{ open = true, close = true }` |

## Security

```lua title="data/koja/admin.lua"
security = {
    autoKick = false,
    logHookRejections = true,
    trimPlates = true,
    emergencyLock = false,
    distanceCheck = true,
    maxDistance = 10.0,
    logLevel = 1,
    evidenceGrade = 2,
    retention = 100,
    imageHosts = { 'r2.fivemanage.com', 'i.fmfile.com' },
    severe = { malformed = true, locked_slot = true, exploit = true },
},
```

| Option | What it does |
| --- | --- |
| `autoKick` | Drops players on severe reports |
| `logHookRejections`, `trimPlates`, `logLevel`, `evidenceGrade` | Live mirrors of `inventory:loghookrejection`, `inventory:trimplate`, `inventory:loglevel`, `inventory:evidencegrade` |
| `emergencyLock` | Blocks every inventory open. Never saved, so it is off after a restart |
| `distanceCheck`, `maxDistance` | Reports drops and opens from further than `maxDistance` metres |
| `retention` | Recent reports kept in memory for the Security page |
| `imageHosts` | Hosts allowed in metadata image URLs (`inventory:validhosts`) |
| `severe` | Report kinds that count as severe (and trigger `autoKick`) |

## Rarities

```lua title="data/koja/rarity.lua"
rarities = {
    { name = 'common',    label = 'Common',    color = '#9AA0A6', discoveryTime = 0,  revealTimer = false },
    { name = 'uncommon',  label = 'Uncommon',  color = '#2ECC71', discoveryTime = 2,  revealTimer = true },
    { name = 'rare',      label = 'Rare',      color = '#3498DB', discoveryTime = 4,  revealTimer = true },
    { name = 'epic',      label = 'Epic',      color = '#9B59B6', discoveryTime = 6,  revealTimer = true },
    { name = 'legendary', label = 'Legendary', color = '#F39C12', discoveryTime = 8,  revealTimer = true },
    { name = 'mythic',    label = 'Mythic',    color = '#E74C3C', discoveryTime = 10, revealTimer = true },
},
```

| Field | What it does |
| --- | --- |
| `name` | Key used by items (`rarity = 'rare'`) |
| `label`, `color` | Shown in tooltips; the colour tints slot borders and labels |
| `discoveryTime` | Seconds an item of this rarity stays "Discovering..." in other inventories |
| `revealTimer` | Shows a countdown on the slot while it is discovered |

Items without a rarity are `common`. Edits on the **Edit Rarity** page are stored in `koja_inv_rarities` and override this file.

## Staff

```lua title="data/koja/staff.lua"
return {
    ['license:0123456789abcdef'] = 'owner',
}
```

Identifier → role name. Accepted identifiers: `license:`, `license2:`, `discord:`, `fivem:`, `steam:`.
This file is not in the manifest `files`, so clients never download it. Staff added on the **Permissions** page is stored in the database and merged on top.

## Upstream data files

| File | What Koja adds |
| --- | --- |
| `data/items.lua` | Optional item fields (`rarity`, `throwable`, `prop`, `unique`, `isStash`, `hanger`, `backpack`, `clothing`, `plate`, `tint`) — see [Items](/new/inventory/items) |
| `data/weapons.lua` | Component `type` decides the marker of the 3D attachments view (`sight` is shown as `scope`) |
| `data/shops.lua` | `paymentMethods`, `interaction` and `icon` per shop — see [Features](/new/inventory/features#shops) |
| `data/crafting.lua` | `duration` per recipe is shown in the crafting panel; `count` may be `{ min, max }` — see [Features](/new/inventory/features#crafting) |
| `data/stashes.lua` | Unchanged. Stashes can also be created on the **Stashes** admin page |
