
Rblxsync
- 2 installs
- Updated July 25, 2026
- dig1t/rblxsync
Declaratively manages Roblox experience metadata (game passes, developer products, badges, places) from one YAML file via the rblxsync CLI and GitHub Action.
About
Sets up and runs rblxsync, a Rust CLI plus GitHub Action that syncs Roblox Universe settings, game passes, developer products, badges and places from a YAML file over the Open Cloud API. A developer uses it to author rblxsync.yml, preview diffs, publish places and wire the generated Config.luau into Luau game code.
- One rblxsync.yml is the source of truth for game passes, developer products, badges and places
- Idempotent sync via Open Cloud API with dry-run preview and generated Config.luau
Rblxsync by the numbers
- 2 all-time installs (skills.sh)
- Ranked #1,839 of 2,715 Automation & Workflows skills by installs in the Skillselion catalog
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/dig1t/rblxsync --skill rblxsyncAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2 |
|---|---|
| Last updated | July 25, 2026 |
| Repository | dig1t/rblxsync ↗ |
What it does
Declaratively manages Roblox experience metadata (game passes, developer products, badges, places) from one YAML file via the rblxsync CLI and GitHub Action.
Files
rblxsync
rblxsync is a Rust CLI + GitHub Action that declaratively manages Roblox experience metadata via the Open Cloud API. One YAML file (rblxsync.yml) is the source of truth for Universe settings, Game Passes, Developer Products, Badges, and Places. Running it is idempotent: resources are matched by name (case-insensitive), created if missing, updated (PATCH) if present.
Use this skill to set rblxsync up in a user's Roblox project, author/edit their config, run syncs safely, and wire the generated Config.luau into game code.
Mental model (read this first)
- `rblxsync.yml` = desired state, hand-edited, committed.
- `rblxsync-lock.yml` = generated state (resource IDs + icon hashes).
Commit it. Never hand-edit it — it is overwritten on the next sync.
- `Config.luau` (only if
output_pathis set) = generated, typed Luau module
of all resource IDs. Game code requires it. Never hand-edit it.
- Matching is by name. Renaming a resource in the YAML makes rblxsync think
it is a new resource (it creates a second one) rather than renaming the old. To rename, change it in Roblox/lock state, not just the YAML. Flag this to the user before any rename.
Golden rules
1. Never run a mutating sync without previewing first. Always do rblxsync run --dry-run and show the user the diff before rblxsync run. 2. Never commit, print, or paste `ROBLOX_API_KEY` or `ROBLOX_COOKIE`. They live in a gitignored .env (local) or CI secrets. Treat them like passwords. 3. The `--config` flag is global and goes BEFORE the subcommand. rblxsync --config prod.yml run ✅ — rblxsync run --config prod.yml ✗. 4. Creating a badge costs 100 Robux each. Confirm with the user before a sync that adds new badges. 5. Confirm before destructive or paid actions: new badges (Robux), publishing places (goes live), any first real run.
Setup workflow (new project)
Run these steps when adding rblxsync to a codebase that doesn't have it yet.
1. Confirm install. Check rblxsync --version / which rblxsync. If absent, install (cargo install --path . from source, or a tool-manager pin once binaries are published — see references/cli-and-ci.md). 2. Create `.env` with ROBLOX_API_KEY=... and ensure .env is gitignored. Add ROBLOX_COOKIE=... only if the config will set universe settings (see the cookie gotcha below). Never write real secret values yourself — leave placeholders and tell the user to fill them in. 3. Write a minimal `rblxsync.yml` — only universe.id is required. Start small; add sections incrementally. Copy assets/rblxsync.example.yml as a starting point and trim it to what the user actually needs. 4. Validate: rblxsync validate (no API key / network needed). 5. Preview: rblxsync --config rblxsync.yml run --dry-run. 6. Apply: rblxsync run once the user approves the preview. 7. Commit rblxsync.yml and rblxsync-lock.yml (NOT .env). If output_path is set, commit the generated Config.luau too.
Minimal config
Only universe.id is required:
universe:
id: 123456789
game_passes:
- name: "VIP Pass"
price: 100For the full field-by-field schema, defaults, and every gotcha, read references/config-schema.md. A complete annotated sample is in assets/rblxsync.example.yml.
Commands
| Command | What it does |
|---|---|
rblxsync run [--dry-run] | Sync universe settings + game passes, products, badges. Default command. Writes rblxsync-lock.yml and (if set) Config.luau. |
rblxsync publish | Publish .rbxl places where publish: true. Always publishes (no "save"). Does NOT need the cookie. |
rblxsync validate | Parse + check the YAML (dup names, etc.). No API key, no network. |
rblxsync export [-o PATH] [--lua] | One-way snapshot of live resources to a flat Luau/Lua table. NOT a config you can feed back in, and NOT the same shape as Config.luau. |
Full command reference, flags, the GitHub Action, environment variables, and Open Cloud permission scopes are in references/cli-and-ci.md.
The cookie gotcha (very common error)
rblxsync run hard-fails demanding `ROBLOX_COOKIE` whenever any universe field is set — including genre and max_players, which are local-only and never actually call a cookie API. So a config that sets only genre still requires the cookie even though no cookie request is made.
If a user hits "ROBLOX_COOKIE is not set" and doesn't want to provide a cookie, the fix is to *remove all `universe. fields except id** from the config (move name/description/etc. tracking elsewhere). Universe *settings* updates use .ROBLOSECURITY cookie auth against develop.roblox.com — an API key alone cannot update them. See references/cli-and-ci.md` for how to obtain the cookie safely.
Fields that don't sync (surface these, don't silently rely on them)
- `genre` and `max_players` — tracked in lock/
Config.luaubut **never
pushed to Roblox** (max_players is a per-place setting).
- Developer Product `is_active` — parsed but not synced; has no effect.
- Game Pass `is_for_sale` — this one is synced.
Do not type game code against a Developer Product IsActive field — the generated DeveloperProduct type has no such field ({ Id, Name, Description, Price }).
Integrating with game code
If the user wants resource IDs available in Luau, set output_path (e.g. src/shared/Config.luau or a Rojo-mapped path) and require the generated module:
local Config = require(game.ReplicatedStorage.Shared.Config)
local vipId = Config.GamePasses[1].Id
print(Config.Universe.Name)Wiring it cleanly (lookup by name, Rojo paths, regenerate-on-sync, lock-file commits) is covered in references/integration.md — read it before editing a user's existing Luau to consume rblxsync output.
Adding to CI
A ready-to-use workflow template is in assets/github-workflow-sync.yml. Copy it to .github/workflows/, set repo secrets (ROBLOX_API_KEY, optionally ROBLOX_COOKIE), and pin the action to a published ref (e.g. @v0.1.0). The CI gotchas (the args input word-splits and cannot contain spaces; only published tags exist) are in references/cli-and-ci.md.
When unsure
- Schema / field semantics / defaults →
references/config-schema.md - CLI flags, Action inputs, env vars, permission scopes →
references/cli-and-ci.md - Consuming output in Luau, lock-file handling, idempotency →
references/integration.md - Authoritative upstream docs: the project's
README.mdanddocs/API.md.
# Copy to .github/workflows/sync.yml
#
# Prerequisites (repo Settings -> Secrets and variables -> Actions):
# - ROBLOX_API_KEY (required)
# - ROBLOX_COOKIE (only if rblxsync.yml sets universe settings)
#
# Pin the action to a published ref. v0.1.0 exists; a moving @v1 tag does NOT
# yet — do not use @v1 until it's published. @main works but is unpinned.
name: Sync Roblox Experience
on:
push:
branches: [main]
# Optional: only run when relevant files change.
# paths:
# - "rblxsync.yml"
# - "rblxsync-lock.yml"
# - "assets/**"
workflow_dispatch: {} # allow manual runs
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Recommended: preview first on PRs / dry runs.
# The `args` input is appended UNQUOTED and word-splits — use only
# space-free flags. One arg containing a space will be broken apart.
- name: Preview changes (dry run)
uses: dig1t/rblxsync@v0.1.0
with:
api_key: ${{ secrets.ROBLOX_API_KEY }}
roblox_cookie: ${{ secrets.ROBLOX_COOKIE }}
command: run
args: --dry-run
- name: Sync Roblox metadata
uses: dig1t/rblxsync@v0.1.0
with:
api_key: ${{ secrets.ROBLOX_API_KEY }}
roblox_cookie: ${{ secrets.ROBLOX_COOKIE }}
command: run
config: rblxsync.yml
# rblxsync.yml — annotated template.
# Only `universe.id` is required. Delete any section you don't need and start small.
# Full schema: references/config-schema.md
# Directory holding icon files referenced below (default: "assets").
assets_dir: assets/icons/
# Who pays the 100 Robux fee per NEW badge: "user" or "group". Only needed if you create badges.
badge_payment_source: "user"
# If set, every successful `rblxsync run` regenerates a typed Luau module here.
# Point this at a Rojo-synced path so game code can require it.
output_path: "src/shared/Config.luau"
# Asset creation context for uploaded icons. Required only when uploading icons.
creator:
id: "12345678" # User ID or Group ID, as a string (quote it)
type: "user" # "user" or "group"
universe:
id: 123456789 # Universe ID (required) — NOT a place ID
# --- Everything below triggers the ROBLOX_COOKIE requirement on `run`. ---
# If you only want `id`, delete the rest to avoid needing a cookie.
name: "My Awesome Game" # synced (cookie)
description: "Updated via rblxsync!" # synced (cookie)
genre: "adventure" # LOCAL-ONLY: never sent to Roblox
playable_devices: ["computer", "phone", "tablet"] # computer | phone | tablet | console | vr
max_players: 50 # LOCAL-ONLY: never PATCHed (per-place setting)
private_server_cost: "disabled" # "disabled" | 0 (free) | 100 (Robux)
game_passes:
- name: "VIP Pass" # match key (case-insensitive); renaming creates a NEW pass
description: "Exclusive access and perks"
price: 100
icon: "vip.png" # relative to assets_dir
is_for_sale: true # synced
developer_products:
- name: "Speed Boost"
description: "Double speed for 5 minutes"
price: 50 # REQUIRED for developer products
icon: "boost.png"
is_active: true # parsed but NOT synced — has no effect today
badges:
- name: "First Win" # creating a NEW badge costs 100 Robux
description: "Awarded for your first victory!"
icon: "first_win.png"
is_enabled: true
places:
- place_id: 1234567890
file_path: "places/start_place.rbxl"
publish: true # only published by `rblxsync publish` when true
rblxsync Claude Code skill
A Claude Code skill that teaches Claude how to work with `rblxsync` — set it up, author/edit rblxsync.yml, run safe syncs, and wire the generated Config.luau into Luau game code.
Install
Copy the rblxsync/ skill folder into one of:
- This project only:
<your-project>/.claude/skills/rblxsync/ - All your projects (global):
~/.claude/skills/rblxsync/
# Global install from a checkout of the rblxsync repo:
mkdir -p ~/.claude/skills
cp -R .claude/skills/rblxsync ~/.claude/skills/rblxsyncThe skill is then available automatically (Claude invokes it by relevance) or via /rblxsync in Claude Code. No restart needed.
What's inside
rblxsync/
├── SKILL.md # main workflow + golden rules
├── references/
│ ├── config-schema.md # full rblxsync.yml field reference
│ ├── cli-and-ci.md # commands, env vars, Action, permissions
│ └── integration.md # consuming Config.luau, lock file, idempotency
└── assets/
├── rblxsync.example.yml # annotated config template
└── github-workflow-sync.yml # CI workflow templateVerify
# from the skill-creator skill, if installed:
python ~/.claude/skills/skill-creator/scripts/quick_validate.py ~/.claude/skills/rblxsyncrblxsync — CLI, Environment, CI, Permissions
Installation
The binary is rblxsync.
# From source (works today)
cargo install --path .
# Tool managers — ONLY once binary releases exist. Pin to a published tag.
# rokit.toml / aftman.toml
# [tools]
# rblxsync = "dig1t/rblxsync@0.1.0"As of v0.1.0 there is no published binary-release pipeline — the GitHub Action
builds from source, and the source tag v0.1.0 exists. Prefer from sourceuntil pre-built binaries are published. Do not assume rokit add works yet.CLI
rblxsync [--config <PATH>] [COMMAND]The --config / -c flag is global and must come before the subcommand:
rblxsync --config production.yml run # ✅ correct
rblxsync run --config production.yml # ✗ clap rejects thisIf no subcommand is given, it defaults to run (not dry-run).
run [--dry-run]
Syncs universe settings + game passes, developer products, badges. Idempotent (match by name, create/PATCH). Icons re-upload only when their local SHA-256 differs from the lock file.
--dry-run: previews changes, makes no mutating HTTP calls, does not
write state, does not write Config.luau. Always run this first.
- Requires
ROBLOX_COOKIEif anyuniverse.*setting is present (see below). - On success writes
rblxsync-lock.yml, and regeneratesConfig.luauif
output_path is set.
Path note: the lock file is loaded from the config file's parent dir but
saved to the current working directory. Run rblxsync from the directory that
holds rblxsync-lock.yml so state stays consistent.publish
Publishes every place with publish: true (always a Published version — no "Saved" option). Does NOT need the cookie. Per-place errors are logged but not fatal; a place with a missing file_path is skipped and the rest continue. Publishing makes the place live — confirm with the user.
validate
Parses and checks the config. No API key, no network. Rejects duplicate case-insensitive names. Exits 1 on failure.
export [-o/--output PATH] [--lua]
Pulls live game passes, products, badges and dumps a flat Luau/Lua table. Default filename config.luau (config.lua with --lua — content is identical; --lua only changes the default name). This is a one-way snapshot for inspection/migration. It is NOT a valid rblxsync.yml, and NOT the richer shape that run writes to output_path. Flat shape:
return {
game_passes = { { name = "VIP Pass", id = 123456, price = 100 } },
developer_products = { { name = "Speed Boost", id = 234567, price = 50 } },
badges = { { name = "First Win", id = 345678 } },
}Environment variables
| Variable | Required | Notes |
|---|---|---|
ROBLOX_API_KEY | Yes (all but validate) | Open Cloud API key, sent as x-api-key. |
ROBLOX_COOKIE | Conditional | .ROBLOSECURITY cookie. Required only when universe settings are defined. |
RUST_LOG | No | env_logger filter; defaults to info. Use RUST_LOG=debug to troubleshoot. |
Both secrets load from a gitignored .env via dotenvy. Never commit or print them. Add to .env:
ROBLOX_API_KEY=your_api_key_here
ROBLOX_COOKIE=your_roblosecurity_cookie_here # only if syncing universe settingsGetting the .ROBLOSECURITY cookie (only if needed)
1. Log into roblox.com in a browser. 2. DevTools (F12) → Application → Cookies → copy the value of .ROBLOSECURITY.
Anyone with this cookie can access the account — treat it like a password, store it only in .env or a CI secret, never in the repo or chat. Don't fetch or echo it yourself; instruct the user to place it.
Open Cloud API key permissions
Universe settings updates do not use the API key — they use cookie auth against develop.roblox.com. The API key needs these scopes:
| Feature | Scope | Endpoint(s) |
|---|---|---|
| Game Passes | read + write | game-passes/v1/universes/{uid}/game-passes |
| Developer Products | read + write | developer-products/v2/universes/{uid}/developer-products |
| Badges | read + create/manage | list via badges.roblox.com; create/update/icon via legacy legacy-badges / legacy-publish |
| Assets (icons) | upload | POST /assets/v1/assets (multipart), polled at GET /assets/v1/{operation} |
| Places | publish | POST /v1/universes/{uid}/places/{placeId}/versions?versionType=Published |
429 responses are retried up to 3 times honoring Retry-After.
GitHub Action
Composite action: checks out rblxsync into .rblxsync-action, sets up stable Rust, caches cargo, builds --release, then runs rblxsync "$COMMAND" --config "$CONFIG" $ARGS.
Inputs
| Input | Required | Default | Notes |
|---|---|---|---|
api_key | Yes | – | Open Cloud key → ROBLOX_API_KEY. |
command | No | run | run / publish / validate / export. |
config | No | rblxsync.yml | Passed as --config. |
args | No | "" | Extra flags, appended unquoted. |
roblox_cookie | No | "" | .ROBLOSECURITY → ROBLOX_COOKIE. Only for universe settings. |
`args` word-splits and cannot be quoted. Multiple flags like
--dry-run --foo split correctly, but **any single argument containing a spaceis broken apart** — there is no quoting mechanism. Use only space-free flags.
Pin to a published ref. Av0.1.0tag exists; a moving@v1major tag is
not published. Do not reference@v1until it exists.@mainworks but is
unpinned.
Store ROBLOX_API_KEY (and ROBLOX_COOKIE if needed) under Settings → Secrets and variables → Actions. See assets/github-workflow-sync.yml for a copy-paste workflow.
rblxsync.yml — Configuration Schema
Parsed by serde_yaml into RblxSyncConfig. The default config path is rblxsync.yml; override with the global --config flag. Only universe.id is required.
Root
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
assets_dir | string | No | assets | Directory holding icon files referenced by resources. |
creator | object | No | – | Asset creation context. Required only when uploading icons. |
universe | object | Yes | – | Target universe + settings. |
game_passes | list | No | [] | Game passes to sync. |
developer_products | list | No | [] | Developer products to sync. |
badges | list | No | [] | Badges to sync (100 Robux each to create). |
places | list | No | [] | Places available to rblxsync publish. |
badge_payment_source | string | No | – | "user" or "group" — who pays the 100 Robux badge fee. |
output_path | string | No | – | Where run regenerates the typed Config.luau (e.g. src/shared/Config.luau). |
creator
Drives the asset creationContext (user vs group) for uploaded icons.
| Field | Type | Required | Notes |
|---|---|---|---|
id | string | Yes | User or group id, as a string (quote it). |
type | string | Yes | "user" or "group". Anything other than "group" is treated as a user. |
universe
| Field | Type | Required | Notes |
|---|---|---|---|
id | number (u64) | Yes | Universe ID (NOT a place ID). |
name | string | No | Experience name. Synced (cookie). |
description | string | No | Experience description. Synced (cookie). |
genre | string | No | Local-only. Never sent to any API. Not validated against any list. |
playable_devices | list of string | No | Allowed: computer, phone, tablet, console, vr. Unknown values are filtered out. Synced (cookie). |
max_players | number (u32) | No | Local-only. Never PATCHed (it's a per-place setting). |
private_server_cost | special | No | See table below. Synced (cookie). |
Cookie trigger: setting any of the above (even local-only genre /max_players) makesrblxsync runrequireROBLOX_COOKIE. If you only want
universe.idwithout a cookie, set nothing else underuniverse.
private_server_cost values
| YAML value | Meaning | API effect |
|---|---|---|
"disabled" (case-insensitive) | Private servers off | allowPrivateServers = false |
"free", 0, or "0" | Free private servers | allowPrivateServers = true, price 0 |
positive integer (100 or "100") | Paid private servers | allowPrivateServers = true, price n |
Negative values and values > u32::MAX are rejected at parse time.
game_passes[]
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
name | string | Yes | – | Match key, unique case-insensitive. Renaming creates a new pass. |
description | string | No | – | |
price | number (u32) | No | 0 on create | Robux. |
icon | string | No | – | Filename relative to assets_dir. Re-uploaded only when its SHA-256 changes. |
is_for_sale | boolean | No | – | Synced. |
developer_products[]
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | Match key, unique case-insensitive. |
description | string | No | |
price | number (u32) | Yes | Robux. Required (unlike game passes). |
icon | string | No | Filename relative to assets_dir. |
is_active | boolean | No | Parsed but NEVER synced. Has no effect today. Don't rely on it. |
badges[]
| Field | Type | Required | Notes |
|---|---|---|---|
name | string | Yes | Match key, unique case-insensitive. |
description | string | No | |
icon | string | No | Filename relative to assets_dir. |
is_enabled | boolean | No | Mapped to the API enabled field on PATCH. |
Creating a badge costs 100 Robux and needs badge_payment_source ("user" or "group"). Confirm with the user before syncing new badges.
places[]
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
place_id | number (u64) | Yes | – | Target place ID. |
file_path | string | Yes | – | Path to a .rbxl / .rbxlx file. |
publish | boolean | No | false | Only publish: true places are published by rblxsync publish. |
Validation (rblxsync validate)
Runs with no API key and no network. It confirms the file exists, parses it, and rejects case-insensitive duplicate names within game passes, products, or badges. Always validate before a real sync.
Duplicate-name pitfall
Because matching is case-insensitive by name, two entries like "VIP Pass" and "vip pass" in the same list are a hard validation error. Across the live account, a name that already exists on Roblox is updated rather than duplicated; a name that doesn't exist is created.
Integrating rblxsync output into a codebase
The generated Config.luau (from run, via output_path)
When output_path is set, every successful rblxsync run regenerates a --!strict Luau module at that path. It's deterministic (resources sorted by id) and always emits the full type definitions plus tables — empty sections appear as empty tables, never omitted. Keys are PascalCase.
--!strict
-- Auto-generated by rblxsync. Do not edit manually.
-- This file is regenerated each time `rblxsync run` completes.
export type Universe = {
Id: number,
Name: string?,
Description: string?,
Genre: string?,
PlayableDevices: {string}?,
MaxPlayers: number?,
PrivateServerCost: (number | "disabled")?,
}
export type GamePass = { Id: number, Name: string, Description: string?, Price: number?, IsForSale: boolean? }
export type DeveloperProduct = { Id: number, Name: string, Description: string?, Price: number? }
export type Badge = { Id: number, Name: string, Description: string?, IsEnabled: boolean? }
return {
Universe = { Id = 123456789, Name = "My Awesome Game", MaxPlayers = 50 } :: Universe,
GamePasses = { { Id = 111, Name = "VIP Pass", Price = 100, IsForSale = true } } :: { GamePass },
DeveloperProducts = {} :: { DeveloperProduct },
Badges = {} :: { Badge },
}Key facts:
DeveloperProductis exactly{ Id, Name, Description, Price }— there is
no IsActive field. Don't type game code against one.
PrivateServerCostis a bare number for paid/free, or the string"disabled".GenreandMaxPlayersappear here for reference but were never pushed to
Roblox by the sync.
- Do not hand-edit this file — it is overwritten on every
run. To change its
format you'd edit the tool's src/output.rs, not the output.
Consuming it in-game
output_path should point at a Rojo-synced location so the module lands in the game (e.g. ReplicatedStorage/Shared/Config). Then:
local Config = require(game.ReplicatedStorage.Shared.Config)
print(Config.Universe.Name)
local vipPassId = Config.GamePasses[1].IdPrefer name-based lookup over index
GamePasses[1] is brittle (order is by id, and entries shift as resources are added). When wiring real game code, look up by name so it survives reordering:
local function byName<T>(list: { T }, name: string): T?
for _, item in list do
if (item :: any).Name == name then
return item
end
end
return nil
end
local vip = byName(Config.GamePasses, "VIP Pass")
local vipId = vip and vip.IdWhen editing a user's existing code, match their conventions (their own lookup helpers, module path aliases, naming). Don't introduce a new pattern if they already have one.
export output is different — don't confuse them
rblxsync export writes a flat, untyped table with snake_case keys (game_passes / developer_products / badges; passes & products carry name/id/price, badges only name/id). It's a one-way snapshot for inspection or migrating an existing game into a config — it is NOT what game code should require long-term, and NOT a rblxsync.yml. Use the output_path Config.luau for in-game consumption.
Lock file (rblxsync-lock.yml)
Generated local state mapping live resource IDs to names, prices, flags, and icon SHA-256 hashes. Top-level keys: universe, game_passes, developer_products, badges (the last three are maps of resource-id → state).
- Commit it. It's how syncs stay idempotent across machines/CI and how icon
change-detection works (only re-uploads when the hash differs).
- Never hand-edit it — overwritten on the next sync. A missing file is treated
as empty default state (so the next sync will try to create everything fresh).
Idempotency & renames
- Re-running
rblxsync runwith an unchanged config is a no-op against Roblox
(nothing to PATCH, no icon re-upload).
- Matching is by name. Changing a
name:in the YAML does NOT rename the
Roblox resource — rblxsync sees an unknown name and creates a new one, leaving the old resource behind. Warn the user before any rename and handle the rename on Roblox's side (or accept the new resource + retire the old).
Recommended repo hygiene
- Commit:
rblxsync.yml,rblxsync-lock.yml, and the generatedConfig.luau
(if output_path is set), plus icon files under assets_dir.
- Gitignore:
.env(always). - Keep
Config.luauin version control so reviewers see ID changes in diffs and
the game builds without a network sync.