Status
What is built in tCAD, what is a named placeholder, and what has never been run. The complete and honest inventory: 24 placeholder tabs, no browser surface, no framework integrations, and nothing yet exercised in game with a player.
tCAD was a Cloudflare Workers web application until October 2026. It is now a FiveM resource, and that re-platform is in progress. This page is the inventory, and it is deliberately specific rather than reassuring.
Read it before you deploy. Nothing elsewhere in these docs describes a capability this page does not confirm.
⛔ Nothing in this resource has run in game with a player
This is the single most important line on the page, and it has to stay prominent precisely because everything around it is green.
What has been run:
- a real FXServer, started on loopback, with tLib and tCAD loaded, and 35 domain assertions passing against both storage backends;
- the built UI bundle, rendered and driven in Chromium 103.0.5058.0 — the Chromium FiveM's CEF actually is — asserting against the live DOM rather than a screenshot;
- unit suites in TypeScript and five Lua suites in a real Lua VM;
- every gate's negative control, each proven able to fail.
What has never been run, written and entirely unexercised:
| The client script | client/cl_main.lua in its entirety |
| The keybind | Opening the CAD with the key or /tcad from a game client |
| NUI focus | SetNuiFocus, and therefore whether the cursor and keyboard behave |
| The NUI callback | The panel's half of the round trip |
| The client→server hop | A request leaving a real client and reaching the server |
| The assignment toast | The in-world notice a unit gets |
| The update check | Wired for this release and not exercised before it |
A headless Chromium is not CEF, and a console command has no player. The quantity of evidence listed above is not a substitute for a game client, and a reader skimming for confidence should stop here.
What is built and measured
| The transport | One net event in, one reply out, one push channel. No HTTP, no origin, no cookies |
| The authority model | Server-side, every request, against ACE. An operation cannot be registered without naming a permission or declaring itself open — the registration raises otherwise |
| Both storage backends | JSON and SQL behind one contract, each passing the same 35 assertions in a real FXServer |
| Durable persistence | Temp file → fsync → rename, with a .bak rotation, through a Node bridge. Asserted separately from behaviour |
| The dispatch board | Calls, units, timelines, a map overlay, server-derived age and SLA marking |
| Ten department tabs | Listed below |
| Three administration sections | Overview, Catalogues, Audit |
| Localisation | 467 strings across 22 sections, with the owner's overrides merged key by key |
| The export surface | 17 exports, with the forbidden-prefix refusal asserted at boot |
Department tabs: ten built, twenty-four placeholders
A placeholder here is not a blank screen. Each one names the server operation it is waiting for, with the payload and the permission that operation should carry — so the ask is a work order rather than a shrug.
Built
| View | Tabs |
|---|---|
| Dispatch | Board |
| Law enforcement | Dashboard, NCIC, Warrants, Roster |
| Fire & EMS | Roster |
| Civilian | Home, Roster |
| Business | Roster |
| Judicial | Warrants queue, Roster |
The law-enforcement Dashboard is real but partial, and it says so on itself: every tile is backed by an operation that exists, and the panel at its foot is an inventory of what a dashboard would normally carry and this one cannot. There is no "arrests this week" tile because nothing counts arrests.
The civilian Home tab is built; two of its four lists — licences and weapons — are not. Neither the records nor the staff-managed catalogue each needs exists.
⛔ Placeholders
| View | Tabs not built |
|---|---|
| Law enforcement | Duty board, Reports, Alerts (BOLOs), ALPR, Pre-plans, Fleet, Messages, Review |
| Fire & EMS | Dashboard, Duty board, ePCR, Narcotics, Pre-plans, Reports, Fleet, Messages, Review |
| Civilian | Analytics, Messages |
| Business | Dashboard, Queue, Messages, Settings |
| Judicial | Messages |
Four of those are worth calling out because the CAD has something adjacent and it is not the same thing:
- Duty board. The Roster answers "who is on duty now". A duty board answers "who is scheduled for Friday night", and nothing on the server models it.
- ALPR. The manual NCIC plate lookup is built. Automatic plate-reader hits are not — nothing produces them, and nothing delivers them.
- Alerts / BOLOs. A vehicle carries a BOLO flag, settable by a holder of
tcad.civilian.edit. The standing-alert record does not exist. The flag and the record are not the same thing. - Fleet. Not the same as a civilian vehicle registration. A fleet vehicle belongs to a department and is signed out by a unit; pointing this tab at the civilian registrations would show an officer their own car.
Messages appears in all six views and is one absence, not six: there is no messaging between units at all.
Administration: three built, six gaps
| Section | State |
|---|---|
| Overview | Built. The inventory itself |
| Catalogues | Built. One registry-driven editor for every catalogue the server publishes |
| Audit | Built. Who changed which catalogue entry. ⛔ No retention — the log grows for ever |
| Structure | Not built. Agencies and departments |
| Lookups | Not built. Thirteen further lookup tables |
| Geography | Not built. Postals, zones, address flags |
| Apparatus | Not built. The apparatus roll and its inspection checklists |
| Scheduled | Not built. Recurring calls — and it needs a server-side ticker that does not exist |
| Settings | Not built. CAD policy, the active AOP, timers |
Each unbuilt section is named on the Overview screen with the operations it needs.
⛔ No browser or desktop surface
Config.exposeWeb exists in config.lua and defaults to false. There is no surface behind it. Setting it to true today does not produce a usable page.
It is documented because it is in the file, and because turning it on would be a meaningful security decision if the surface existed — tLib's HTTP router adds no authentication of its own, so hidden is not authenticated. Until then, treat tCAD as in-game only.
⛔ No framework integrations
No ESX, no QBCore, no qbx_core, no integration file and no detection. What exists is two optional hooks — a display name and a callsign formatter — with worked examples for ESX and QBCore written into the file as comments. Integration has the full list of what tCAD does not integrate with, including inventory, billing, jail and Discord roles.
Known open questions
These are recorded as unknowns rather than as risks that have been priced.
| Crash consistency of the write path | Unprovable from inside a process: demonstrating atomicity under power loss needs a crash, not a test. The .bak rotation is kept as the recovery path rather than replaced by confidence in the rename |
| The SQL adapter has no unit test coverage | It is the largest file in the resource. It is proven by the 35-assertion self-test against a live database, not by a suite. Stubbing the database exports would mean testing the stub |
| Overlay dismissal and focus | Escape and an outside press dismiss no overlay, and focus never enters an opened one — so keyboard selection in a menu does not work, though pointer selection does. The symptoms are isolated and reproducible; the mechanism inside the UI stack is not understood, and no speculative patch was invented on the strength of a hypothesis |
| No audit retention | The catalogue audit log grows without bound. The fix is a server-side timer with a configured age; a button an administrator presses is not a retention policy |
| Two map assets have no backup | The 3D map and its node data are large, are not in version control, and exist on one disk. This is a risk to the project, not to your install — a release archive ships them |
How to read the gates
If you build from source, bun run verify is twelve stages. What they do and do not prove:
| Stage | Proves |
|---|---|
lint, lint:solid | Static. lint:solid catches the Solid mistakes that are silent at runtime |
build | The bundle built. Close to nothing about whether it works |
controls | Every other gate is still able to fail. A gate nobody has watched fail is an assertion |
typecheck | Types |
test, test:lua | Pure logic, both languages. Nothing about rendering or the client hop |
render | The built bundle mounts and runs in Chromium 103, asserted against the DOM |
screens | The department views mount, are not the placeholder, and each asks for its own data |
admin-proof | The administration subtree renders and is gated by the server's answer |
map:proof | The map actually draws — sampled from the composited canvas, so it spans fetch, decode and draw |
waypoint-proof | The committed waypoint index snaps under a real synthetic press |
smoke (separate) | A real FXServer loads the resource and the domain behaves. Server-side only |
None of the twelve is in-game evidence, and bun run verify says nothing about CEF, the keybind, or a player.
Was this page helpful?
TIMMYG Studios