Skip to content

Configuration

Everything Operator.Start accepts. Every field is optional. Operator.Start({}) is valid and gives you a console for the place owner and nobody else.

luau
local admin = script.Parent

Operator.Start({
	Commands = admin.Commands,
	ClientCommands = admin.ClientCommands,
	Types = admin.Types,
	Guards = admin.Guards,

	DefaultCommands = { "debug", "moderation" },

	Roles = {
		owner = { UserIds = { 1234567 }, Inherits = { "moderator" } },
		moderator = { GroupId = 7654321, MinRank = 200 },
	},

	Audit = { Enabled = true },
	Logs = { Enabled = true },
	Client = { UI = true, ActivationKey = Enum.KeyCode.F2, Theme = "signal" },
})
FieldDefaultMeaning
Commandsnonean Instance whose descendant ModuleScripts are your commands
ClientCommandsnonean Instance holding your client commands
Typesnoneyour argument types; also delivered to authorised clients
Guardsnonemodules returning a guard function, or { guard, path }
DefaultCommandsnonewhich built-in packs to enable
Rolesplace owner onlyrole definitions
Auditenabledsee Audit
Logsenabledsee Logs
ClientUI on, F2see Client
Loggerwarnings to outputsee Logging
Serializerpass-throughsee Serialization

Audit

Controls the audit log.

FieldDefaultMeaning
Enabledtrueset false and no records are kept
BufferSize200how many records the in-memory ring buffer holds
DiscordWebhooknonea webhook URL to attach the Discord sink to
DiscordSend"all"which records reach Discord; see below
DiscordFooterIconthe Operator markthe embed footer icon URL

DiscordSend takes "all", "failures", or a (record) -> boolean of your own:

luau
Audit = {
	DiscordWebhook = webhookUrl,
	DiscordSend = "failures",
}

On a busy game one embed per command is a channel nobody reads. "failures" forwards only records where ok is false.

The filter applies only to the webhook. The ring buffer, the log panel and audit:query still see everything, so this changes what you are notified about, never what is recorded.

The webhook URL is configuration, not code

Pass it in from somewhere that is not a committed script: Roblox's secrets store, or a server-only module you do not commit.

Logs

Controls the log panel.

FieldDefaultMeaning
Enabledtrueset false and the panel reports that logging is off
BufferSize200how many moderation actions are kept
Permissionnonea role required to read logs; by default, anyone who can run logs

Client

Travels with the delivered bundle.

FieldDefaultMeaning
UItruewhether the built-in console is delivered
Mountnonea ModuleScript of yours to mount instead; see Custom Interfaces
ActivationKeyEnum.KeyCode.F2the toggle key
Theme"signal"signal, operator, iris, or a table of token overrides
HistoryLimit500console lines kept; 0 means uncapped
Iconthe >_ glyphthe topbar icon image, or false for none

Setting both Mount and UI = true is an error rather than a silent choice between them.

What is bounded

Everything that accumulates is capped. Three of the caps are yours to set; the rest are fixed.

WhatSet withDocumented in
Console history, and command historyClient.HistoryLimitClient
Audit records in memoryAudit.BufferSizeAudit
Moderation actions in memoryLogs.BufferSizeLogs
Dispatch, log query and report rate limitsfixedTransport
Suggestion cache entriesfixedSuggestionCache
Log lines per key, per windowfixedLog
In-flight dispatchesfixedcleared on result or timeout

Validation

Config is checked before anything is built, and a mistake names the offending key:

Operator.Start: "Commmands" is not a config option; expected one of Audit, Client,
ClientCommands, Commands, DefaultCommands, Guards, Logger, Logs, Roles, Serializer, Types

Operator.Start: "nope" is not a default command pack; expected one of debug, fun, moderation

A wrong-typed value, an unknown sub-key, or a bad Client.Theme all fail the same way. You never get a silent nil. A theme table is checked token by token:

Operator.Start: Client.Theme is invalid: "highlite" is not a theme token; expected one of
background, surface, text, subtext, highlight, success, error, severityBan, severityKick,
severityWipe, severityWarn, severityCustom

Operator.Start: Client.Theme is invalid: theme token "highlight" must be a Color3, got string

The fix is always a typo in your own config table. The message names the key it did not recognise and lists the ones it accepts.

Start is idempotent. Calling it twice logs a warning and returns the same handle. It never double-registers or re-delivers a bundle.

The handle

Start returns a handle, so one-call setup does not lock functionality away:

luau
local operator = Operator.Start({ DefaultCommands = { "debug" } })

operator.registry:registerCommand(myCommand)
operator.refreshAll()
MemberUse
registryregister commands or types after startup
dispatcherrun commands from your own code, add guards
rolesquery or invalidate a player's roles
auditquery the log, add sinks
logsthe log panel's store
refresh(player) / refreshAll()re-evaluate access after a role change
stop()tear everything down

Commands registered after Start work immediately; the next refresh puts them in open consoles.

Logging

Operator is silent unless you opt in, so it never prints into someone else's output. With no Logger configured, warnings and errors go to Studio output prefixed with [Operator] and info is dropped.

luau
Operator.Start({
	Logger = function(level, message)
		MyLogger[level](message)
	end,
})

Logger accepts a function, or a table with info, warn and error. See api/log for the throttling behaviour, which matters if your sink forwards to a webhook.

Serialization

Remote payloads pass through a serializer. The default is an identity pass-through, since Roblox already serializes remote arguments.

luau
Operator.Start({
	Serializer = {
		serialize = function(value) return MySera.encode(value) end,
		deserialize = function(value) return MySera.decode(value) end,
	},
})

Supply one if you are slotting Operator into a framework that has its own wire format.

Released under the MIT Licence.