Configuration
Every setting in oxide-phone, what it does in game, its default value and where to find it in the /phone settings menu — plus how database-backed settings work, the o-link map mode and the app ID reference.
This page lists every setting in oxide-phone, what it does in game, its default value, and where to find it in the /phone settings menu. Start with How Settings Work: the phone saves its settings in your database, so editing the config file does not always do what you expect.
How Settings Work
The Short Version
- The first time oxide-phone starts, it copies every setting from
shared/config.luainto a database table calledoxide_settings. - From then on, the database decides. Editing
shared/config.luadoes not change a setting that is already saved in the database. - To change settings after the first start, join the server as an admin and type
/phone settings. - Changes you save in that menu are stored in the database straight away, and almost all of them work immediately, without a restart.
The First Start
Optional: open shared/config.lua (the settings file) and change anything you want before starting the phone for the first time. You can also skip this and do everything in game later.
Start your server.
oxide-phone creates the oxide_settings table if it does not exist yet. Other Oxide resources may use the same table; each one keeps its own rows.
It copies every setting from shared/config.lua into that table. The server console shows a configSeeded line when it does.
After the First Start
On every start after the first one, oxide-phone reads its settings from the database. The config file is only used in these cases:
- A setting missing from the database is copied in from
shared/config.lua. This happens when a resource update adds a brand-new setting, or when you delete a setting's row from the database. - A new option inside an existing group is filled in from
shared/config.lua. For example, if an update adds a new option underConfig.Battery, that one option is added and your other battery settings stay as they are. - Lists are never merged. Lists such as
Config.Items.phones,Config.Numbers.prefixes,Config.Evidence.jobs,Config.Evidence.stations,Config.Emergency.numbers,Config.DeadZones,Config.Store.preinstalled,Config.Home.wallpapersandConfig.ModelSwapsare saved as a whole. After the first start, the whole list comes from the database. - Reset to default in the menu puts a setting back to the value in
shared/config.lua(see below).
When you update oxide-phone and the update replaces shared/config.lua, you lose nothing: your saved settings stay in the database.
Changing Settings in Game
Join your server with an admin account. See Admin for who counts as an admin.
Type /phone settings in chat and press Enter. The Phone Settings window opens. If your own phone was open, it is put away first.
Pick a section in the left sidebar. Sections are grouped under Device, Police, World, Screen and Advanced.
Use the Basic and Advanced tabs at the top. Most settings are on Basic. Advanced has the app start timeout, the timers and debug mode.
Use the search box to find a setting by name.
Change a value. A changed badge appears next to it, and the bar at the bottom counts your unsaved changes.
Press Save Changes. A message confirms the save. If a value is not allowed, that setting shows the reason and your other changes are still saved.
Close the window with Close or ESC. Closing throws away changes you have not saved.
Reset to default next to a setting puts it back to the value shared/config.lua had the last time the phone started. The reset is saved immediately. For settings that hold several options (such as Battery), the reset puts back all of them together.
Positions in the world (Forensics stations and charging stations) have two small buttons: the crosshair (Place in world) hides the menu so you can pick the spot in the world, and Teleport here takes you to a saved spot. A charging station is placed as a see-through copy of its model that you turn and put down.
Using the Config File After the First Start
If you prefer editing shared/config.lua, you need one extra step to push your change into the database:
Open shared/config.lua, change the value, and save the file.
Restart your server (or type restart oxide-phone in the server console).
Join as an admin and type /phone settings.
Find the setting and press Reset to default. The value from the file is now saved in the database.
When Changes Take Effect
Almost every setting applies as soon as you press Save Changes, for every player, with no restart. The exceptions:
| Setting | When it applies |
|---|---|
Config.OpenKey, Config.Calls.answerKey, Config.Calls.declineKey | Only for players who have never joined your server. Everyone else keeps their own key binding |
Config.Numbers | Only for new numbers. Existing numbers never change |
Config.Home.wallpapers rows pointing at a new file in wallpapers/ | After the new file is served: restart oxide-phone once after copying the file |
Config.Prop | The next time a phone is opened |
Server convars (keys in server.cfg) | After a restart. See Installation → Server Convars |
Where Each Setting Is in the Menu
| Sidebar | Section | Settings |
|---|---|---|
| Device | General | Config.OpenKey, Map mode (see below) |
| Device | Items | Config.Items |
| Device | Numbers & SIMs | Config.Numbers, Config.Sim |
| Device | Security | Config.Lock |
| Device | Calls & Messages | Config.Calls, Config.Messages, Config.Contacts, Config.Mail |
| Device | Media | Config.Media, Config.Camera, Config.Streams |
| Police | Evidence | Config.Evidence |
| Police | Emergency | Config.Emergency |
| World | Battery | Config.Battery, Config.Chargers |
| World | Signal | Config.Signal, Config.DeadZones |
| Screen | Home & Apps | Config.Store, Config.Appearance, Config.Home, Config.AppReadyTimeout (Advanced tab) |
| Screen | Apps | Config.Apps |
| Screen | Notifications | Config.Notifications |
| Screen | Appearance | Config.Prop |
| Advanced | Compatibility | Config.ModelSwaps |
| Advanced | Advanced | Config.Session, Config.Debug (Advanced tab) |
| Not in the menu | Config.SettingsCommand, Config.ModelHints |
Config File: shared/config.lua
General
| Setting | Type | Default | Description |
|---|---|---|---|
Config.Debug | boolean | false | Prints extra detail to the server console and the F8 console, and offers a Demo app in the Store (and on oxide-tablet, when it runs) for testing apps from other resources. Leave off on a live server |
Config.SettingsCommand | string | '/phone settings' | The command named in model warnings as the place to set a replacement model. Not in the settings menu |
Config.OpenKey | string | 'F1' | The default key that opens the phone the player carries. Players change it in Esc → Settings → Key Bindings → FiveM → Open phone. A new default only reaches players who have never joined |
Config.Debug = false
Config.SettingsCommand = '/phone settings' -- named in warnings
Config.OpenKey = 'F1'Menu: Device → General (OpenKey, up to 20 characters) and Advanced → Advanced on the Advanced tab (Debug, shown as Debug mode). Config.SettingsCommand has no place in the menu; leave it as it is.
Items
The inventory item names the phone reacts to. Each item must exist in your inventory (see Installation → Framework Setup).
| Setting | Type | Default | Description |
|---|---|---|---|
Config.Items.phones | table (list of strings) | { 'phone' } | Every item listed opens as a phone. Each phone item is its own device with its own data |
Config.Items.sim | string | 'sim_card' | The registered SIM card. The carrier records the character it was issued to |
Config.Items.burnerSim | string | 'burner_sim' | The unregistered SIM card. '' turns burners off |
Config.Items.powerbank | string | 'powerbank' | A one-shot charge for the phone the player holds. '' turns it off |
Config.Items.wiper | string | 'phone_wiper' | Carrying one adds Secure wipe to the phone's Settings app; wiping uses it up. '' turns secure wipe off |
Config.Items = {
phones = { 'phone' }, -- every item listed opens as a phone
sim = 'sim_card', -- registered SIM
burnerSim = 'burner_sim', -- unregistered SIM; '' disables burners
powerbank = 'powerbank', -- one-shot charge; '' disables the item
wiper = 'phone_wiper', -- secure wipe; '' disables it
}Menu: Device → Items. Item names can be up to 60 characters. A name you add is ready to use as soon as you save.
Phone Numbers
| Setting | Type | Default | Description |
|---|---|---|---|
Config.Numbers.format | string | '@-###-####' | How new numbers look. Each # becomes a random digit and each @ one of the prefixes. May only contain #, @, digits and -, and needs at least one #; anything else falls back to the default. The default gives numbers like 213-555-0142 |
Config.Numbers.prefixes | table (list of strings) | ten Southern California area codes (below) | The area codes used in place of @. Digits only, all the same length |
Config.Numbers = {
format = '@-###-####',
prefixes = { '213', '310', '323', '424', '562', '626', '714', '818', '909', '949' },
}Menu: Device → Numbers & SIMs → Phone numbers. Existing numbers never change. Players can type a number with or without dashes; the phone lays it out in your format.
SIM Cards
| Setting | Type | Default | Description |
|---|---|---|---|
Config.Sim.autoIssue | boolean | true | A phone used for the first time without a SIM gets a SIM registered to that character, so it has a number straight away. Turn off to make players buy or find SIM cards |
Config.Sim.replacementCost | number | 0 | What a player pays for a replacement SIM after reporting theirs lost or stolen. 0 makes it free |
Config.Sim.replacementAccount | string | 'bank' | Where the replacement is paid from: 'bank' or 'cash' |
Config.Sim = {
autoIssue = true,
replacementCost = 0,
replacementAccount = 'bank',
}Menu: Device → Numbers & SIMs → SIM cards (Free SIM with a new phone, Replacement SIM price, Paid from).
Screen Lock
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Lock.minLength | number | 4 | 4 to 8 | Shortest passcode, in digits |
Config.Lock.maxLength | number | 6 | 4 to 8 | Longest passcode, in digits |
Config.Lock.maxAttempts | number | 5 | 1 to 20 | Wrong passcodes in a row before the phone locks itself out |
Config.Lock.lockoutSeconds | number | 300 | 10 to 86400 | How long a lockout lasts, in seconds |
Config.Lock = {
minLength = 4,
maxLength = 6,
maxAttempts = 5, -- wrong passcodes in a row before a lockout
lockoutSeconds = 300, -- 5 minutes
}Menu: Device → Security → Screen lock. Passcodes are stored scrambled (hashed), so nobody can read them back. Admins remove a forgotten one with /phone resetpin.
Battery
Batteries are off by default: phones never run flat.
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Battery.enabled | boolean | false | Turns battery drain on. When off, phones never run flat and the powerbank says so | |
Config.Battery.drainPerMinute | number | 0.2 | 0 to 10 | Percent lost per minute while the phone is switched on, even in a pocket. 0.2 lasts about 8 hours |
Config.Battery.inUsePerMinute | number | 0.6 | 0 to 10 | Extra percent lost per minute while the screen is open |
Config.Battery.torchPerMinute | number | 0.4 | 0 to 10 | Extra percent lost per minute while the phone's torch shines |
Config.Battery.lowPowerFactor | number | 0.6 | 0.1 to 1 | Low Power multiplies every drain by this (0.6 drains 40% slower) |
Config.Battery.vehiclePerMinute | number | 3.0 | 0 to 50 | Percent gained per minute while the holder sits in a vehicle. 0 turns car charging off |
Config.Battery.powerbankCharge | number | 50 | 1 to 100 | Percent one powerbank adds |
Config.Battery = {
enabled = false, -- off: phones never run flat
drainPerMinute = 0.2, -- while switched on; 0.2 lasts about 8 hours
inUsePerMinute = 0.6, -- extra while the screen is open
torchPerMinute = 0.4, -- extra while the torch is on
lowPowerFactor = 0.6, -- Low Power multiplies every drain by this
vehiclePerMinute = 3.0, -- charge gained while sitting in a vehicle
powerbankCharge = 50, -- percent a powerbank adds
}Menu: World → Battery. A flat or switched-off phone gets no calls, texts or mail (texts and mail wait) and cannot be traced.
Charging Stations
Kiosks where players charge their phones: the phone's oxide_phone_charger model. They only do something while batteries are on (Config.Battery.enabled). How players use them is in Features → Charging Stations.
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Chargers.nearby | boolean | true | Phones a player carries charge while they stand near a station | |
Config.Chargers.nearbyRadius | number | 3.0 | 1 to 15 | How close to a station's shelf counts as near, in metres |
Config.Chargers.nearbyPerMinute | number | 2.0 | 0.1 to 50 | Percent gained per minute near a station |
Config.Chargers.docking | boolean | true | Players can leave a phone on one of a station's three pads. Phones already on a pad can always be taken back, even when this is off | |
Config.Chargers.dockPerMinute | number | 5.0 | 0.1 to 50 | Percent gained per minute on a pad. 5 charges a flat phone in 20 minutes |
Config.Chargers.takeBack | string | 'anyone' | Who can take a phone off a pad: 'anyone', or 'owner' (the player who left it there, or the phone's registered owner) | |
Config.Chargers.policeCanTake | boolean | true | With 'owner', police on duty (the jobs in Evidence) can still take any phone off a pad | |
Config.Chargers.stations | table (rows) | {} | 50 rows | The stations. Each row is { label = 'Name', coords = vector4(x, y, z, heading) }. The phone puts the station model at each one, unless your map already has one at that spot |
Config.Chargers = {
nearby = true,
nearbyRadius = 3.0,
nearbyPerMinute = 2.0,
docking = true,
dockPerMinute = 5.0,
takeBack = 'anyone', -- or 'owner'
policeCanTake = true,
stations = {}, -- { label = 'Name', coords = vector4(x, y, z, heading) }
}Menu: World → Battery → Charging stations. Each option shows a short hint under its name. Changes, new stations included, take effect for everyone as soon as you save.
Place a station in game (this uses o-link's in-world placement):
Stand where the station should go, type /phone settings and open World → Battery.
Scroll to Charging stations → Stations and press Add.
Type a Name, for example the place it stands. Only you see it.
Press the crosshair button next to Spot. The menu hides and a see-through station follows your aim. The keys show on screen.
Scroll to turn it so its pads face where players will stand, then left-click to put it down. Right-click or Esc cancels.
The menu comes back with X, Y, Z and W (the way it faces) filled in. Press Save Changes. The station appears for every player.
The person button next to the crosshair takes you to a saved spot.
Use a station you placed with your own map tools: add a row the same way, and in step 4 aim at your station. The see-through station snaps onto it, so the spot matches it exactly. The phone then sees the model already there and doesn't spawn a second one. A station model on your map that isn't in this list charges nothing: the server only trusts its own list.
Move or remove a station: change its position or delete its row, then save. Phones lying on its pads go back to the players who left them, the next time they are online.
Calls
Calls need pma-voice. See Installation → Calls.
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Calls.ringSeconds | number | 30 | 5 to 120 | An unanswered call ends after this many seconds and becomes a missed call |
Config.Calls.ringRange | number | 15.0 | 0 to 50 | Metres within which other players hear a phone ring. 0 means nobody hears it. A phone whose owner turned sound off or uses Do Not Disturb never rings out loud |
Config.Calls.ringSound.name | string | 'Text_Arrive_Tone' | The GTA sound played on the ringing phone | |
Config.Calls.ringSound.set | string | 'Phone_SoundSet_Default' | The GTA sound set that sound is in | |
Config.Calls.ringSound.interval | number | 2500 | 500 to 10000 | How often the sound repeats, in milliseconds |
Config.Calls.voicemailSeconds | number | 30 | 5 to 120 | Longest voicemail message, in seconds. Never longer than Config.Media.audio.maxSeconds. Voicemail needs photo storage |
Config.Calls.voicemailDays | number | 30 | 1 to 365 | Voicemail is deleted after this many days. Police can still recover it for the recovery window |
Config.Calls.answerKey | string | 'RETURN' | Default key that answers a call with the phone put away. Only for players who have never joined; everyone else rebinds it in FiveM's key bindings | |
Config.Calls.declineKey | string | 'BACK' | Default key that declines a call with the phone put away. Same rule as answerKey |
Config.Calls = {
ringSeconds = 30, -- an unanswered call ends after this
ringRange = 15.0, -- metres within which other players hear a phone ring
ringSound = { name = 'Text_Arrive_Tone', set = 'Phone_SoundSet_Default', interval = 2500 },
voicemailSeconds = 30,
voicemailDays = 30,
answerKey = 'RETURN',
declineKey = 'BACK',
}Menu: Device → Calls & Messages → Calls.
Texts
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Messages.maxLength | number | 500 | 20 to 2000 | Longest text, in characters |
Config.Messages.groupSize | number | 8 | 2 to 32 | People in one group text, the sender included |
Config.Messages.perMinute | number | 20 | 1 to 120 | Texts one player may send per minute |
Config.Messages.queueDays | number | 7 | 1 to 60 | A text for a phone that stays off or out of signal is dropped after this many days |
Config.Messages.voiceSeconds | number | 60 | 5 to 300 | Longest voice message, in seconds. Never longer than the voice clip limit in Config.Media.audio |
Config.Messages = {
maxLength = 500,
groupSize = 8,
perMinute = 20,
queueDays = 7,
voiceSeconds = 60,
}Menu: Device → Calls & Messages → Texts.
Contacts
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Contacts.max | number | 200 | 10 to 1000 | Contacts one phone can hold |
Config.Contacts.shareRange | number | 3.0 | 1 to 10 | How close, in metres, two players must stand to share something nearby (a contact, photo, place, note or post) |
Config.Contacts.shareSeconds | number | 60 | 10 to 300 | How long a nearby share waits to be accepted, in seconds |
Config.Contacts = {
max = 200,
shareRange = 3.0,
shareSeconds = 60,
}Menu: Device → Calls & Messages → Contacts. Contacts are stored on the phone, not on the character.
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Mail.maxPerPhone | number | 200 | 20 to 2000 | Mail one phone keeps. The oldest is removed beyond this |
Config.Mail.pendingDays | number | 30 | 1 to 365 | Mail for someone who has no phone is dropped after this many days |
Config.Mail.domain | text | 'phone.mail' | up to 40 characters | Every SIM's address is its digits at this domain, and aliases end in it. Changing it changes every address |
Config.Mail.perMinute | number | 6 | 1 to 60 | Mail one phone may send per minute |
Config.Mail.perHour | number | 40 | 1 to 500 | Mail one phone may send per hour |
Config.Mail.transactionWords | list | bank, shop, store, receipt, invoice, payment | each word up to 24 characters | Mail from a resource whose sender name has one of these words goes to Transactions; the rest goes to Updates. Mail between players goes to Primary |
Config.Mail = {
maxPerPhone = 200,
pendingDays = 30,
domain = 'phone.mail',
perMinute = 6,
perHour = 40,
transactionWords = { 'bank', 'shop', 'store', 'receipt', 'invoice', 'payment' },
}Menu: Device → Calls & Messages → Mail. Mail sent to a character without a phone waits and arrives on the next phone that holds one of their registered SIMs.
Photo Storage
The phone stores no photos itself. See Installation → Photos for setting up each service.
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Media.provider | string | 'none' | 'none', 'fivemanage', 's3', 'custom' | Where photos go. 'none' turns the camera off (it says photos are not set up). 'custom' means another resource hands out upload addresses |
Config.Media.maxKB | number | 2048 | 64 to 10240 | Largest photo, in kilobytes |
Config.Media.perMinute | number | 6 | 1 to 60 | Photos one player may upload per minute |
Config.Media.keepPerPhone | number | 500 | 10 to 5000 | Photos, videos and audio clips one phone can hold. A full phone refuses new ones until some are deleted |
Config.Media.video.maxSeconds | number | 60 | 5 to 180 | Longest video, in seconds. Recording stops and saves at this length |
Config.Media.video.fps | number | 24 | 24 or 30 | Video frame rate |
Config.Media.video.kbps | number | 1200 | 500 to 3000 | Video bitrate. Higher looks sharper and makes bigger files (about 9 MB a minute at 1200) |
Config.Media.video.maxMB | number | 12 | 1 to 50 | Largest video, in megabytes |
Config.Media.video.perMinute | number | 3 | 1 to 20 | Videos one player may upload per minute |
Config.Media.video.perPhoneMB | number | 250 | 10 to 5000 | All the videos on one phone together, in megabytes |
Config.Media.audio.maxSeconds | number | 120 | 5 to 300 | Longest voicemail or voice message, in seconds |
Config.Media.audio.maxKB | number | 1024 | 64 to 10240 | Largest voicemail or voice message, in kilobytes |
Config.Media.audio.perMinute | number | 3 | 1 to 20 | Voice clips one player may upload per minute |
Config.Media.hosts | table (list of strings) | {} | Extra web hosts photos may come from, like cdn.example.com (subdomains included). Needed for custom storage. Photos from anywhere else are never shown | |
Config.Media.fivemanage.endpoint | string | 'https://api.fivemanage.com/api/v3/file/presigned-url' | Fivemanage's upload address API. Must be on https://api.fivemanage.com/; anything else uses the default | |
Config.Media.s3.endpoint | string | '' | Your S3 or R2 endpoint, starting with https:// | |
Config.Media.s3.region | string | 'auto' | The bucket's region. auto for R2 | |
Config.Media.s3.bucket | string | '' | The bucket name | |
Config.Media.s3.publicUrl | string | '' | The https:// address the bucket is served from, like https://photos.example.com. Its host is automatically allowed | |
Config.Media.s3.prefix | string | 'phone' | The folder inside the bucket photos go into |
Config.Media = {
provider = 'none', -- 'none' | 'fivemanage' | 's3' | 'custom'
maxKB = 2048, -- largest photo
perMinute = 6, -- photos one player may upload per minute
keepPerPhone = 500, -- photos, videos and audio clips a phone can hold
hosts = {}, -- extra hosts photos may come from (custom storage)
video = {
maxSeconds = 60, -- longest clip (5-180)
fps = 24, -- 24 or 30
kbps = 1200, -- recording bitrate (500-3000)
maxMB = 12, -- largest clip
perMinute = 3, -- clips one player may upload per minute
perPhoneMB = 250, -- all the videos on one phone together
},
audio = { maxSeconds = 120, maxKB = 1024, perMinute = 3 }, -- voicemail and voice messages
fivemanage = { endpoint = 'https://api.fivemanage.com/api/v3/file/presigned-url' },
s3 = { endpoint = '', region = 'auto', bucket = '', publicUrl = '', prefix = 'phone' },
}Menu: Device → Media → Photo storage (Video and Audio are inside it). The keys for Fivemanage and S3 go in server.cfg, never here. With Fivemanage, photos and videos are allowed from fivemanage.com and fmapi.net.
Camera
With the camera out on foot, W walks where the player and the camera face, and the mouse turns the player (the selfie camera too).
| Key | Type | Default | Description |
|---|---|---|---|
Config.Camera.sprint | boolean | false | Off, players cannot sprint with the camera out and move at their normal pace. On, they can sprint |
Config.Camera.modeKey | string | 'F7' | Initial key for changing camera mode. Players can rebind it in FiveM's key bindings. Restart the phone after changing this default |
Config.Camera = { sprint = false, modeKey = 'F7' }Menu: Device → Media → Camera.
Live Streams
Apps on the phone (such as Leakd and Glitch) can let players go live with the phone's camera. The video goes through your server to every viewer, so each viewer costs your server upload. A viewer counts as the video bitrate sent as base64 (4/3 of it) plus a tenth for sound and events: kbps × 4/3 × 1.1, about 1.47 times the bitrate. At the default 500 kbps that is about 0.73 Mbps a viewer, so 25 viewers count as about 18 Mbps. A stream that really sends more than that counts at what it sends.
| Key | Type | Default | Range | Description |
|---|---|---|---|---|
Config.Streams.enabled | boolean | true | Off refuses every new stream | |
Config.Streams.maxViewers | number | 25 | 1 to 200 | Viewers per stream. Anyone past this is told the stream is full |
Config.Streams.maxStreams | number | 4 | 1 to 50 | Streams live at once on the server, every app together |
Config.Streams.kbps | number | 500 | 150 to 2000 | Video bitrate. Each viewer costs about 1.47 times this in upload |
Config.Streams.fps | number | 20 | 10 to 30 | Frames per second |
Config.Streams.egressMbps | number | 40 | 5 to 1000 | Upload your server may spend on streams, in Mbps. A viewer who would pass it is told the server is busy |
Config.Streams.maxMinutes | number | 60 | 1 to 240 | Longest stream, in minutes |
Config.Streams = { enabled = true, maxViewers = 25, maxStreams = 4, kbps = 500, fps = 20, egressMbps = 40, maxMinutes = 60 }Menu: Device → Media → Live streams. Admins can list and end streams with /phone streams and /phone endstream (see Admin).
Evidence
Everything police do with phones. Every check runs on the server. See Features → Police and Evidence.
A job row is { job = 'name', rank = number }: that job at that grade or higher.
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Evidence.jobs | table (job rows) | { { job = 'police', rank = 0 } } | up to 20 rows | Who counts as police. They see the Forensics app and open other people's phones in evidence mode |
Config.Evidence.authorizers | table (job rows) | { { job = 'police', rank = 3 } } | up to 20 rows | Who may issue and revoke search authorizations (police command, or a judge job) |
Config.Evidence.requireDuty | boolean | true | Police and authorizers must be on duty to use Forensics. Opening someone else's phone in evidence mode works on or off duty | |
Config.Evidence.authorization | string | 'builtin' | 'builtin', 'none', 'hook' | 'builtin': an authorizer issues an authorization in Forensics. 'none': police need none. 'hook': another resource decides (see Evidence Bridge) |
Config.Evidence.maxAuthorizationHours | number | 72 | 1 to 720 | The longest an authorization can last, in hours |
Config.Evidence.extractionSeconds | number | 60 | 5 to 600 | How long an extraction takes, in seconds |
Config.Evidence.requireStation | boolean | false | When on, extraction only works while standing at a forensics station. When off, police can also extract from the Forensics app anywhere | |
Config.Evidence.stations | table (rows) | {} | up to 30 rows | Forensics stations. Each row is { label, coords = vector3(x, y, z), radius }. radius is how close police must stand, in metres (0.5 to 10) |
Config.Evidence.recoveryDays | number | 7 | 0 to 90 | Deleted texts, contacts, calls, mail, photos and notes an extraction still finds, for this many days after deletion. It is also how long Recently Deleted in Photos and Messages keeps items. A secure wipe defeats it. 0 recovers nothing deleted |
Config.Evidence.carrierDays | number | 30 | 1 to 365 | How long carrier records (calls and texts per number) are kept, in days. They survive a wiped or destroyed phone |
Config.Evidence.traceCooldown | number | 120 | 0 to 3600 | Seconds before the same officer can trace the same number again |
Config.Evidence.traceRadius | number | 150.0 | 25 to 1000 | Radius, in metres, of the area a trace shows around the phone |
Config.Evidence.backends | table (rows) | { { name = 'oxide-police', enabled = true } } | up to 10 rows | Resources that also receive every extraction report (an MDT). Each row is { name = 'resource-name', enabled = true }. Reports are always kept in Forensics too |
Config.Evidence.darkChatRecovery | number | 0 | 0 to 100 | The chance, in percent, that an extraction recovers each Dark Chat message sent from the phone. 0 keeps Dark Chat out of reports |
Config.Evidence = {
jobs = { { job = 'police', rank = 0 } }, -- who is police here
authorizers = { { job = 'police', rank = 3 } }, -- who may issue search authorizations
requireDuty = true,
authorization = 'builtin', -- 'builtin' | 'none' | 'hook'
maxAuthorizationHours = 72,
extractionSeconds = 60,
requireStation = false,
stations = {}, -- { label, coords = vector3, radius }
recoveryDays = 7,
carrierDays = 30,
traceCooldown = 120,
traceRadius = 150.0,
backends = { { name = 'oxide-police', enabled = true } },
darkChatRecovery = 0,
}A station written in the file looks like this:
stations = {
{ label = 'Mission Row Lab', coords = vector3(0.0, 0.0, 0.0), radius = 1.5 }, -- use your own position
},Menu: Police → Evidence. Use the job names exactly as your framework spells them. Always give a station a Reach (radius).
Apps
The phone's own apps.
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Apps.notes.max | number | 100 | 10 to 1000 | Notes one phone can hold. Deleted notes still inside the evidence recovery window count toward a second limit of three times this |
Config.Apps.maps.maxPlaces | number | 50 | 5 to 200 | Saved favourites one phone can hold (Home, Work and the parked car come on top). Deleted favourites and replaced Homes, Works and parked cars still inside the evidence recovery window count toward a second limit of three times this, per kind |
Config.Apps.darkchat.enabled | boolean | true | Turns Dark Chat on or off. When off, it disappears from every phone | |
Config.Apps.darkchat.perMinute | number | 15 | 1 to 60 | Dark Chat messages one player may send per minute |
Config.Apps.darkchat.maxRooms | number | 20 | 1 to 100 | Dark Chat rooms one SIM card can be in |
Config.Apps.feed.enabled | boolean | true | Turns Feed on or off | |
Config.Apps.feed.perMinute | number | 3 | 1 to 20 | Feed posts one player may make per minute |
Config.Apps.feed.maxLength | number | 280 | 20 to 600 | Longest Feed post, in characters |
Config.Apps.feed.postsPerHour | number | 30 | 1 to 500 | Feed posts one SIM may make in an hour |
Config.Apps.feed.editMinutes | number | 15 | 0 to 60 | How long a Feed post can be edited. Every earlier version is kept as evidence. 0 turns editing off |
Config.Apps.feed.trendHours | number | 24 | 1 to 168 | The hours trending hashtags are counted over |
Config.Apps.bank.enabled | boolean | true | Turns the Wallet app on or off | |
Config.Apps.bank.maxTransfer | number | 50000 | 1 to 10000000 | Largest single transfer |
Config.Apps.bank.dailyLimit | number | 100000 | 1 to 100000000 | The most one character can send in 24 hours |
Config.Apps.bank.perMinute | number | 3 | 1 to 20 | Transfers one player may make per minute |
Config.Apps.bank.requestsPerDay | number | 30 | 1 to 200 | Payment requests one character can send in 24 hours |
Config.Apps.bank.requestHours | number | 24 | 1 to 168 | How long a payment request can be paid |
Config.Apps.gigs.enabled | boolean | true | Turns the Gigs app on or off. It shows only while an Oxide gig job runs. When off, the gig jobs send their shift updates as texts |
Config.Apps = {
notes = { max = 100 },
maps = { maxPlaces = 50 }, -- saved places (Home, Work, favourites, parked car) per phone
darkchat = { enabled = true, perMinute = 15, maxRooms = 20 },
feed = {
enabled = true,
perMinute = 3,
maxLength = 280,
postsPerHour = 30, -- posts one number may make in an hour
editMinutes = 15, -- how long a post can be edited (0 turns editing off)
trendHours = 24, -- the hours trending hashtags are counted over
},
bank = {
enabled = true,
maxTransfer = 50000, -- largest single transfer
dailyLimit = 100000, -- most one character sends in 24 hours
perMinute = 3,
requestsPerDay = 30, -- payment requests one character may send in 24 hours
requestHours = 24, -- how long a payment request can be paid
},
gigs = { enabled = true }, -- the Oxide gig jobs that run here; off keeps their updates as texts
}Menu: Screen → Apps.
Emergency Numbers
| Setting | Type | Default | Description |
|---|---|---|---|
Config.Emergency.enabled | boolean | true | Turns emergency numbers on or off. When off, the lock screen has no Emergency call button and 911/311 are treated as normal numbers |
Config.Emergency.numbers | table (rows) | 911 and 311 (below) | Each row: number (up to 6 characters), channel ('911' emergency or '311' non-emergency) and jobs (the jobs alerted when no dispatch resource runs; police when empty) |
Config.Emergency = {
enabled = true,
numbers = {
{ number = '911', channel = '911', jobs = { 'police', 'ambulance' } },
{ number = '311', channel = '311', jobs = { 'police' } },
},
}Menu: Police → Emergency. Up to 10 numbers. See Features → Emergency Numbers for where a call goes.
Signal
| Setting | Type | Default | Description |
|---|---|---|---|
Config.Signal.indoorsNoSignal | boolean | false | When on, a player inside any interior has no signal on their own phone: they cannot call, text, post or send an emergency call from inside. Calls and texts to them still arrive |
Config.Signal = {
indoorsNoSignal = false,
}Menu: World → Signal → Signal (No signal indoors).
No-Signal Areas
| Setting | Type | Default | Description |
|---|---|---|---|
Config.DeadZones | table (list of areas) | {} | Areas with no signal, such as tunnels and basements. Inside one, a phone cannot call or text, gets no calls or texts (texts wait), and an active call drops |
Config.DeadZones = {}Menu: World → Signal → No-signal areas. Draw each area in the world:
Press Draw a zone. The menu hides and o-link's zone builder starts: place the corners of the area in the world.
Finish the shape. The menu comes back and the area appears in the list with its number of points.
Give it a Zone name if you like, then press Save Changes.
Redraw in the world reshapes an existing area. Each area needs 3 to 64 points, and you can have up to 100 areas. Drawing needs an o-link with in-world placement; with an older one the menu says so.
Store
| Setting | Type | Default | Description |
|---|---|---|---|
Config.Store.enabled | boolean | true | Players install apps on each phone from the Store. When off, the Store disappears and every app a player may use is on every phone |
Config.Store.preinstalled | table (list of app IDs) | {} | Apps on every phone that players cannot remove. Use app IDs from the App ID Reference or from the resource that adds the app |
Config.Store.simulateInstalls | boolean | true | Getting an app downloads and installs it over a few seconds, as on a real phone. When off, apps install at once |
Config.Store.installSpeed | number (0.1 to 20) | 1 | Download speed multiplier. At 1 a download runs at about 6 MB a second on full signal; 2 halves the wait |
Config.Store.defaultAppSize | number, MB (1 to 4000) | 50 | The size of apps that do not declare one. At 1, a 50 MB app takes about 11 seconds |
Config.Store = {
enabled = true,
preinstalled = {}, -- e.g. { 'phone_darkchat', 'phone_feed' }
simulateInstalls = true,
installSpeed = 1,
defaultAppSize = 50,
}Menu: Screen → Home & Apps → Store.
How a download behaves. Tapping Get turns the button into a ring (tap it to cancel) and puts the app's icon on the next free spot of the home screen, dimmed, with a progress pie. Its label reads Waiting…, Loading…, then Installing…; then the icon brightens. Tapping the icon pauses or resumes the download (Paused); holding it offers Cancel Download. The app cannot open, notify or show widgets until it is installed.
- Time: the app's size divided by the download speed, with a little random variation, plus about a second before it starts and one to four seconds to install. Most apps take 5 to 20 seconds.
- Signal: weak signal slows the download. No signal, Airplane Mode or a phone that is off or flat pauses it on Waiting… until the signal returns.
- Kept by the server: closing the phone, logging out or restarting the resource never loses a download. A phone nobody is holding finishes on time in the background, and the app is there the next time it is opened.
- Not for every app: built-in apps and preinstalled apps are always there and never download. Removing an app is still instant.
Default Look
How every phone looks until its owner picks something else in Settings → Appearance on the phone. A player's own choice always wins. A player who taps Use server default goes back to these values.
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Appearance.mode | string | 'dark' | 'dark', 'light', 'auto' | Dark or light screens. 'auto' is dark from 20:00 to 06:00 game time and light the rest of the day |
Config.Appearance.accent | string | 'blue' | 'blue', 'red', 'green', 'orange', 'purple', 'pink', 'graphite' | The colour of buttons, links and switches |
Config.Appearance.glass | number | 0.5 | 0 to 1 | How see-through the glass panels are (dock, folders, sheets). 0 is clearer, 1 is more tinted |
Config.Appearance = {
mode = 'dark',
accent = 'blue',
glass = 0.5,
}Menu: Screen → Home & Apps → Default Look. A change reaches each phone the next time it is opened.
With Oxide HUD running, the card that rises while the phone is put away keeps the phone's own colours: there is no setting to give it Oxide HUD's accent colour. See Features → With Oxide HUD.
Home Screen
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Home.dockSize | number | 4 | 0 to 4 | Apps in the dock at the bottom of the home screen |
Config.Home.maxPages | number | 4 | 1 to 6 | Home screen pages |
Config.Home.wallpapers | table (rows) | {} | up to 30 rows | Extra wallpapers besides the five built-in ones. Each row is { id, label, url }. The url is an https:// address or the name of a .png, .jpg, .jpeg or .webp file in the wallpapers/ folder |
Config.Home.wallpaperLinks | table (list) | {} | up to 100 entries | Sites players may set a wallpaper from by pasting an image link. An entry is a site (images.example.com, which covers its subdomains too) or an https:// address links must start with (https://cdn.example.com/wallpapers/). Empty: players cannot use links |
Config.Home = {
dockSize = 4,
maxPages = 4,
wallpapers = {}, -- { id = 'skyline', label = 'Skyline', url = 'skyline.jpg' }
wallpaperLinks = {}, -- e.g. { 'images.example.com', 'https://cdn.example.com/wallpapers/' }
}A link is accepted only when it is an https:// address ending in .png, .jpg, .jpeg or .webp, with nothing after it (no ? or #), at most 512 characters, on an approved site. The phone checks this when the player adds the link and the server checks it again on every save; a site you later remove sends those phones back to the default wallpaper. The image loads from that site in the player's game, so approve only sites you trust.
Menu: Screen → Home & Apps → Home screen. Lowering a limit trims existing home screens to fit. See Installation → Custom Wallpapers.
Notifications
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Notifications.bannerDuration | number | 4000 | 1000 to 15000 | How long a banner stays on screen, in milliseconds |
Config.Notifications.sound.enabled | boolean | true | Turns the notification sound on or off for everyone | |
Config.Notifications.sound.volume | number | 0.5 | 0 to 1 | Loudness of the notification sound. Each player's own volume setting is applied on top |
Config.Notifications.stored | number | 100 | 10 to 500 | Notifications a phone keeps until the player clears them. The oldest go first |
Config.Notifications = {
bannerDuration = 4000, -- ms a banner stays on screen
sound = { enabled = true, volume = 0.5 },
stored = 100, -- notifications kept per phone
}Menu: Screen → Notifications. Players can also mute apps, turn banners or sound off, or use Do Not Disturb in their phone's Settings app.
App Start Timeout
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.AppReadyTimeout | number | 8000 | 2000 to 60000 | How long, in milliseconds, an app from another resource may take to load before the phone shows an error with a Try again button |
Config.AppReadyTimeout = 8000Menu: Screen → Home & Apps, on the Advanced tab (App start timeout).
Held Phone
The phone in the character's hand while the phone is open. The phone ships its own model in four colours (see Assets → Phone Models). The colour belongs to the phone, not the player: a phone keeps its colour for good, and everyone nearby sees it.
| Setting | Type | Default | Description |
|---|---|---|---|
Config.Prop.colour | string | 'random' | 'black', 'silver', 'burgundy' or 'glacier' gives every phone that colour. 'random' gives each phone its own colour the first time it is opened, and it keeps it |
Config.Prop.itemColours | table (rows) | {} | A colour per phone item, for selling phones in colours. Each row is { item = 'item name', colour = 'colour' }. A row wins over colour |
Config.Prop.customModel | string | '' | Another model for every phone instead of the colours, for example 'prop_npc_phone_02'. Leave it empty for the colours |
Config.Prop.bone | number | 28422 | The hand bone it is attached to |
Config.Prop.offset | vector3 | vector3(0.0, 0.0, 0.0) | Moves the model from the bone, in metres |
Config.Prop.rotation | vector3 | vector3(0.0, 0.0, 0.0) | Turns the model, in degrees |
Config.Prop = {
colour = 'random',
itemColours = {},
customModel = '',
bone = 28422,
offset = vector3(0.0, 0.0, 0.0),
rotation = vector3(0.0, 0.0, 0.0),
}Menu: Screen → Appearance (Held phone). Colour per phone item is a list: press Add for each row.
The bundled phone sits in the hand with no offset and no rotation, in every pose. Leave Offset and Rotation at zero unless you set Other model to a model that needs them.
Sell phones in colours (optional):
Register one extra phone item per colour in your inventory, for example phone_silver, the same way as your phone item.
In /phone settings → Items, add each item to Phone items, so it opens as a phone.
In Appearance → Held phone → Colour per phone item, add a row for each item with its colour.
Press Save Changes. Each phone made from those items now shows its colour the next time it is opened.
Phones made from items with no row use Colour.
Config.ModelHints lists the models the phone streams itself, so a warning about one of them says the install is incomplete. It is not in the menu; leave it as it is.
Model Replacements
| Setting | Type | Default | Description |
|---|---|---|---|
Config.ModelSwaps | table (rows) | {} | Replace a model your game build lacks. Each row is { from = 'shipped model', to = 'your model' } |
Config.ModelSwaps = {} -- { { from = 'oxide_phone_black', to = 'your_model' } }Menu: Advanced → Compatibility → Model replacements. When a model can't load, the phone warns in the console, tells online admins, and skips it. Add a row using the model name exactly as the warning shows it.
Timers
How often the server checks phones.
| Setting | Type | Default | Menu limits | Description |
|---|---|---|---|---|
Config.Session.watchdogSeconds | number | 5 | 1 to 30 | How often an open phone is checked. It closes when it leaves the inventory, goes flat or is switched off |
Config.Session.scanSeconds | number | 15 | 5 to 120 | How often carried phones are looked up. Car charging and finding who holds a number use this scan |
Config.Session = {
watchdogSeconds = 5,
scanSeconds = 15,
}Menu: Advanced → Advanced, on the Advanced tab (Timers).
Setting Added by o-link: Map Mode
o-link adds one setting of its own to the phone's menu, for the Maps app. It is not in shared/config.lua.
| Setting | Type | Default | Description |
|---|---|---|---|
Config.MapMode | string | 'inherit' | How Maps draws the islands. 'inherit' uses the o-link default, 'combined' shows both islands on one map, 'separate' shows separate island views, 'disabled' hides Cayo Perico |
Menu: Device → General (Map mode). Admins can also set it with oxide-phone:mapmode (see Admin).
App ID Reference
Use these IDs in Config.Store.preinstalled.
| App | ID | Notes |
|---|---|---|
| Phone | phone_calls | Always on every phone |
| Messages | phone_messages | Always on every phone |
phone_mail | Always on every phone | |
| Camera | phone_camera | Always on every phone |
| Photos | phone_gallery | Always on every phone |
| Notes | phone_notes | Always on every phone |
| Calculator | phone_calculator | Always on every phone |
| Clock | phone_clock | Always on every phone |
| Maps | phone_maps | Always on every phone |
| Wallet | phone_bank | Always on every phone, beside any bank app, unless turned off |
| Gigs | phone_gigs | On every phone while at least one Oxide gig job runs, unless turned off |
| Settings | phone_settings | Always on every phone |
| Store | phone_store | While Config.Store.enabled is on |
| Forensics | phone_forensics | Only for the jobs in Config.Evidence |
| Dark Chat | phone_darkchat | From the Store |
| Feed | phone_feed | From the Store |
| Demo | phone_demo | From the Store, only while Config.Debug is on |
Apps from other resources have their own IDs; oxide-banking's app is banking.
Next Steps
Installation Guide
Step-by-step setup for oxide-phone — what's in the package, dependencies, load order, the items for each inventory, database, server convars, calls, photo storage, police evidence, emergency numbers, apps, custom wallpapers, translations, admin access, updating and verification.
Features
What oxide-phone does in game — phones and SIM cards, battery, signal and the screen lock, the home screen, Store and widgets, notifications, calls, messages, contacts, mail, camera and photos, the built-in apps, emergency numbers, police evidence, erase and Find Phone, and apps from other resources.