Frameworks & Bridges
The phone never calls a framework directly. Player, money, inventory, job and notifications all go through an abstraction layer (Bridge). Switching frameworks = switching which implementation is loaded.
Supported frameworks
Detection is automatic (qbx → esx → qb: the first one started wins). Nothing to configure in the normal case.
Forcing a framework
If you run more than one core, force it via Config.Framework or the convar:
set orb-phone:framework "qbx" # auto (default) | qbx | esx | qbInventory
ox_inventory is optional/recommended, not required. If it isn't running, the bridge falls back to the framework inventory (qb-inventory and forks on QBCore, native on ESX). With ox/qb the number lives in the item metadata (the physical phone can be traded/dropped); on ESX native it's anchored to the character identifier.
On-duty (Services)
Bridge.IsOnDuty reads PlayerData.job.onduty on QBox/QBCore. Native ESX has no built-in duty concept, so it returns nil → the caller fail-opens (doesn't block). To gate on duty in ESX, point a service's customCheck at your duty resource's export.
Adding a framework
Copy bridges/qbx.lua, implement the interface (identical signatures), register OrbBridges., add detection in bridges/init.lua, and load it in fxmanifest.lua (server + client). Never call exports. outside bridges/. See docs/bridges.md in the package for the full interface.