Open-source Roblox passport system. Players receive nation-issued passports, collect border stamps, hold visas, and open a Fusion UI (plus an optional physical Tool).
- Nation resolution from Roblox groups (
GameNation,HighestGroupRank, orDual) - Multi-passport issuance (host nation, highest-rank nation, UN when eligible)
- ENTRY / EXIT / TRANSIT stamps and visa grant/revoke
- ProfileService persistence
- Per-nation UI via PR — missing custom UI falls back to
DEFAULT - Cmdr admin commands and optional Discord webhooks
- Open the latest release.
- Download
passport-system.rbxm. - In Roblox Studio, drag the file into the viewport (or Insert from File).
- You’ll get a Folder. Move its contents into your place:
| From the release Folder | Move into |
|---|---|
PassportSystem |
ServerScriptService |
Everything inside ReplicatedStorage |
ReplicatedStorage |
Everything inside StarterPlayerScripts |
StarterPlayer.StarterPlayerScripts |
- Install dependencies under
ReplicatedStorage.Packages(Wally or manual):
# wally.toml example
[dependencies]
Fusion = "elttob/fusion@0.3.0"
ProfileService = "firebird702/profileservice@1.1.0"
[server-dependencies]
Cmdr = "evaera/cmdr@1.12.0" # optional, for admin commands- Edit
ServerScriptService.PassportSystem.Settings, then initialise (see Quick Start). Delete the empty release Folder when done.
Tagged releases (v*) publish this .rbxm automatically via CI.
git clone https://github.com/metricrb/passport-system.git
cd passport-system
aftman install && wally install
rojo serve default.project.jsonBuild the same release artifact locally:
rojo build release.project.json --output passport-system.rbxm- Edit
src/Settings.lua— host nation, resolution mode, validity, tools, admin group, webhook. - Initialise on the server:
local PassportManager = require(game:GetService("ServerScriptService").PassportSystem)
PassportManager:init()- (Optional) Cmdr — register on the server, require
CmdrClienton the client (setup docs):
-- Server
local Cmdr = require(path.to.Cmdr) -- ServerPackages.Cmdr or Packages.Cmdr
Cmdr.Registry:RegisterDefaultCommands() -- optional
local CmdrSetup = require(game:GetService("ServerScriptService").PassportSystem.Cmdr.CmdrSetup)
CmdrSetup.registerCommands(Cmdr)-- Client (StarterPlayerScripts)
local CmdrClient = require(game:GetService("ReplicatedStorage"):WaitForChild("CmdrClient"))
CmdrClient:SetActivationKeys({ Enum.KeyCode.RightAlt })ExampleSetup + CmdrClientSetup do this for you when Cmdr is installed.
Nations are not listed in Settings.lua. Built-in defaults cover ~79 nations (group IDs + cover decals). Override policy or branding by PRing:
src/assets/{CODE}/NationConfig.lua and/or custom ui.lua
| Setting | Description |
|---|---|
hostNation |
Nation that owns this game server (issuing authority in Dual mode) |
nationResolution |
"GameNation" | "HighestGroupRank" | "Dual" |
defaultNation |
Legacy fallback (usually mirror hostNation) |
validityDays |
Expiry in days (0 = never) |
givePassportTool |
Give backpack Tools on issue |
autoStampOnJoin |
ENTRY stamp on join |
webhookUrl |
Discord webhook ("" = off) |
adminGroup |
Cmdr permission { groupId, minRank } (groupId = 0 → creator only) |
travelBans / nationSuspensions / unlpEligibleUserIds |
UN-level policy |
- GameNation — everyone gets
hostNation - HighestGroupRank — highest rank among registered nation groups
- Dual (default) — host passport if citizen + highest non-host nation + UN if eligible
Contributors PR a folder under src/assets/{CODE}/:
| File | Required? | Purpose |
|---|---|---|
NationConfig.lua |
No | Visa/border/bans overrides (defaults apply if omitted) |
ui.lua |
No | Custom passport UI (DEFAULT used if omitted) |
settings.lua |
No | Cover/stamp asset IDs |
Full guide: docs/ADDING_A_NATION.md
local PM = require(game:GetService("ServerScriptService").PassportSystem)
PM:init()
PM.Data.getPassport(userId)
PM.NationResolver.resolve(userId)
PM.NationConfigLoader:get("USA")
PM.PassportIssuer.issue(player) -- primary passport
PM.PassportIssuer.issueMultiple(player) -- all eligible passports
PM.BorderService.stamp(passport, "ENTRY", nation, location?)
PM.VisaService.grantVisa(passport, nation, visaType, expiryDays)
PM.VisaService.revokeVisa(passport, nationCode)
PM.PassportValidator.validate(passport)
PM.BorderStation.bindPrompt(prompt, { nation, stampType, location })Open with Right Alt. Passport commands (Group Passport, admin-gated via BeforeRun):
| Command | Alias | Example |
|---|---|---|
issue |
passport-issue |
issue @Player |
view |
passport-view |
view @Player |
stamp |
passport-stamp |
stamp @Player ENTRY Customs |
ban |
passport-ban |
ban @Player reason |
unban |
passport-unban |
unban @Player |
renew |
passport-renew |
renew @Player |
Definitions are in src/Cmdr/Commands/; *Server modules stay on the server. See Cmdr setup.
| Doc | Topic |
|---|---|
| Architecture | Data flow, modules, authority |
| Adding a nation | PR format, NationConfig, UI fallback |
| Border stamping | ProximityPrompt stations |
| assets/README | Asset folder notes |
aftman install
wally install
rojo serve default.project.json
selene src/
rojo build default.project.json --output passport-system.rbxmStack: Luau (--!strict), Rojo, Wally, Selene, Fusion 0.3, ProfileService, Cmdr.
- Use
--!strict - Keep PRs focused (one nation or one feature)
- Test in Studio before opening a PR
- Prefer GitHub issue templates under
.github/ISSUE_TEMPLATE/
MIT