Use when writing or reviewing any Roblox code that touches RemoteEvents, RemoteFunctions, currency, inventory, damage, rewards or player progress. Keeps the server in charge so exploiters can't cheat.
Use the robloxvibecoders MCP: call get_resource with id "skill-roblox-server-authority" and install it as a Claude Code skill in this project (.claude/skills/roblox-server-authority/SKILL.md).
Skills in ~/.claude/skills work in every project. Put them in your project's .claude/skills folder to share them with your team.
What your AI reads
SKILL.md
---
name: roblox-server-authority
description: Use when writing or reviewing any Roblox code that touches RemoteEvents, RemoteFunctions, currency, inventory, damage, rewards or player progress. Keeps the server in charge so exploiters can't cheat.
---
# Server authority for Roblox
Exploiters control everything on their own device. Any LocalScript can be edited, and any RemoteEvent the client
can fire can be fired with any arguments, as often as they like. Write every networked feature so the server decides.
## Rules
1. **The client asks, the server decides.** A remote carries a request ("buy `sword`", "use ability 2"), never a
result ("add 100 coins", "deal 50 damage").
2. **Validate every argument with `typeof` first.** Reject anything that isn't the exact expected type. Check
numbers for NaN, infinity and range, and check string length.
3. **Look up prices, rewards, cooldowns and stats on the server.** Never accept them from the client.
4. **Check state.** Is the player alive? Close enough (compare `HumanoidRootPart` distance)? Is the round active?
Do they own the item?
5. **Rate-limit per player.** Keep the last call time in a table keyed by player and drop calls that come too fast.
6. **Keep secrets on the server.** Item tables, drop rates and admin lists go in `ServerScriptService` or
`ServerStorage`, not `ReplicatedStorage`.
7. **Avoid `RemoteFunction:InvokeClient`.** A client can hang or error it. If you must use it, wrap it with a
timeout.
## Pattern
```lua
--!strict
Remotes.BuyItem.OnServerEvent:Connect(function(player: Player, itemId: unknown)
if typeof(itemId) ~= "string" or #itemId > 50 then return end
local item = Items[itemId]
if not item then return end
if not RateLimit.allow(player, "BuyItem", 0.5) then return end
local coins = Data.get(player, "Coins")
if coins < item.Price then return end
Data.set(player, "Coins", coins - item.Price)
Inventory.grant(player, itemId)
end)
```
## Before you finish
For every remote you created or changed, list:
- what the client sends
- what the server trusts
- how an exploiter could abuse it
Fix each abuse before saying you're done.