TSS Bridge exposes explicitly selected, realm-specific modules through a FiveM export. Bridge code and adapter state remain inside tss_bridge; consuming resources receive only the public methods they request.
Declare the dependency without injecting bridge files:
-- fxmanifest.lua
dependency 'tss_bridge'
client_scripts { 'client/*.lua' }
server_scripts { 'server/*.lua' }Request modules separately in each realm:
-- client/main.lua
local TSS = exports.tss_bridge:GetModules({
'Framework',
'Player',
'Notify',
'Interaction',
})-- server/main.lua
local TSS = exports.tss_bridge:GetModules({
'Framework',
'Player',
'Banking',
'Inventory',
'Notify',
})Module names are case-insensitive and returned under their canonical names. An unknown module, malformed request, or module unavailable in the current realm produces a startup error.
The old @tss_bridge/starter.lua injection entry point no longer exists.
Any resource that calls exports.tss_bridge:GetModules(...) is checked once by
the bridge. No version-check module or extra script is required. By default the
bridge reads:
https://github.com/TinySpriteScripts/version_check
and requests <resource-name>-version.txt from its main branch. A version
file contains the latest version, expected resource name, and support link:
1.2.2
tss-comicbook
https://discord.gg/example
Third-party resources can select another public repository in their manifest:
version '1.2.2'
version_check_repo 'https://github.com/example/public-versions'Normal GitHub repository links, GitHub /tree/<branch> links, raw HTTPS base
URLs, and direct .txt URLs are supported. A direct URL may contain
{resource} (or %s), which is replaced with the invoking resource name. Repository and
base URLs have <resource-name>-version.txt appended. The resource name must
match line two of the version file so renamed installations are reported.
To disable checking for one resource, add:
suppress_updates 'true'The bridge includes documentation types in shared/types.lua. Add the realm type
above each module request to get hover documentation, parameter hints, and
completion in Lua Language Server:
-- client/main.lua
---@type TSSClientModules
local TSS = exports.tss_bridge:GetModules({ 'Notify', 'Interaction' })-- server/main.lua
---@type TSSServerModules
local TSS = exports.tss_bridge:GetModules({ 'Callbacks', 'Banking', 'Inventory' })Each public function also has an EmmyLua/LuaLS documentation block in its
implementation. The type catalog records the client/server signature differences;
for example, server money mutation methods do not exist on TSSClientModules.
Defaults live in shared/config.lua:
TSS.Config.Framework = 'auto'
TSS.Config.Notify = 'auto'
TSS.Config.ProgressBar = 'auto'
TSS.Config.Target = 'auto'
TSS.Config.Inventory = 'auto'
TSS.Config.TextUI = 'auto'
TSS.Config.Interaction = 'auto'
TSS.Config.VehicleKeys = 'auto'
TSS.Config.Locale = 'en'
TSS.Config.CallbackTimeout = 10000Convars override the Lua defaults when set:
set tss_framework qb
set tss_notify ox
set tss_progressbar ox
set tss_target qb
set tss_inventory qb
set tss_textui qb
set tss_interaction auto
set tss_vehiclekeys qb
set tss_locale en
set tss_callback_timeout 10000
set tss_debug falseCommon aliases and resource names are accepted, including qb, qb-core, qbox, qbx_core, esx, es_extended, ox, and ox_lib where relevant.
Authorized Bridge Lab users can also choose the nine adapter defaults from the in-game Configuration menu. The effective priority is:
- A convar present in
server.cfg(shown as Blocked by server.cfg and not editable in the lab). - A selection saved through the Bridge Lab.
- The default in
shared/config.lua.
Lab selections are persisted in the server resource KVP store and synchronized to connected clients. Remove the corresponding convar and restart FXServer before attempting to edit a previously convar-controlled setting.
| Module | Client | Server |
|---|---|---|
Framework |
Framework detection and current player data | Framework detection and player lookup |
Player |
Read-only current player data | Read-only player lookup and permissions |
Banking |
Player account reads and provider detection | Player mutations and society accounts |
Billing |
Opens the selected billing interface | Unavailable |
Inventory |
Read/open operations | Read, mutation, stash and open operations |
Notify |
Adapter selection and local notifications | Notifications delivered to a player source |
TextUI |
Local show/hide/state | Show/hide for a player source |
Callbacks |
Register client callbacks and invoke server callbacks | Register server callbacks and invoke client callbacks |
Interaction |
Target or proximity/TextUI interactions | Unavailable |
Targets |
Entity, model and zone targets | Unavailable |
ProgressBar |
Progress UI | Unavailable |
VehicleKeys |
Read-only Has |
Has, Give, Remove |
UI |
Notify, progress, menu, context and input | Remote notify/progress |
Blips, Peds, Props, World, Gizmo |
Client entity/world and transform-editor helpers | Unavailable |
Vehicles |
Create/Delete/Clear plus Inspect, Current, FromNetworkId, Nearby, TrunkPosition, SetTrunkOpen | Inspect, Current(source), FromNetworkId |
Groups |
Boss-grade lookup | Boss-grade lookup |
Locale, Capabilities, Diagnostics |
Available | Available |
Debug |
Debug tools | Secured debug persistence |
Mutation methods are not merely guarded with IsDuplicityVersion(); they are absent from client module projections.
Modules self-register from modules/<name>/client.lua and
modules/<name>/server.lua. Realm-specific wildcards in fxmanifest.lua
discover them automatically, so there is no central client registry, server
registry, or manifest list to update. Server factories are never loaded as
client scripts.
Every module now owns its complete implementation directory. There is no
shared/modules implementation tree:
modules/
└── money/
├── client.lua # client API
├── server.lua # server API and authoritative mutations
└── shared.lua # read logic genuinely used by both realms
Module-specific integrations stay in the same directory as well; for example,
all integrations use modules/<name>/adapters/:
modules/
└── notify/
├── client.lua
├── server.lua
├── shared.lua
└── adapters/
├── ox.lua
├── qb.lua
├── qbox.lua
└── native.lua
Framework, Inventory, Banking, Billing, Groups, Notify, ProgressBar, Targets, Interaction, TextUI and VehicleKeys all share the same adapter registry. The registry handles aliases, config selection, priority-based auto detection, availability checks and fallbacks. Public module files only normalize arguments and expose the stable bridge API; resource-specific exports stay in adapter files.
An adapter registers itself after the module's shared.lua creates its registry:
-- modules/example/adapters/provider.lua
TSS.Example.RegisterAdapter('provider', 'provider_resource', {
supports = { read = true },
read = function(resource, value)
local ok, result = TSS.CallExport(resource, 'Read', value)
return ok and result or nil
end,
}, {
priority = 20,
aliases = { 'provider-resource', 'legacy-provider-name' },
})Add that adapter path to the module descriptor's lazy files list. There is no
detection switch or central adapter table to edit. fxmanifest.lua already
packages every modules/**/adapters/*.lua file for clients.
For example, a client module is one file:
-- modules/example/client.lua
TSSModuleLoader.Define({
name = 'Example',
dependencies = { 'Framework' },
client = function(context)
local Example = {}
--- Returns the resource that requested this module.
---@return string resource
function Example.Consumer()
return context.Caller
end
return Example
end,
})A server implementation lives separately:
-- modules/example/server.lua
TSSModuleLoader.Define({
name = 'Example',
server = function(context)
local Example = {}
--- Performs a server-authoritative check.
---@param source integer Trusted server player ID.
---@return boolean allowed
function Example.CanUse(source)
return source > 0 and IsPlayerAceAllowed(source, 'example.use')
end
return Example
end,
})To add a function to an existing module, edit only its client.lua or
server.lua factory and add the function before returning the public table. A
new one-realm module needs one file; a module available in both realms needs two.
Updating shared/types.lua is optional at runtime, but recommended when the new
function should appear in consumer autocomplete.
To add support for another provider, add one adapter file and include it in that module's descriptor. Existing adapter files and the shared registry do not need changes.
Descriptor options:
dependenciesinitializes other modules first without exposing them to the consumer.filescontains implementation files loaded lazily in both realms.clientFilesandserverFilescontain realm-specific lazy implementation files.clientandserverare independent API factories in their corresponding files.context.Calleridentifies the consuming resource.context.Optionscontains the options passed toGetModules.context.CopyFunctionssafely copies existing implementation functions.context.OnStopregisters cleanup scoped to the consuming resource.
Factories must return a table containing only named functions. Invalid public
values fail during GetModules rather than silently exposing internal state.
Supported systems:
- TSS built-in UI
ox_lib- QB-Core
- Qbox
okokNotifywasabi_notifymythic_notify- Native GTA fallback
Auto-detection prefers ox, Qbox, QB-Core, okok, Wasabi, Mythic, TSS, then native.
-- Client
local TSS = exports.tss_bridge:GetModules({ 'Notify' })
TSS.Notify.Send({
id = 'item_added',
title = 'Inventory',
message = 'Water added',
type = 'success',
duration = 5000,
position = 'top-right',
icon = 'check',
})
print(TSS.Notify.System())
print(TSS.Notify.Supports('position'))-- Server
local TSS = exports.tss_bridge:GetModules({ 'Notify' })
TSS.Notify.Send(source, {
title = 'Inventory',
message = 'Water added',
type = 'success',
})The old positional format remains supported:
TSS.Notify.Send('Inventory', 'Water added', 'success')
TSS.Notify.Send('Inventory', 'Water added', 'success', source, 5000) -- serverFramework adapters support QB-Core, Qbox and ESX.
local TSS = exports.tss_bridge:GetModules({ 'Framework', 'Player', 'Banking' })
local framework, resource = TSS.Framework.System()
local playerData = TSS.Player.Data(source)
local citizenId = TSS.Player.Identifier(source, 'citizenid')
local job = TSS.Player.Job(source)
local isPolice = TSS.Player.HasGroup(source, { police = 2 })
local cash = TSS.Banking.GetAccount(source, 'cash')Player and society mutations are server-only and validate amounts:
if TSS.Banking.GetAccount(source, 'cash') >= 500 then
TSS.Banking.RemoveMoney(source, 'cash', 500, 'shop_purchase')
end
TSS.Banking.AddMoney(source, 'bank', 1000, 'mission_reward')
TSS.Banking.FundSociety('police', 1000, 'job', 'mission_reward')Society mutations automatically add provider transaction history when the selected adapter needs a separate history call. Providers such as QB Banking and FD Banking already record their own mutations, so the bridge does not add a duplicate. Pass transaction metadata in place of the reason string when richer history is useful:
TSS.Banking.FundSociety('police', 1000, 'job', {
reason = 'vehicle_sale',
message = 'Sale of vehicle ABC 123',
issuer = 'Premium Deluxe Motorsport',
receiver = 'LSPD',
source = source,
})Renewed Banking and okokBanking support both explicit transaction recording and history retrieval. QB Banking supports explicit bank-statement creation, but does not expose history retrieval. Check the capability before using an optional operation:
if TSS.Banking.Supports('transactions') then
local transactions = TSS.Banking.GetTransactions('police', 25)
end
if TSS.Banking.Supports('recordtransaction') then
local recorded = TSS.Banking.RecordTransaction('checking', {
amount = 500,
type = 'withdraw',
reason = 'shop_purchase',
issuer = 'John Doe',
receiver = '24/7 Supermarket',
source = source,
accountType = 'player',
})
endRecordTransaction records history only; it never changes a balance. Do not
call it after a QB/FD society mutation unless a second, intentional record is
required. Returned history uses normalized account, id, amount, type,
reason, issuer, receiver, and time fields while retaining fields supplied
by the provider. History limits default to 50 and are capped at 200.
Client player-account calls omit source:
local citizenId = TSS.Player.Identifier('citizenid')
local hunger = TSS.Player.Metadata('hunger')
local cash = TSS.Banking.GetAccount('cash')Register an existing item in server code using just the Inventory module:
local TSS = exports.tss_bridge:GetModules({ 'Inventory' })
local registered, err = TSS.Inventory.CreateUseableItem('gps_beacon', function(source, item)
TriggerClientEvent('tss-blipcreator:client:OpenMenu', source)
end)
if not registered then error(('GPS registration failed: %s'):format(err)) endCreateUsableItem is an alias. Item names are lowercased. Registration calls
the selected framework directly: QB-Core's Functions.CreateUseableItem,
Qbox's CreateUseableItem export, or ESX's RegisterUsableItem.
The original callback is passed unchanged. Its arguments, return values, replacement and lifetime follow the framework. The bridge does not normalize item data, track ownership, create definitions or consume items. Register after the framework starts and re-register after restarting it.
TSS.Inventory.Supports('usable') reports whether a supported framework is
started on the server. Registration methods are absent from the client API.
The inventory must use the selected framework's usable-item registry;
successful registration alone does not guarantee inventory dispatch.
For OX, the bridge now relies on its framework integration, without installing
usingItem hooks or changing item definitions. In the installed OX dispatch,
framework callbacks require consume omitted/false and a working
server.UseItem integration. consume = 0 takes the native use path instead.
TextUI supports ox, QB-Core and a native fallback:
local TSS = exports.tss_bridge:GetModules({ 'TextUI' })
TSS.TextUI.Show({
text = '[E] Open shop',
position = 'right-center',
icon = 'store',
})
TSS.TextUI.Hide()Interaction chooses the configured target adapter when available and otherwise uses a proximity TextUI prompt:
local TSS = exports.tss_bridge:GetModules({ 'Interaction' })
local interaction = TSS.Interaction.Create({
name = 'shop_counter',
coords = vector3(25.7, -1347.3, 29.5),
radius = 1.5,
distance = 2.0,
label = 'Open shop',
icon = 'basket-shopping',
onSelect = function()
TriggerEvent('my_shop:client:open')
end,
})
TSS.Interaction.Remove(interaction)It also accepts entity, model, event, serverEvent, command, groups, items, canInteract, and box-zone dimensions. Server events triggered by an interaction must validate the player and all submitted data.
Callback names are automatically scoped to the consuming resource.
-- server/main.lua
local TSS = exports.tss_bridge:GetModules({ 'Callbacks', 'Banking' })
TSS.Callbacks.Register('getBalance', function(source, account)
-- Validate source and arguments here.
return TSS.Banking.GetAccount(source, account)
end)-- client/main.lua
local TSS = exports.tss_bridge:GetModules({ 'Callbacks' })
local balance, err = TSS.Callbacks.Await('getBalance', 5000, 'bank')
if err then print(err) end
TSS.Callbacks.Trigger('getBalance', function(value, callbackError)
print(value, callbackError)
end, 'cash')Server-to-client callbacks use the mirrored API:
local result, err = TSS.Callbacks.Await(source, 'clientCheck', 5000, data)
TSS.Callbacks.Trigger(source, 'clientCheck', function(value, callbackError) end, data)Namespacing prevents collisions, not cheating. A client can request any server callback it knows, so every server callback must validate source, permissions, ownership, ranges and payload values. Requests are rate-limited and time out automatically.
Request Blips on the client. Each module instance tracks the handles it creates
and clears them when the consumer stops. Update changes only supplied options.
local TSS = exports.tss_bridge:GetModules({ 'Blips' })
local blip = TSS.Blips.Create({
coords = vector3(25.7, -1347.3, 29.5),
label = 'Shop', sprite = 52, colour = 2, display = 2,
})
TSS.Blips.Update(blip, { display = 0 }) -- Hide without replacing the handle.
TSS.Blips.Update(blip, { display = 2, route = true, routeColor = 2 })
local coords = TSS.Blips.Coords(blip)
local colour = TSS.Blips.Colour(blip)
TSS.Blips.Remove(blip)Exists, Update, Coords and Colour operate on this module instance's owned
blips. Clear() removes all its blips. Options include hiddenOnLegend,
shortRange, category, scale, route and routeColor; false and zero values
are preserved. Creation supports either coords or entity.
Request Gizmo on the client for a reusable move/rotate editor. It can edit an
entity directly or return raw transform data. F2 cycles Move, Rotate, and the
combined Move + Rotate layout; F3 switches between world and heading-relative
local movement. Enter accepts, Backspace
cancels, Ctrl enables precision, and RMB plus WASD/Q/E controls the optional
detached camera.
local TSS = exports.tss_bridge:GetModules({ 'Gizmo' })
local started, err = TSS.Gizmo.Start({
entity = prop,
space = 'world', -- use 'local' for entity-local axes
camera = true,
restoreOnCancel = true,
}, function(result, cancelled)
if cancelled then return end
print(json.encode(result)) -- coords, rotation, heading, mode, space and entity
end)For data without an entity, pass coords, rotation or heading. Use
GetState(), Finish() and Stop() when another UI owns confirmation. Advanced
tools can call Create() and describe axis, point and ring handles; the Bridge
Lab uses this route for bone-local attachments and zone resize/radius handles.
Vehicle keys support qb-vehiclekeys and qbx_vehiclekeys:
-- Client, read only
local hasKeys = TSS.VehicleKeys.Has(vehicle)
-- Server
TSS.VehicleKeys.Give(source, plateOrVehicle)
TSS.VehicleKeys.Remove(source, plateOrVehicle)
local hasKeys = TSS.VehicleKeys.Has(source, plateOrVehicle)local TSS = exports.tss_bridge:GetModules({ 'Capabilities', 'Diagnostics' })
print(TSS.Capabilities.Module('Banking'))
print(TSS.Capabilities.Resource('ox_lib'))
local snapshot = TSS.Capabilities.Snapshot()
TSS.Diagnostics.Print()The server registers /tssbridge automatically. Console use is always allowed; players require:
add_ace group.admin tss_bridge.diagnostics allowThe report lists available and loaded modules, configured adapters, started resources and server consumers.
The bridge includes an official, lazily loaded developer lab. Configure
authorized Discord/FiveM identifiers in bridge_lab/server_config.lua, restart
the bridge, and use /bridgelab or press F7 in game. Both entry points are
authorized by the server; the allowlist remains server-only and
every lab callback rechecks the player's identifiers.
See bridge_lab/README.md for setup, security, and tool details.
The built-in developer tools include a detached-freecam PolyZone Creator, ped
animation, prop-attachment, and multi-prop scene gizmo editors, a client/server
API Explorer, and a read-only shared inventory item browser. Created zones,
attachments, and prop scenes are saved to
server-validated Lua data files under bridge_lab/.
English strings live in shared/locales/en.lua. Add another file using the same table structure and select it with:
set tss_locale frlocal TSS = exports.tss_bridge:GetModules({ 'Locale' })
print(TSS.Locale.T('callback_timeout', 'my_callback'))Missing languages and keys fall back to English.
- Client module registries never contain Inventory, Banking, or VehicleKeys mutation methods.
- Server APIs require a player source where relevant and do not trust client-provided sources.
- Callback handlers and consuming resource network events must still perform authorization and data validation.
- Debug table saving requires debug mode, rate limiting and the
tss_bridge.debugACE permission. - Per-resource cleanup removes bridge-created entities, targets, interactions and UI ownership when a consuming client resource stops.
add_ace group.admin tss_bridge.debug allowThe loader test exercises both mocked realms and asserts that client projections cannot access server mutation methods:
lua tests/module_loader_spec.lua client <resource-path>
lua tests/module_loader_spec.lua server <resource-path>
lua tests/banking_transactions_spec.lua <resource-path>Pass your script's Config.TSS as the second argument on both client and server:
Config.TSS = {
Target = 'qb',
Notify = 'qb',
-- ProgressBar = 'ox',
-- TextUI = 'ox',
-- Interaction = 'target', -- target, textui, or auto
}
local TSS = exports.tss_bridge:GetModules({ 'Targets', 'Notify', 'Debug' }, Config.TSS)
TSS.Notify.Send({ message = 'Saved', type = 'success' }) -- client
TSS.Debug.Print('Saved a blip')Only Target, Notify, ProgressBar, TextUI and Interaction accept these overrides.
Inventory and other gameplay providers remain bridge-wide. The config key
is Target (singular); the imported module is Targets (plural).
Selection order: explicit server convar, per-call adapter (where supported),
Config.TSS supplied at import, bridge config, then automatic detection. Explicit
auto requests automatic detection. Missing or unavailable providers retain each
module's normal fallback behavior. Import settings are copied; restart the consumer
after changing its config. They do not modify other resources' choices.
Server provider convars are also replicated by the bridge. Restart the bridge after
changing those convars. TSS.Debug.Print(...) uses the bridge's existing debug switch;
use setr tss_debug true before startup to enable it on both client and server.
UI.Menu and UI.Context options accept existing named icons, Font Awesome Free
class names, or image URLs. Both the option row and selected-item preview display
the same icon. Images preserve their original colours; missing images show a placeholder.
Font Awesome Free is bundled locally (solid, regular and brands; no Pro icons).
{ label = 'Tools', icon = 'wrench' }
{ label = 'Home', icon = 'fas fa-house' }
{ label = 'My marker', icon = {
type = 'image',
src = 'https://cfx-nui-my_resource/images/40.png',
} }A full image URL can also be supplied directly as icon. Put local images in the
calling resource and list them in that resource's files manifest; no image pack
needs to be installed in the bridge. Blipcreator demonstrates this with its own
images/blips/<sprite ID>.png previews. Replace those images to match custom YTDs;
the UI does not read live map textures.
Import Commands in the realm where the handler should run. Registration uses
FiveM natives, so it works with QBCore, Qbox, ESX, or no framework. Server commands
are automatically supplied to tss-chat's command provider when it is running;
otherwise the default chat receives standard suggestions. Client commands receive
standard suggestions in either chat. No tss-chat dependency or manual registration
is needed, including when tss-chat starts later or restarts.
-- server.lua in your resource (with dependency 'tss_bridge')
local TSS = exports.tss_bridge:GetModules({ 'Commands', 'Notify' })
local ok, err = TSS.Commands.Register('announce', {
description = 'Send a server announcement',
category = 'admin',
permission = 'my_script.announce',
arguments = {
{ name = 'message', type = 'rest', help = 'Announcement text' },
},
}, function(source, args, rawCommand, rawArgs)
TSS.Notify.Send(-1, { title = 'Announcement', message = args.message })
end)
assert(ok, err)Grant the configured ACE (for example add_ace group.admin my_script.announce allow),
or grant command.announce. Permission strings are ACE names, not framework job or
group names. A permission array allows any listed ACE. Omit permission (or use false
or user) for public commands. Permissions are checked on every execution. Console
source 0 is allowed unless allowConsole = false. Register restricted actions server-side;
the client module rejects restricted permission settings.
arguments are required unless optional = true. Types: string, number, integer,
boolean (true/false), and rest (remaining text, last argument only). The callback
receives named, converted args, plus the untouched command text and positional
rawArgs. options on an argument supplies autocomplete choices, not validation.
Additional positional arguments remain available in rawArgs.
hidden = true hides a command; debugOnly = true also blocks execution unless bridge
debugging is enabled. List(source) returns visible commands owned by the caller.
Refresh() refreshes suggestions after permission or debug changes; tss-chat also
rechecks server command visibility whenever it requests its command list.
Unregister(name) and Clear() disable handlers and remove suggestions. Resource-stop
cleanup runs automatically. Native command wrappers remain inert until the bridge
stops, and can be reused on consumer restart. Another consumer cannot overwrite or
remove your registration. Names already registered outside this module are rejected.
Register again from the consumer's startup after restarting the bridge itself.
Use tss_interact as the target provider to route existing bridge target calls to tss-interactions:
local TSS = exports.tss_bridge:GetModules({ 'Targets', 'Interaction' }, {
Target = 'tss_interact',
Interaction = 'target',
})
local desk = TSS.Targets.Box({
name = 'reception_desk',
coords = vector3(307.2, -595.3, 43.3),
length = 2.0,
width = 2.0,
}, {
{
label = 'Speak to reception',
icon = 'fas fa-comment',
onSelect = function(context)
print('hello', context.coords)
end,
},
}, 2.5)
-- TSS.Targets.RemoveZone(desk)Or select it server-wide with setr tss_target tss_interact. Server overrides take precedence over per-resource preferences. Start tss-interactions before resources registering these interactions. Restart those consumers after changing providers or restarting tss-interactions.
Entity and model calls retain their anchors. Box and circle calls become coordinate points: dimensions, radius, heading, Z bounds and debug-zone settings are ignored. The distance argument controls interaction range (default 2 metres). Removal and consumer-stop cleanup use the existing bridge API.
onSelect receives a context table containing entity, coords and args; native action callbacks receive (entity, data). Bridge groups and items filters use the bridge's Player and Inventory providers. Native TSS fields such as job, gang and citizenid remain available and use the interaction resource's player data.
Aliases tss-interactions and tss_interactions also work. Auto detection prefers existing ox/qb/tgiann/bt providers before TSS so installing this adapter does not change those selections; select tss_interact explicitly to use it alongside another target resource.
Server Inventory also exposes RegisterShop(data), OpenShop(source, id), and SetMetadata(source, slot, metadata). Shop data contains id, label, items and optional coords/groups. QB and OX adapters implement these operations; other adapters return false with unsupported_shop or unsupported_metadata. Supports('shop') and Supports('metadata') advertise availability on the server. Consumers must validate shop access and exact bag ownership before calling these methods.
The OX shop adapter accepts both native count/metadata fields and the shared
QB-style amount/info aliases without mutating the caller's shop table. OX
metadata updates verify that the requested slot exists before reporting success.
OX stash definitions also forward the optional instance restriction.
Server Player.SetDuty(source, boolean) uses QB/Qbox SetJobDuty or ESX tss_duty metadata. Player.AddNeeds(source, { hunger=delta, thirst=delta, stress=delta }) applies server-authorized changes, clamped to 0..100 for QB/Qbox; ESX hunger/thirst use esx_status. These mutation methods are not exposed on the client.
UI.Input fields support type='select', options={ {value='bank', label='Bank'} }, required, min, max and step. Submitted values remain keyed by field name.
Regression checks: lua tests/restaurant_capabilities_spec.lua. Restaurant integration checks live in tss-restaurant/tests/bridge_spec.lua.
Peds.Create and World.CreateDistancePed accept data.custom for freemode head blend, face sliders, hair, overlays, clothing and props. Custom fields follow the existing TSS dialogue definitions; custom.randomize randomizes appearance before applying explicit overrides, and a negative prop index clears that accessory. Unspecified fields use safe defaults for non-randomized custom peds.
Distance controllers accept options.onDespawn(entity), called before deletion so consumers can remove targets and close UI. Repeated remove calls are harmless; peds finishing model loading after cancellation or leaving range are discarded. Run lua tests/dialogue_peds_spec.lua for mocked appearance/lifecycle checks.
Inventory.Image(item) returns an item image URL for QB Inventory and OX Inventory (including custom image filenames), preserves absolute image URLs, and returns nil for unknown items or unsupported image providers. OX image URLs respect its inventory:imagepath convar, including external CDN paths.
Inventory.GetSlot(source, slot), Inventory.Find(source, item, metadata),
Inventory.Weight(source) and Inventory.MaxWeight(source) provide normalized,
read-only inspection. Omit source on the client. Find returns every matching
stack ordered by slot. Weight methods return nil when the active provider does
not expose enough information; check Supports('weight') or
Supports('maxweight') first. Weight values remain in the provider's native
units (normally grams).
Successful server mutations emit the local event
tss_bridge:server:inventoryAudit. Its payload includes action, system,
resource, caller, timestamp and the relevant source/inventory, item,
amount, metadata, slot and reason fields. Actions are add, remove,
stash_add, stash_remove and set_metadata. The event is server-local and
does not replace authorization in the consuming resource.
Peds.LookAt(target, ped?, timeout?) turns the local player (or supplied ped) toward an entity or coordinates and looks for five seconds. The turn defaults to 1500 ms and yields only when needed. Invalid/despawned entities return false. World.Face remains a compatible alias.
Inventory.Stash(id), Inventory.RemoveFromStash(id, item, amount, metadata, slot, reason) and Inventory.AddToStash(...) operate on non-player stashes. These are server-only operations; validate the stash ID and player access in the consuming resource. Check Supports('stashitems'), Supports('stashremove') and Supports('stashadd') first. Unsupported adapters return nil/false explicitly.
Currently implemented for ox_inventory and qb-inventory. QB mutations also persist the live stash through oxmysql because its normal stash save occurs on UI close. Failed persistence is logged. Exact slots and metadata are preserved for refunds.
StreetKings is an additive framework adapter. Existing Qbox, QB and ESX adapters
remain installed and retain their automatic detection priority. StreetKings is
detected when its resource is started; optionally use setr tss_framework streetkings.
Start streetkings, then tss_bridge, then the consuming resources. The bridge
does not have a mandatory StreetKings dependency on other framework servers.
Framework.System() returns streetkings. On the server, Player.Data, Name,
Identifier, Metadata and IsLoaded use the active save. Metadata contains
progression, stats and gameState. Banking.GetAccount(source, 'cash'), AddMoney
and RemoveMoney use StreetKings cash exports. Bank/crypto/society accounts,
jobs, gangs, duty, needs, inventory and vehicle-key ownership are not supplied
by StreetKings. Existing independently installed providers remain available.
StreetKings does not export client save documents or active save IDs. Normalized
client player data is therefore nil and client cash reads return zero. Server
identifier/license identify the account, not the save slot; no citizenid is
fabricated. For per-save progress, use StreetKings.ReadSaveData/WriteSaveData
rather than account identifiers. Framework.Object() is nil; Framework.Player()
is a snapshot, not a mutable QB/ESX player object.
Notifications automatically use StreetKings toasts while StreetKings is available.
Provider overrides still apply, e.g. setr tss_notify ox or streetkings.
Request the dedicated realm-specific module for native gameplay features:
local TSS = exports.tss_bridge:GetModules({ 'StreetKings', 'Banking', 'Player' })
-- server
if TSS.StreetKings.HasActiveSave(source) then
local found = TSS.StreetKings.ReadSaveData(source, 'myScript.found') or {}
-- Validate actions and rewards on the server before writing progress.
TSS.StreetKings.WriteSaveData(source, 'myScript.found', found)
endlocal TSS = exports.tss_bridge:GetModules({ 'StreetKings', 'Notify' })
-- client
if TSS.StreetKings.IsAvailable() and TSS.StreetKings.GetGameState() == 'freeroam' then
TSS.Notify.Send({ title = 'Challenge', message = 'Ready to start' })
endThe module preserves native argument signatures and all return values, including
false and nil. It exposes only the native exports listed below in their original
realm, plus IsAvailable() in both realms and client CheckpointCleared() for
the native nitrous checkpoint event. Calls fail with a descriptive error when
StreetKings is unavailable or an export fails. No client network event for save
writes, rewards or arbitrary export invocation is introduced.
tss_carcollectibles and tss_speedzones now request this module and declare a
bridge dependency; their gameplay, validation and reward rules are unchanged.
They remain StreetKings gameplay resources, not generic RP scripts.
Client
AllowLeaveVehicle, ClosePhone, CreateWaypoint, DisableChaseCam, EnableChaseCam, GetAllVehicleData, GetCurrentTime, GetCurrentTrack, GetCurrentWeather, GetGameState, GetPlayerVehicle, GetVehicleAvailability, GetVehicleData, GetWantedLevel, IsChaseCamEnabled, IsCinematicMode, IsInFreeroam, IsNearPolice, IsPhoneOpen, IsPlayerWasted, IsPoliceChasing, IsRaceEditorFreecamEnabled, LoadAnimDict, LoadAnimSet, LoadModel, LoadPtfxAsset, OpenPhone, RaceEditorFreecamRaycast, RegisterGameState, RemoveAllWaypoints, RemoveWaypoint, SetCinematicMode, SetGameState, SetSoundtrackEnabled, SetSpeedometerEnabled, SetupTestJump, ShowNotification, StartRaceEditorFreecam, StopRaceEditorFreecam, TeardownTestJump, UnloadModel, WarpPlayer, WarpToWaypoint, awaitDebugToolsPermission.
Server
AddPlayerCash, AwardPlayerXp, AwardVehicleXp, BroadcastPhoneMessage, CreateServerWaypoint, GetActiveVehicle, GetOwnedVehicles, GetPlayerCash, GetPlayerGameState, GetPlayerLevel, GetServerWaypoints, HasActiveSave, IncrementStat, IsPlayerInHangoutZone, PersistSave, ReadSaveData, RecordActivityBest, RegisterStat, RemovePlayerCash, RemoveServerWaypoint, SendPhoneMessage, SetStatMax, SetTime, SetWeather, WriteSaveData.
The Inventory module now includes tss (tss-inventory, tss_inventory) for the grid resource. Auto detection prefers it when started; use setr tss_inventory tss before startup to force it. The inventory resource depends on this bridge for world peds, targets, notifications and Banking player payments; the bridge does not depend on tss-inventory. Start bridge before inventory and inventory before consumers. Restart consumers after restarting the bridge.
The adapter supports standard inventory reads, item add/remove/carry checks, exact metadata updates, native usable callbacks, unrestricted server stashes and registered shop opening. Instance IDs are stable bridge slots. Explicit add slots can fill an existing compatible stack; new preferred-slot insertions fail. Missing item definitions remain errors. Arbitrary client stash opening is blocked. Stash owner/groups/coords restrictions and shop groups fail explicitly in this first version, rather than being discarded. Shops require server coordinates and enforce proximity. Existing direct calls to other inventories are not emulated. See tss-inventory/SHOPS_AND_BRIDGE.md for current coverage and setup.
GetModules({'Vehicles'}) provides vehicle queries on both sides. Inspect(entity) returns entity, networkId, model, normalized plate, coordinates, lock status and vehicle type. Current(source) and FromNetworkId(id) resolve live vehicles. Server inspection includes routing bucket, client inspection includes class. Client also exposes Nearby(distance), TrunkPosition(entity) and SetTrunkOpen(entity, shouldOpen). SetTrunkOpen animates a valid boot door and returns success and its previous open state. Validate storage with server inspection; never trust client-provided plates, classes or sizes. Creation/deletion and door helpers remain client-only.
For vehicle bone targets, use Targets.Bone({'boot'}, options, distance) and Targets.RemoveBone({'boot'}). Check Targets.Supports('bones') first. Bone targets are supported by ox_target, qb-target and tss-interactions, and scoped registrations are removed by Targets.Clear().
Push a stable version tag such as v1.0.0 to create a GitHub Release with
that title and automatically generated release notes:
git tag v1.0.0
git push origin v1.0.0Commit and push the release workflow before tagging; the tagged commit must
include .github/workflows/release.yml. GitHub Actions builds the UI using
npm ci and npm run build, then attaches tss_bridge-v1.0.0.zip.
Extract the ZIP into your resources directory and ensure tss_bridge.
The ZIP includes the Lua modules, Bridge Lab, and compiled UI, without UI
source files or development dependencies. Its manifest version is set from
the tag (1.0.0); the repository manifest is not changed by the workflow.
Only stable vMAJOR.MINOR.PATCH tags are accepted. The workflow uses the
built-in GitHub token and requires no additional secrets. The separate
version_check repository is not updated by this workflow.