Skip to content

Delivery

Nothing about Operator sits in ReplicatedStorage, and nothing is replicated to everyone. When a player is allowed to use the console, the server builds a private bundle for that player only.

Start does all of this for you. The call below is shown to make the shape concrete. Transport is internal and not re-exported through require(Operator).

luau
local Transport = require(ServerStorage.Operator.Server.Transport)

local transport = Transport.new({
	packageRoot = ServerStorage.Operator,
	registry = registry,
	dispatcher = dispatcher,
	canRun = function(player, command)
		return player.UserId == game.CreatorId
	end,
})

transport:start()

canRun is asked once per command per player. It decides both what goes in that player's manifest and whether a dispatch is allowed, so there is a single answer to "may this player use this command".

What a player actually receives

A ScreenGui named OperatorConsole is created in their PlayerGui, holding:

  • a clone of Shared/ and Client/, never Server/, so no server command handler ever reaches a client
  • their client command modules, if you passed ClientCommands. These are the one carve-out, and their bodies do reach the client because that is where they run
  • their consumer type modules, if you passed consumerTypes
  • a RemoteEvent created at runtime, inside that same container

The container is ResetOnSpawn = false and Enabled = false. Scripts inside it still run even though it is disabled. Why it is a ScreenGui rather than a Folder is recorded in DECISIONS.md.

A player who is allowed nothing receives nothing at all

No modules, no remote, no container, and no way to tell the package exists.

The container is fully populated before it is parented, so the client never observes a half-built bundle, and the boot script still uses a bounded WaitForChild with a handled nil path.

Keeping delivery in step with permissions

luau
transport:refresh(player)  -- re-evaluate one player
transport:refreshAll()     -- re-evaluate everyone
transport:stop()           -- tear everything down

refresh re-runs canRun, rebuilds the manifest and sends it. If the player has lost access entirely the bundle is removed, the remote destroyed and every connection dropped. If they have just gained access the bundle is delivered. If they already had it, only the manifest is resent. The bundle is never delivered twice, including across respawn, repeated refreshes, and regaining access.

When a player loses access the console is unmounted on their client first and the container removed after, so revoking a role takes effect within a couple of seconds and needs no restart. Nothing on the server stays pinned waiting for a client that never answers.

What the server enforces

The client parse is a convenience and is never trusted. A dispatch carries raw text only; the server re-tokenizes, re-binds, re-transforms and re-runs every guard from scratch, and re-resolves player arguments at execution time so a target who left is caught.

Every remote message is checked for shape and type, and each player has a token-bucket rate limit (10 dispatches of burst, refilling at 2/second). Exceeding it returns a RateLimited result and logs the player: at most a few lines per player per minute, then a summary carrying the suppressed count.

NotFound, not Denied

A command the player is not allowed to run comes back as NotFound, so probing the remote with guessed names cannot be used to enumerate commands the player was never shown.

The manifest is what the client may display, never what the server will accept. Permission is re-checked on every dispatch independently of what was delivered.

Released under the MIT Licence.