roblox-networking
Design or review Roblox client-server communication. Use for remotes, bindables, replication, network ownership, input validation, rate limiting, and exploit defenses.
Install
npx skills add https://github.com/nonlooped/roblox-suite/tree/main/roblox-networking
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install nonlooped-roblox-suite@llmmart
git clone https://github.com/nonlooped/roblox-suite.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole nonlooped/roblox-suite collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
roblox-networking
Core sources: https://create.roblox.com/docs/projects/client-server , https://create.roblox.com/docs/scripting/security/security-tactics (server-authority techniques), the entire scripting/security/ section (security-tactics, defensive-design, client-server-boundary, network-ownership, access-control, etc.), events/remote and bindable, plus the relevant Engine classes (RemoteEvent, RemoteFunction, Bindable*, RunService, etc.).
Authority boundary
The server is authoritative for game state and simulation. The client simulates for responsiveness and renders what the server tells it. Anything that can be abused (economy, progression, combat outcomes, movement in competitive play) must be validated or simulated on the server.
Communication tools
RemoteEvent. Fire-and-forget across the boundary.
- Client → Server:
RemoteEvent:FireServer(...)→ server handler receives(player, ...) - Server → Client:
FireClient(player, ...)orFireAllClients(...)
RemoteFunction. Request/response (yields).
- Client:
InvokeServer(...)→ serverOnServerInvoke(player, ...) - Server can InvokeClient (less common, has caveats).
Critical warning:
RemoteFunction:InvokeClientyields the server until the targeted client returns a value. A hostile or lagging client can leave the invocation pending and hang the server indefinitely. Avoid server→client invocation; if you must use it, enforce a timeout and treat the client as untrusted.
BindableEvent / BindableFunction. Same-side only (server modules talking to each other, or client modules). They stay on one side of the network boundary.
General rule: Client sends intent. Server decides outcome and replicates the result (or lets normal Instance replication handle it).
Server authority techniques
- Validate every client action (distance, rate, prerequisites, stamina, line-of-sight, etc.).
- Simulate critical systems on the server (or at least re-execute the important parts).
- Use server tables or Attributes as source of truth, not leaderstats or client-visible values.
- For movement/physics in competitive games: rely on Roblox physics + network ownership + server validation/correction.
- Replicate only what each client needs (don't broadcast entire inventories every frame).
See the official server-authority techniques page for movement validation, physics, etc.
Network ownership
Unanchored parts and assemblies have a network owner (usually the nearest player or the server, based on proximity and client capacity). The owner simulates the physics locally for low latency. Server can call BasePart:SetNetworkOwner(player or nil) to set ownership for the entire connected assembly. Anchored parts are always owned by the server and cannot be manually reassigned.
Critical for vehicles, projectiles, pushable objects, etc. Misuse leads to rubber-banding for other players or easy exploitation.
Common exploit classes and defenses
- Speed/fly/noclip: server movement validation or physics ownership + correction.
- Remote spam / duplication: per-player rate limiting + server-side cooldowns + argument validation.
- LocalScript injection / free-model backdoors: audit assets, use capabilities, server validation.
- Tampering with leaderstats or client-visible values: treat them as pure display; authoritative state lives in server tables or DataStores.
- Information disclosure: don't parent secret models in replicated locations; use streaming, server-only storage (ServerStorage/ServerScriptService), and avoid putting sensitive geometry/triggers where clients can see them.
CanQueryand collision groups are not confidentiality controls; they only affect spatial queries and physics collisions, not replication or rendering.
Rate limiting pattern (simple but effective): Keep a table of last action times per player per action type. On Remote, check delta. Warn or kick on abuse.
Capabilities (experimental security layer):
Script capabilities let you restrict what scripts are allowed to do at a granular level. They are currently experimental. To use them, set Workspace.SandboxedInstanceMode = Enum.SandboxedInstanceMode.Experimental, then set Sandboxed = true and assign a SecurityCapabilities value on the script or a parent container (Model/Folder). This restricts the script to only the abilities in its capabilities set.
Access control and confidentiality
- What the client can see: control with Instance streaming, don't parent sensitive geometry in Workspace if clients shouldn't know the exact layout, use server-only containers where possible.
- Confidential data (exact economy formulas, drop rates, anti-cheat parameters) should stay on the server.
Secure monetization and data flows (tie-in to other skills)
- Prompt purchase on client.
- For game passes: only grant benefits inside
PromptGamePassPurchaseFinishedwhenwasPurchased == true, and re-verify ownership withUserOwnsGamePassAsynconPlayerAdded. - For developer products and subscriptions: validate and grant benefits only inside the server-side
ProcessReceiptcallback. - DataStore writes only from server Scripts.
- Any client Remote that says "I bought X, give me the item" must be ignored or treated as a hint and re-validated on the server.
Practical placement rules
- Authoritative logic, economy, progression, combat resolution, datastore access → ServerScriptService or server-only modules.
- UI, input handling, cosmetic prediction, local VFX → client (LocalScripts or client modules under PlayerGui or required from ReplicatedStorage with IsClient branches).
- Shared pure functions / constants / Remote definitions → ReplicatedStorage modules (loaded on both sides but executed in context).
Use RunService context checks everywhere you are unsure.
Scripts
scripts/RateLimiter.lua: a reviewed per-player/per-action limiter with a hard minimum interval, token capacity/refill, bounded action-key allocation, and denial reasons. Calldestroy()on dedicated instances to disconnect cleanup;RateLimiter.defaultis the shared singleton.
Reference index
- exploits-and-defenses.md: Review movement exploits, remote abuse, asset backdoors, or data exposure.
- remote-and-bindable-patterns.md: Choose events versus functions or define communication payloads.
- server-authority-and-validation.md: Validate remote arguments, enforce cooldowns, or review client prediction.
Files (roblox-suite)
-
references
-
exploits-and-defenses.md 2.6 KB
--- read_when: "Review movement exploits, remote abuse, asset backdoors, or data exposure" last_reviewed: 2026-06-17 --- # Common exploits and defenses ## Speed / fly / noclip **Defense**: - Server-side movement validation or position correction. - Use proper network ownership + physics simulation on server for competitive games. - Don't trust client-reported position for anything important. ## Remote spam / duplication **Defense**: - Per-player, per-action rate limiting (timestamp table + threshold). - Server-side cooldowns that the client cannot bypass. - Argument sanitization on every Remote (type checks, range checks, instance validity). ## Information disclosure **Defense**: - Don't parent secret geometry, triggers, or logic in replicated locations. - Use StreamingEnabled + streaming targets. - Server-only storage (ServerStorage) for sensitive models. - `CanQuery = false` and collision groups are **not** confidentiality controls. They only affect spatial query operations and physics collisions, not whether the client can see or replicate a part. Use them for physics/query filtering only. ## LocalScript injection / free Model backdoors **Defense**: - Never use untrusted free models in production. - Audit all third-party code. - Use Script Capabilities (experimental). Enable `Workspace.SandboxedInstanceMode = Enum.SandboxedInstanceMode.Experimental`, then set `Sandboxed = true` and assign a `SecurityCapabilities` value on scripts or parent containers to restrict what they can do. - Server validation means even if client code is compromised, the server still decides outcomes. ## Tampering with client-visible values Leaderstats, Attributes the client can see, etc. are **display only**. Always keep authoritative values in server-side tables or DataStores. Update the visible values from the server result. ## General hardening - Assume the client is hostile. - Every Remote that can affect shared state must have validation. - Log suspicious patterns (very high action rates, impossible values). - Have a plan for consequence (kick, temporary restrictions, ban via `Players:BanAsync`). Note: `BanAsync` is server-only, yields, and requires `Players.BanningEnabled` to be set before the server starts (enable it in Studio and publish, or set it very early in a server Script). The config must include `UserIds`, `Duration`, `DisplayReason`, and `PrivateReason`, and optionally `ApplyToUniverse`. Wrap it in `pcall`. The combination of good architecture (this skill) + proper data patterns (roblox-datastores skill) + secure monetization flows makes the majority of common exploits ineffective or easily detectable. -
remote-and-bindable-patterns.md 4.1 KB
--- read_when: "Choose events versus functions or define communication payloads" last_reviewed: 2026-06-17 --- # Remote and bindable patterns ## RemoteEvent (fire-and-forget) Best for most gameplay communication. **Client to Server** (input/intent): ```lua -- Client RemoteEvent:FireServer("attack", targetPosition) -- Server RemoteEvent.OnServerEvent:Connect(function(player, action, data) if action == "attack" then -- validate player can attack, range, cooldown, etc. -- then apply effect and replicate result if needed end end) ``` **Server to Client**: - `FireClient(player, ...)` for one player - `FireAllClients(...)` for everyone - To target a specific subset of players, iterate over the desired players and call `FireClient` on each; there are no built-in variants for specific player lists. ## RemoteFunction (request-response) Use sparingly because it yields the caller. Good for: - Client asking server for specific data that must be fresh (e.g. current shop prices, complex calculated stats) - Server asking a specific client something (rarer) Bad for frequent or performance-critical traffic. > **Critical warning:** `RemoteFunction:InvokeClient` yields the server until the targeted client returns a value. A hostile or lagging client can leave the invocation pending and hang the server indefinitely. Avoid server→client invocation; if you must use it, enforce a timeout and treat the client as untrusted. On the server, wrap `OnServerInvoke` handlers in `pcall` so an error in your validation logic does not propagate to the invoking client (which could leak internals or cause unexpected client-side failures). ## BindableEvent / BindableFunction **Same side only.** - Server modules talking to other server modules - Client modules talking to other client modules Never put a Bindable in a place where it could be used to bypass the network boundary. Excellent for clean event-driven architecture within one side of the client/server divide. ## Recommended patterns 1. **Central Remotes folder** in ReplicatedStorage. All RemoteEvents and RemoteFunctions live here with clear names. 2. **Wrapper modules** on both sides that expose clean APIs instead of raw Fire/OnEvent calls everywhere. 3. **Validation layer** on the server side of every Remote. 4. **Rate limiting** on sensitive or abusable Remotes. 5. **Source of truth** lives on the server. Remotes are for synchronization, not authority. ## Argument sanitization examples Validate every argument from the client before using it. ```lua -- Type and bounds checks local function sanitizeDamage(amount: unknown): number if typeof(amount) ~= "number" then return 0 end if amount ~= amount then return 0 end -- NaN check return math.clamp(amount, 0, 100) end -- Instance validity and ancestry local function sanitizeTarget(target: unknown, validFolder: Folder): Model? if typeof(target) ~= "Instance" or not target:IsA("Model") then return nil end if not target:IsDescendantOf(validFolder) then return nil end return target end -- Whitelisted table keys local ALLOWED_KEYS = { Name = true, Slot = true } local function sanitizeOptions(options: unknown): { [string]: unknown } if typeof(options) ~= "table" then return {} end local result = {} for key, value in pairs(options :: { [string]: unknown }) do if ALLOWED_KEYS[key] and typeof(key) == "string" then result[key] = value end end return result end ``` > **Warning:** A client-to-server Instance reference can point to any Instance the client can see, including other players' characters or replicated map geometry. The server must re-check the Instance's ClassName, ancestry, and whether the player is allowed to interact with it. Do not trust the client to send the "right" object. ## Payload sizes Keep Remote payloads small. Sending large tables, long strings, or many Instances every frame can degrade server performance and increase bandwidth for all clients. Prefer compact identifiers (IDs, positions) and fetch detailed data on demand. See the authority-and-validation reference for more validation and rate-limiting examples. -
server-authority-and-validation.md 3.8 KB
--- read_when: "Validate remote arguments, enforce cooldowns, or review client prediction" last_reviewed: 2026-07-16 --- # Server authority and validation ## Core principle If it can give a player an advantage, the server must be the one that ultimately decides whether it happens. Client can: - Send "I want to do X" - Predict the result for smooth visuals - Display the outcome after server confirmation Server must: - Receive the intent - Run all the important checks (distance, timing, cost, prerequisites, anti-cheat, etc.) - Apply the change (or reject it) - Replicate the authoritative result ## Practical validation checklist (per remote) - Is the player alive / in a valid state? - Is the action on cooldown? - Does the player have the required resources / level / item? - Is the target in range / line of sight? - Is the data the client sent reasonable (numbers within bounds, instances exist and are valid)? - Has this player been rate-limited on this action recently? Only after all checks pass do you apply the effect and save/persist as needed. ## Common implementation Many teams keep a "Validator" or "ActionHandler" module on the server that all Remotes funnel through. Example structure: ```lua --!strict -- `actionName` is selected by server code, never copied from a client string. local limiter = RateLimiter.default local function handleAction(player: Player, actionName: string, payload: any) local allowed = limiter:canPerform(player, actionName) if not allowed then return end local validator = Validators[actionName] if not validator or not validator(player, payload) then return end Effects[actionName](player, payload) end ``` ## Concrete argument sanitization Sanitize every value the client sends before trusting it. ```lua local function sanitizeNumber(value: unknown, min: number, max: number): number? if typeof(value) ~= "number" then return nil end if value ~= value then return nil end -- reject NaN return math.clamp(value, min, max) end local function sanitizeInstance(value: unknown, expectedClass: string, ancestor: Instance): Instance? if typeof(value) ~= "Instance" then return nil end if not value:IsA(expectedClass) then return nil end if not value:IsDescendantOf(ancestor) then return nil end return value end local VALID_KEYS = { Slot = true, Amount = true } local function sanitizeDictionary(value: unknown): { [string]: unknown } if typeof(value) ~= "table" then return {} end local out = {} for k, v in pairs(value :: { [string]: unknown }) do if typeof(k) == "string" and VALID_KEYS[k] then out[k] = v end end return out end ``` Instance references from the client can refer to any replicated Instance. Always re-validate the type, ancestry, and whether the player is permitted to interact with that specific object. ## Network ownership For physics objects (vehicles, projectiles, pushable crates): - The network owner simulates the physics. - Server can change ownership with `SetNetworkOwner`, which sets ownership for the **entire connected assembly**. - Anchored parts are always server-owned; `SetNetworkOwner` cannot override them. - Automatic ownership is based on character proximity and client capacity, not simply "nearest player." - Use `Workspace.SetNetworkOwnerAuto = false` to disable automatic ownership assignment entirely when you need full manual control (e.g., competitive or tightly-controlled physics). - Always validate important outcomes on the server regardless of who owns the physics. ## When client prediction is acceptable - Cosmetic animations - Local camera work - UI state - Short-term movement prediction (with server correction) Never predict economy, unlocks, damage application, or quest progress. See the [exploits-and-defenses reference](exploits-and-defenses.md) for how to handle the cases where clients try to lie.
-
-
scripts
-
RateLimiter.lua 5.9 KB · in bundle
-
-
tests
-
RateLimiter.spec.lua 1.3 KB · in bundle
-
-
SKILL.md 6.8 KB
--- name: roblox-networking description: "Design or review Roblox client-server communication. Use for remotes, bindables, replication, network ownership, input validation, rate limiting, and exploit defenses." last_reviewed: 2026-07-16 --- # roblox-networking **Core sources:** https://create.roblox.com/docs/projects/client-server , https://create.roblox.com/docs/scripting/security/security-tactics (server-authority techniques), the entire scripting/security/ section (security-tactics, defensive-design, client-server-boundary, network-ownership, access-control, etc.), events/remote and bindable, plus the relevant Engine classes (RemoteEvent, RemoteFunction, Bindable*, RunService, etc.). ## Authority boundary The server is authoritative for game state and simulation. The client simulates for responsiveness and renders what the server tells it. Anything that can be abused (economy, progression, combat outcomes, movement in competitive play) must be validated or simulated on the server. ## Communication tools **RemoteEvent**. Fire-and-forget across the boundary. - Client → Server: `RemoteEvent:FireServer(...)` → server handler receives `(player, ...)` - Server → Client: `FireClient(player, ...)` or `FireAllClients(...)` **RemoteFunction**. Request/response (yields). - Client: `InvokeServer(...)` → server `OnServerInvoke(player, ...)` - Server can InvokeClient (less common, has caveats). > **Critical warning:** `RemoteFunction:InvokeClient` yields the server until the targeted client returns a value. A hostile or lagging client can leave the invocation pending and hang the server indefinitely. Avoid server→client invocation; if you must use it, enforce a timeout and treat the client as untrusted. **BindableEvent / BindableFunction**. Same-side only (server modules talking to each other, or client modules). They stay on one side of the network boundary. General rule: Client sends *intent*. Server decides *outcome* and replicates the result (or lets normal Instance replication handle it). ## Server authority techniques - Validate every client action (distance, rate, prerequisites, stamina, line-of-sight, etc.). - Simulate critical systems on the server (or at least re-execute the important parts). - Use server tables or Attributes as source of truth, not leaderstats or client-visible values. - For movement/physics in competitive games: rely on Roblox physics + network ownership + server validation/correction. - Replicate only what each client needs (don't broadcast entire inventories every frame). See the official server-authority techniques page for movement validation, physics, etc. ## Network ownership Unanchored parts and assemblies have a network owner (usually the nearest player or the server, based on proximity and client capacity). The owner simulates the physics locally for low latency. Server can call `BasePart:SetNetworkOwner(player or nil)` to set ownership for the **entire connected assembly**. Anchored parts are always owned by the server and cannot be manually reassigned. Critical for vehicles, projectiles, pushable objects, etc. Misuse leads to rubber-banding for other players or easy exploitation. ## Common exploit classes and defenses - Speed/fly/noclip: server movement validation or physics ownership + correction. - Remote spam / duplication: per-player rate limiting + server-side cooldowns + argument validation. - LocalScript injection / free-model backdoors: audit assets, use capabilities, server validation. - Tampering with leaderstats or client-visible values: treat them as pure display; authoritative state lives in server tables or DataStores. - Information disclosure: don't parent secret models in replicated locations; use streaming, server-only storage (ServerStorage/ServerScriptService), and avoid putting sensitive geometry/triggers where clients can see them. `CanQuery` and collision groups are **not** confidentiality controls; they only affect spatial queries and physics collisions, not replication or rendering. **Rate limiting pattern (simple but effective):** Keep a table of last action times per player per action type. On Remote, check delta. Warn or kick on abuse. **Capabilities (experimental security layer):** Script capabilities let you restrict what scripts are allowed to do at a granular level. They are currently experimental. To use them, set `Workspace.SandboxedInstanceMode = Enum.SandboxedInstanceMode.Experimental`, then set `Sandboxed = true` and assign a `SecurityCapabilities` value on the script or a parent container (Model/Folder). This restricts the script to only the abilities in its capabilities set. ## Access control and confidentiality - What the client can see: control with Instance streaming, don't parent sensitive geometry in Workspace if clients shouldn't know the exact layout, use server-only containers where possible. - Confidential data (exact economy formulas, drop rates, anti-cheat parameters) should stay on the server. ## Secure monetization and data flows (tie-in to other skills) - Prompt purchase on client. - For game passes: only grant benefits inside `PromptGamePassPurchaseFinished` when `wasPurchased == true`, and re-verify ownership with `UserOwnsGamePassAsync` on `PlayerAdded`. - For developer products and subscriptions: validate and grant benefits only inside the server-side `ProcessReceipt` callback. - DataStore writes only from server Scripts. - Any client Remote that says "I bought X, give me the item" must be ignored or treated as a hint and re-validated on the server. ## Practical placement rules - Authoritative logic, economy, progression, combat resolution, datastore access → ServerScriptService or server-only modules. - UI, input handling, cosmetic prediction, local VFX → client (LocalScripts or client modules under PlayerGui or required from ReplicatedStorage with IsClient branches). - Shared pure functions / constants / Remote definitions → ReplicatedStorage modules (loaded on both sides but executed in context). Use `RunService` context checks everywhere you are unsure. ## Scripts - `scripts/RateLimiter.lua`: a reviewed per-player/per-action limiter with a hard minimum interval, token capacity/refill, bounded action-key allocation, and denial reasons. Call `destroy()` on dedicated instances to disconnect cleanup; `RateLimiter.default` is the shared singleton. <!-- catalog:references:start --> ## Reference index - [exploits-and-defenses.md](references/exploits-and-defenses.md): Review movement exploits, remote abuse, asset backdoors, or data exposure. - [remote-and-bindable-patterns.md](references/remote-and-bindable-patterns.md): Choose events versus functions or define communication payloads. - [server-authority-and-validation.md](references/server-authority-and-validation.md): Validate remote arguments, enforce cooldowns, or review client prediction. <!-- catalog:references:end -->
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.