Skip to content

Transport

Internal

The module is not re-exported through require(Operator) and is not part of the public API. Start builds one and puts the instance on the handle as handle.transport, which is how you reach it. Prefer handle.refresh and handle.refreshAll over calling it directly. The internal path is ServerStorage.Operator.Server.Transport.

Delivers a private console bundle to each authorised player, and carries dispatches back.

Nothing sits in ReplicatedStorage and nothing is replicated to everyone. See Delivery for what a player actually receives.

Functions

Transport.new

luau
Transport.new(options: Options): Transport
luau
local transport = Transport.new({
	packageRoot = ServerStorage.Operator,
	registry = registry,
	dispatcher = dispatcher,
	canRun = function(player, command)
		return roles:canRun(player, command)
	end,
	roles = roles,
})

transport:start()

Methods

:start

luau
transport:start(): ()

Begins delivering. Existing players are evaluated immediately and joiners as they arrive.

:stop

luau
transport:stop(): ()

Tears everything down: every bundle removed, every remote destroyed, every connection dropped.

:refresh

luau
transport:refresh(player: Player): ()

Re-runs canRun, rebuilds that player's manifest and sends it.

  • lost access entirely → the bundle is removed and the remote destroyed
  • just gained access → the bundle is delivered
  • already had it → only the manifest is resent

The bundle is never delivered twice, including across respawn, repeated refreshes, and regaining access.

With roles passed to new, this invalidates that player's cached roles first, so one call re-resolves, rebuilds and sends.

:refreshAll

luau
transport:refreshAll(): ()

:isDelivered

luau
transport:isDelivered(player: Player): boolean

Whether that player currently holds a bundle.

Types

Options

luau
type Options = {
	packageRoot: Instance,
	registry: any,
	dispatcher: any,
	canRun: Predicate,
	consumerTypes: Instance?,
	consumerMount: Instance?,
	roles: any?,
	clientSettings: any?,
	serializer: Serializer?,
	logger: Log.Logger?,
	actionLog: ActionLog?,
	canViewLogs: ((player: Player) -> boolean)?,
	clientCommands: Instance?,
	audit: Audit?,
}
FieldMeaning
packageRootthe Operator instance to clone Shared and Client from
registrythe Registry the manifest is built from
dispatcherthe Dispatcher a dispatch is run through
canRun(player, command) -> boolean, asked once per command per player
consumerTypesyour argument type modules, delivered to authorised clients
consumerMountyour own interface module, for Client.Mount
rolesa Roles so refresh can invalidate its cache
clientSettingsthe Client config table sent with the bundle
serializerserialize / deserialize for remote payloads
loggera Logger
actionLogan ActionLog the log panel reads from
canViewLogs(player) -> boolean, gating log reads; without it, nobody can read logs
clientCommandsyour client command modules, cloned into the bundle
auditan Audit that client-reported records are written into

canRun 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".

Predicate

luau
type Predicate = (player: Player, command: Command) -> boolean

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 is rate limited.

Rate limits

Three independent token buckets per player, so one kind of traffic can never starve another:

TrafficBurstRefillExceeding it
Command dispatches102 / secondRateLimited result, player logged
Log queries41 per 4 secondsRateLimited page
Client command reports122 / secondreport dropped; the command still ran

Log queries are deliberately the tightest, because one query can read the whole buffer. Client command reports get their own bucket rather than sharing the dispatch one, so heavy client command use cannot exhaust a moderator's ability to run real commands.

The manifest is display, not permission

A command the player is not allowed to run comes back as NotFound, not Denied, so probing the remote with guessed names cannot enumerate commands the player was never shown. Permission is re-checked on every dispatch independently of what was delivered.

Released under the MIT Licence.