Skip to content

Permissions

tCAD's nineteen ACE keys, what each one gates, which ones reach civilian and judicial records, and a starter server.cfg block per role.

Every capability tCAD checks is an ACE object, granted in server.cfg. There is nowhere else to grant one.

Authority is checked server-side, every time, on the acting player's source. The NUI is sent no permission set at all, so hiding a button is decoration and reaching a screen is not a grant.

How a method is gated

Two things are true of every operation:

  1. It requires Config.permissions.use — tcad.use — on top of whatever else it asks for.
  2. A method cannot be registered without naming a permission or explicitly declaring itself open. The registration call raises otherwise. So the authority check is structural rather than remembered: there is no path by which somebody adds an operation and forgets the gate.

ACE inheritance applies, and every key shares the tcad. prefix deliberately — so add_ace group.admin "tcad" allow grants the whole tree.

The nineteen keys

Opening the CAD

KeyACE objectGates
usetcad.useRequired for every method. Also Config.acePermission, which gates opening the panel

Calls

KeyACE objectGates
createCalltcad.calls.createFile a new call
editCalltcad.calls.editChange a call's details
holdCalltcad.calls.holdPut a call on hold
cancelCalltcad.calls.cancelCancel a call
closeCalltcad.calls.closeClose a call

Adding a comment to a call is open beyond use. It is the one call action with no key of its own: a unit noting what they saw is not an administrative act, and gating it produces a timeline with gaps in it.

Dispatch

KeyACE objectGates
dispatchSelftcad.dispatch.selfAttach yourself to a call
dispatchUnittcad.dispatch.unitAttach or detach somebody else

Two keys rather than one because attaching yourself is a much smaller capability than assigning another unit. Detaching yourself is open beyond use — a unit must always be able to clear.

Setting your own status needs no key at all. Its authority is by state: the actor must be on duty in that department.

Records — ⛔ these gate personal data

Every civilian, vehicle and warrant operation comes in two forms: one scoped to the caller's own records, which needs nothing beyond use, and one that reaches every record, which needs the key below. A player managing their own character is not an administrative act; a player reading somebody else's medical notes is.

KeyACE objectGates
viewCiviliantcad.civilian.viewRead any civilian's record, their vehicles and their warrants
editCiviliantcad.civilian.editEdit or delete any civilian's record, and set the LEO vehicle flags (stolen, impounded, BOLO, repo)
runNcictcad.ncic.runRun an NCIC person or plate query

viewCivilian is wider than it looks: it also gates the subject's SSN on a warrant. A warrant is readable by anyone with tcad.use — it is a public court record — but the identifying number on it needs this key. A judge without it can approve and deny warrants and will not see which person each one names beyond their name, which for two characters with the same name is a judge ruling on the wrong person. That is a worse outcome than the disclosure the key controls, so if you grant tcad.warrants.approve or tcad.warrants.deny to a group, grant tcad.civilian.view to the same group.

A dispatcher deliberately does not need it: dispatch works calls and units, and a civilian's identifying number is not part of that. A dispatcher opening a warrant sees it in full apart from that field.

editCivilian is separate from viewCivilian because reading a record and rewriting somebody else's are different acts — and because an owner must not be able to clear their own stolen-vehicle flag, which is exactly what collapsing the two would allow.

runNcic has its own key because every query is logged against the officer who ran it. A CAD where everyone can run a name is a CAD where nobody can be held to it.

Warrants

KeyACE objectGates
createWarranttcad.warrants.createRequest a warrant
approveWarranttcad.warrants.approveApprove a requested warrant
denyWarranttcad.warrants.denyDeny a requested warrant
serveWarranttcad.warrants.serveMark a warrant served
recallWarranttcad.warrants.recallWithdraw a warrant, and the extradition hold

Five keys rather than one, because the separation is the point of a judicial view: an officer requests and serves, a judge approves or denies. Granting approve to the requesting officer removes the review step entirely.

⛔ Administration

KeyACE objectBlast radius
manageAgencytcad.admin.agencyAgencies and departments — structure. Retiring a department takes its members off duty in it
manageCataloguetcad.admin.catalogueThe lookup catalogues: call types, priorities, statuses, dispositions, status codes, warrant types, licence types, weapon models. Wrong values are annoying and recoverable
viewAudittcad.admin.auditRead who changed what. Separate because it is a read, and a supervisor can reasonably audit without being able to change anything

Three keys rather than one, split by blast radius rather than by screen.

There is deliberately no key for deleting a catalogue entry as distinct from editing one. Delete is refused by the storage layer for every catalogue whose rows history points at — that is a correctness rule, not a permission — so a separate key would imply a capability that does not exist.

A starter block

Adapt the group names to your framework. These are illustrative roles, not a recommendation about who on your server should hold what.

cfg
# Everybody who uses the CAD at all
add_ace group.dispatcher "tcad.use"              allow
add_ace group.officer    "tcad.use"              allow
add_ace group.judge      "tcad.use"              allow

# Dispatch: calls and units. Deliberately NOT tcad.civilian.view.
add_ace group.dispatcher "tcad.dispatch.unit"    allow
add_ace group.dispatcher "tcad.calls.create"     allow
add_ace group.dispatcher "tcad.calls.edit"       allow
add_ace group.dispatcher "tcad.calls.hold"       allow
add_ace group.dispatcher "tcad.calls.cancel"     allow
add_ace group.dispatcher "tcad.calls.close"      allow

# Patrol
add_ace group.officer    "tcad.dispatch.self"    allow
add_ace group.officer    "tcad.calls.create"     allow
add_ace group.officer    "tcad.civilian.view"    allow
add_ace group.officer    "tcad.ncic.run"         allow
add_ace group.officer    "tcad.warrants.create"  allow
add_ace group.officer    "tcad.warrants.serve"   allow

# Judiciary. tcad.civilian.view is granted WITH approve/deny, for the
# reason under Records above — without it a judge cannot tell two
# same-named subjects apart.
add_ace group.judge      "tcad.civilian.view"    allow
add_ace group.judge      "tcad.warrants.approve" allow
add_ace group.judge      "tcad.warrants.deny"    allow
add_ace group.judge      "tcad.warrants.recall"  allow

# Administration. The whole subtree, and an audit-only grant.
add_ace group.admin      "tcad"                  allow
add_ace group.supervisor "tcad.use"              allow
add_ace group.supervisor "tcad.admin.audit"      allow

Note what is not in that block: tcad.civilian.edit is granted to nobody. Rewriting another player's record is worth deciding on deliberately rather than inheriting from a starter config.

Refusing an action a grant allows

A grant is the floor, not the ceiling. Config.denyMethod in config/sv_functions.lua is called after the gate has already said yes, and the only thing it can do is turn that into a no — on duty state, time of day, your own rules.

No hook can grant a permission. Grants are ACE objects in server.cfg, where an auditor looks. See Customisation.

Checking what a key resolves to

tcad_config

in the server console prints the whole permission map with its resolved ACE object names, which is the fastest way to catch a renamed value that no longer matches your add_ace lines.

Was this page helpful?