{
  "id": "roblox-luau",
  "type": "loadout",
  "title": "Roblox Luau (Rojo)",
  "summary": "Luau conventions for Rojo projects with a server-authoritative mindset, plus a skill that reviews every RemoteEvent for exploits before you publish.",
  "description": "On Roblox, the client is the exploiter's playground: anything a LocalScript sends can be faked. Agents\nroutinely write remotes like \"GiveCoins(amount)\" that hand the game to the first script kiddie. This\nloadout teaches your agent typed Luau, the Rojo file layout, server authority and safe DataStore use,\nand adds a skill that audits every remote in the place.\n",
  "highlights": [
    "Server-authoritative rules: clients request, the server decides",
    "--!strict typed Luau, task library, no deprecated wait/spawn",
    "DataStore patterns with pcall, UpdateAsync and session-safe saving",
    "Remote security review skill with a per-remote verdict"
  ],
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "Agent Loadout",
    "url": "https://loadout.jnw-it.com"
  },
  "categories": [
    "gamedev",
    "security"
  ],
  "stacks": [
    "roblox",
    "luau",
    "rojo"
  ],
  "files": [
    {
      "path": "AGENTS.md",
      "role": "instructions",
      "description": "Roblox Luau project rules for Rojo layouts.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": "AGENTS.md",
        "codex": "AGENTS.md",
        "cursor": "AGENTS.md",
        "gemini-cli": "AGENTS.md",
        "copilot": "AGENTS.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/AGENTS.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/roblox-luau/latest/AGENTS.md",
      "bytes": 2720,
      "lang": "markdown",
      "content": "# AGENTS.md: Roblox Luau (Rojo)\n\n## Layout (Rojo, see `default.project.json`)\n```\nsrc/server/   → ServerScriptService     server Scripts (*.server.luau) and server-only modules\nsrc/client/   → StarterPlayerScripts    LocalScripts (*.client.luau)\nsrc/shared/   → ReplicatedStorage       ModuleScripts used by both sides, plus Remotes\n```\n- `ServerStorage` and `ServerScriptService` are invisible to clients. Anything in `ReplicatedStorage` or the workspace is visible to exploiters.\n- Run `rojo serve` and connect from the Studio plugin to sync. Do not edit synced scripts inside Studio — edits are overwritten.\n\n## The golden rule: the server decides\n- Clients can be fully controlled by exploiters. Every value from a `RemoteEvent`/`RemoteFunction` is an untrusted request.\n- Remotes express intent (\"I want to buy item X\", \"I pressed attack\"), never outcomes (\"give me 500 coins\", \"deal 999 damage\").\n- On the server, for every remote handler:\n  1. Check argument types (`typeof(x) == \"string\"`) and ranges; reject NaN and huge numbers.\n  2. Check the player is allowed (owns the item, has the cash, is close enough, cooldown passed).\n  3. Rate-limit per player.\n  4. Compute the result on the server.\n- Never use `RemoteFunction:InvokeClient` — a client can yield forever and hang the server thread.\n- Currency, inventory, damage, cooldowns and leaderboards live on the server.\n\n## Luau style\n- `--!strict` at the top of every file; type function signatures and module tables.\n- `task.wait`, `task.spawn`, `task.delay`, `task.defer` — never the deprecated `wait`, `spawn`, `delay`.\n- Get services once at the top: `local Players = game:GetService(\"Players\")`.\n- Use `WaitForChild` on the client for replicated objects; avoid it on the server for things the server creates.\n- Disconnect connections you create per player or per round, or clean up with a maid/trove pattern.\n- `PascalCase` for modules and services, `camelCase` for locals and functions.\n\n## Data\n- `DataStoreService` calls can fail: always wrap them in `pcall` and retry with backoff.\n- Prefer `UpdateAsync` to `SetAsync` for player data so concurrent servers do not overwrite each other.\n- Load on `PlayerAdded`, save on `PlayerRemoving` **and** in `game:BindToClose` (with a time budget), plus periodic autosave.\n- Do not save if the initial load failed — you would overwrite real data with defaults.\n- Studio needs \"Enable Studio Access to API Services\" switched on to test DataStores; remind the human.\n\n## Before you say \"done\"\nCode type-checks (`luau-lsp` or Studio's Script Analysis shows no errors), and you have listed what the human should test in a\nStudio play-test — ideally with two players (Test → Clients and Servers).\n"
    },
    {
      "path": "CLAUDE.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Claude Code.",
      "skill": null,
      "agent": "claude-code",
      "install_to": {
        "claude-code": "CLAUDE.md",
        "codex": null,
        "cursor": null,
        "gemini-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/CLAUDE.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/roblox-luau/latest/CLAUDE.md",
      "bytes": 372,
      "lang": "markdown",
      "content": "# CLAUDE.md\n\n@AGENTS.md\n\n## Claude Code notes\n- Skills from this loadout live in `.claude/skills/`: `remote-security-review`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"Roblox Luau (Rojo)\" v1.0.0 — https://loadout.jnw-it.com/loadouts/roblox-luau/ -->\n"
    },
    {
      "path": "GEMINI.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Gemini CLI.",
      "skill": null,
      "agent": "gemini-cli",
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": null,
        "gemini-cli": "GEMINI.md",
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/GEMINI.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/roblox-luau/latest/GEMINI.md",
      "bytes": 373,
      "lang": "markdown",
      "content": "# GEMINI.md\n\n@./AGENTS.md\n\n## Gemini CLI notes\n- Skills from this loadout live in `.agents/skills/`: `remote-security-review`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"Roblox Luau (Rojo)\" v1.0.0 — https://loadout.jnw-it.com/loadouts/roblox-luau/ -->\n"
    },
    {
      "path": ".cursor/rules/luau-remotes.mdc",
      "role": "rules",
      "description": "Rule applied to server scripts that handle remotes.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": ".cursor/rules/luau-remotes.mdc",
        "gemini-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/.cursor/rules/luau-remotes.mdc",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/roblox-luau/latest/.cursor/rules/luau-remotes.mdc",
      "bytes": 1172,
      "lang": "markdown",
      "content": "---\ndescription: Server-side remote handler rules for Roblox\nglobs: \"src/server/**/*.luau\"\nalwaysApply: false\n---\n\nEvery `OnServerEvent` / `OnServerInvoke` handler must:\n\n1. Treat its arguments as untrusted. Check `typeof` for each one and bounds for numbers (reject NaN: `x ~= x`).\n2. Check permission and game state on the server (ownership, cost, distance, cooldown).\n3. Rate-limit per player.\n4. Compute outcomes on the server. Never accept amounts, damage, prices or positions as facts.\n\n```luau\n--!strict\nlocal Remotes = game:GetService(\"ReplicatedStorage\"):WaitForChild(\"Remotes\")\nlocal Shop = require(script.Parent.Shop)\n\nlocal lastCall: { [Player]: number } = {}\n\nRemotes.BuyItem.OnServerEvent:Connect(function(player: Player, itemId: unknown)\n\tif typeof(itemId) ~= \"string\" or #itemId > 64 then return end\n\tlocal now = os.clock()\n\tif (lastCall[player] or 0) + 0.5 > now then return end\n\tlastCall[player] = now\n\tShop.tryPurchase(player, itemId) -- looks up the price server-side and checks the balance\nend)\n\ngame:GetService(\"Players\").PlayerRemoving:Connect(function(player) lastCall[player] = nil end)\n```\n\nNever use `InvokeClient`. Never `loadstring` anything.\n"
    },
    {
      "path": "skills/remote-security-review/SKILL.md",
      "role": "skill",
      "description": "Audits every RemoteEvent and RemoteFunction for exploitable trust.",
      "skill": "remote-security-review",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/remote-security-review/SKILL.md",
        "codex": ".agents/skills/remote-security-review/SKILL.md",
        "cursor": ".agents/skills/remote-security-review/SKILL.md",
        "gemini-cli": ".agents/skills/remote-security-review/SKILL.md",
        "copilot": ".agents/skills/remote-security-review/SKILL.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/skills/remote-security-review/SKILL.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/roblox-luau/latest/skills/remote-security-review/SKILL.md",
      "bytes": 2545,
      "lang": "markdown",
      "content": "---\nname: remote-security-review\ndescription: Audit a Roblox game's RemoteEvents and RemoteFunctions for exploitable client trust. Use before publishing, after adding remotes, or when players report exploits like infinite money or teleporting.\n---\n\n# Remote security review\n\n## 1. Inventory\n- Find every remote: search for `RemoteEvent`, `RemoteFunction`, `UnreliableRemoteEvent` instances in the Rojo project files\n  (`*.model.json`, `*.project.json`, `.rbxmx`) and in code (`Instance.new(\"RemoteEvent\")`).\n- Find every handler: `OnServerEvent`, `OnServerInvoke`, and on the client `FireServer`/`InvokeServer` calls.\n- Build a table: remote name → server handler file → client call sites → arguments sent.\n\n## 2. Review each handler\nFor each remote, answer:\n1. **What does the client claim?** List each argument. Would an exploiter gain anything by changing it?\n2. **Types checked?** Every argument `typeof`-checked, strings length-limited, numbers bounded and NaN-rejected, Instances checked\n   to be the expected class and in an expected place.\n3. **Authority:** does the server compute the outcome, or trust a client value (amount, damage, price, position, target)?\n4. **Permission/state:** does it check ownership, cost, distance (`(char.HumanoidRootPart.Position - target.Position).Magnitude`),\n   cooldown, round state?\n5. **Rate limiting:** can it be spammed? What happens if it is fired 1,000 times a second?\n6. **Errors:** can bad input throw and break the handler for everyone, or leave state half-updated?\n7. **RemoteFunctions:** never `InvokeClient`. `OnServerInvoke` returns quickly and never yields on client data.\n\n## 3. Also check\n- Server-only modules are not in `ReplicatedStorage` (secrets, admin lists, prices that should be hidden).\n- No `loadstring`, no `require` of asset IDs you do not control.\n- Admin commands check a server-side list of `UserId`s (not names, which can change).\n- Clients are not given network ownership of things that matter (e.g. other players' parts, shop NPCs).\n\n## 4. Report\n```\nRemote review — <place> — <n> remotes\n[Critical] AddCoins (src/server/Economy.server.luau:42) — trusts `amount` from the client. Any exploiter can grant themselves unlimited coins.\n  Fix: remove the remote; award coins on the server when the qualifying event happens.\n[High] Attack — no distance check or cooldown. Fix: check range ≤ weapon reach and a per-player cooldown.\n[OK] RequestRespawn — no arguments, cooldown present.\n```\nOrder by severity. Offer to write the fixes, one remote at a time.\n"
    }
  ],
  "requires": {
    "tools": [
      "rojo",
      "roblox-studio"
    ],
    "env": [],
    "mcp": []
  },
  "tested": {
    "status": "untested",
    "last_run": null,
    "fixture": null,
    "per_agent": {
      "claude-code": "untested",
      "codex": "untested",
      "cursor": "untested",
      "gemini-cli": "untested",
      "copilot": "untested"
    }
  },
  "security": {
    "scanned": true,
    "scanner": "intake-precheck/1",
    "verdict": "clean",
    "network": false,
    "scripts": [],
    "notes": "Instructions only."
  },
  "featured": false,
  "deprecated": false,
  "last_checked": "2026-10-15",
  "published": "2026-10-15",
  "updated": "2026-10-15",
  "compatibility": {
    "claude-code": "native",
    "codex": "native",
    "cursor": "native",
    "gemini-cli": "native",
    "copilot": "native"
  },
  "counts": {
    "rules": 2,
    "skills": 1,
    "mcp": 0
  },
  "url": "https://loadout.jnw-it.com/loadouts/roblox-luau/",
  "api_url": "https://loadout.jnw-it.com/api/loadouts/roblox-luau.json",
  "verification": {
    "level": "verified-install",
    "status": "verified",
    "label": "Verified install",
    "verified": true,
    "checked_at": "2026-10-16T22:54:03Z",
    "commit": "ad36e852567aa3c87821d2a9e2fe4052fd983374",
    "content_current": true,
    "agent_tests": "not-run",
    "counts": {
      "pass": 9,
      "fail": 0,
      "skip": 0,
      "not_applicable": 1
    },
    "meaning": "Level 1: automated checks on the files (parse, install paths, MCP config, safety scan, references, size, licence, starter build). No AI agent was run.",
    "checks": [
      {
        "id": "listing",
        "name": "Listing",
        "status": "pass",
        "summary": "Schema valid; 5 files declared, none missing or extra",
        "details": [
          "tested.status: untested (agent tests are separate from Level 1)"
        ]
      },
      {
        "id": "parse",
        "name": "Files parse",
        "status": "pass",
        "summary": "All 5 files parse; front matter valid",
        "details": []
      },
      {
        "id": "adapters",
        "name": "Adapters",
        "status": "pass",
        "summary": "CLAUDE.md and GEMINI.md import AGENTS.md and are up to date",
        "details": [
          "CLAUDE.md: matches the generator",
          "GEMINI.md: matches the generator"
        ]
      },
      {
        "id": "install-paths",
        "name": "Install paths",
        "status": "pass",
        "summary": "Correct paths for all 5 agents; layouts re-discovered",
        "details": [
          "Claude Code: 3 files, 1 skill in .claude/skills/",
          "Codex CLI: 2 files, 1 skill in .agents/skills/",
          "Cursor: 3 files, 1 skill in .agents/skills/, 1 rule",
          "Gemini CLI: 3 files, 1 skill in .agents/skills/",
          "GitHub Copilot: 2 files, 1 skill in .agents/skills/"
        ]
      },
      {
        "id": "mcp",
        "name": "MCP config",
        "status": "n/a",
        "summary": "No MCP servers in this loadout",
        "details": []
      },
      {
        "id": "safety",
        "name": "Safety scan",
        "status": "pass",
        "summary": "Clean: no credentials, risky shell or hidden text",
        "details": [
          "Scanner intake-precheck/1: verdict clean; network no (listing says no)"
        ]
      },
      {
        "id": "references",
        "name": "References",
        "status": "pass",
        "summary": "No cross-file references to check",
        "details": []
      },
      {
        "id": "size",
        "name": "Size limits",
        "status": "pass",
        "summary": "5 files, 7.0 KB total; largest AGENTS.md 2.7 KB",
        "details": [
          "Limits: 30 files, 32 KB per file, 128 KB total, 500 lines per skill or rule"
        ]
      },
      {
        "id": "licence",
        "name": "Licence",
        "status": "pass",
        "summary": "MIT, by Agent Loadout; text in the repository LICENSE",
        "details": []
      },
      {
        "id": "starter-build",
        "name": "Starter build",
        "status": "pass",
        "summary": "The loadout's Luau example compiles with the official Luau compiler inside a Rojo layout (syntax only: Roblox APIs and Studio can't run in CI)",
        "details": [
          "Applied 6 loadout files at every agent's paths",
          "Example from .cursor/rules/luau-remotes.mdc:14 (luau) → src/server/BuyItem.server.luau",
          "luau 0.743",
          "✓ compile Luau (luau-compile): ${LUAU_BIN} --null src/server/BuyItem.server.luau src/server/Shop.luau",
          "✓ Rojo project file is valid JSON: node -e JSON.parse(require('fs').readFileSync('default.project.json','utf8'))",
          "Covers: The Cursor rule's remote-handler example parses and compiles with Luau 0.743 (--!strict syntax, type annotations, compound assignment)",
          "Covers: Not covered: type-checking against Roblox APIs and running in Roblox Studio, which is closed-source and Windows/macOS-only"
        ]
      }
    ]
  },
  "install": {
    "claude-code": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "CLAUDE.md",
          "target": "CLAUDE.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/CLAUDE.md",
          "role": "adapter"
        },
        {
          "path": "skills/remote-security-review/SKILL.md",
          "target": ".claude/skills/remote-security-review/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/skills/remote-security-review/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/roblox-luau/claude-code.zip"
    },
    "codex": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/remote-security-review/SKILL.md",
          "target": ".agents/skills/remote-security-review/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/skills/remote-security-review/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/roblox-luau/codex.zip"
    },
    "cursor": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": ".cursor/rules/luau-remotes.mdc",
          "target": ".cursor/rules/luau-remotes.mdc",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/.cursor/rules/luau-remotes.mdc",
          "role": "rules"
        },
        {
          "path": "skills/remote-security-review/SKILL.md",
          "target": ".agents/skills/remote-security-review/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/skills/remote-security-review/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/roblox-luau/cursor.zip"
    },
    "gemini-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "GEMINI.md",
          "target": "GEMINI.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/GEMINI.md",
          "role": "adapter"
        },
        {
          "path": "skills/remote-security-review/SKILL.md",
          "target": ".agents/skills/remote-security-review/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/skills/remote-security-review/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/roblox-luau/gemini-cli.zip"
    },
    "copilot": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/remote-security-review/SKILL.md",
          "target": ".agents/skills/remote-security-review/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/roblox-luau/1.0.0/skills/remote-security-review/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/roblox-luau/copilot.zip"
    }
  },
  "source_zip_url": "https://loadout.jnw-it.com/download/roblox-luau/source.zip"
}