Skip to content

pack.toml reference

This page is the canonical reference for pack.toml. For a guided tour, see the Quickstart.

KeyTypeRequiredNotes
manifestintyesManifest schema version. Currently 1.
idstringyesReverse-DNS Pack ID, e.g. tv.mobrule.hello-world.
namestringyesHuman-readable Pack name.
versionstringyesSemVer-style version.
mppstringyesMPP version requirement (SemVer range).
authorsarraynoList of author names / handles.
homepagestringnoURL shown as the Pack’s homepage link.
descriptionstringnoShort human description of the Pack.
licensestringnoSPDX license identifier, e.g. "MIT".

The Pack’s manifest_hash is a deterministic SHA-256 over the canonicalised Manifest, formatted as sha256:<64-hex>. Equal content in, equal hash out, across Linux and Windows. Cosmetic edits (whitespace, comments, key reordering) do not change the hash; semantic edits (adding or renaming Events, changing a schema body, changing version) do. mobrule pack print-hash <pack-dir> prints exactly sha256:<64-hex> on stdout; add --json to get a single-line {pack_id, version, manifest_hash} envelope instead.

Tells the bridge how to launch the Adapter process.

[adapter]
command = "python3"
args = ["-u", "adapter.py"]
restart = "on_crash"
KeyTypeRequiredNotes
commandstringyesExecutable to spawn. May use ${paths.<key>} (see [[adapter.path]] below).
argsarray of stringnoArguments, in order. May use ${paths.<key>}.
envtable of stringnoExtra environment variables for the Adapter process. Values may use ${paths.<key>}. Don’t put secrets here: manifest values are hashed and visible.
working_dirstringnoWorking directory for the process. Defaults to the pack directory, so a relative command (a script beside pack.toml) resolves.
restartstringno"on_crash" (default) or "never". Use "never" when the Adapter supervises another process itself (say, it hosts the game server) and an automatic restart would collide with it.

Optional. Packs that drive an external program (typically your game) declare the machine-local paths they need here, e.g. the game executable and its data file. Each declaration gets a key; the bridge substitutes it into command, args, and env as ${paths.<key>} when it launches the Adapter.

[adapter]
command = "python3"
args = ["-u", "adapter.py"]
[adapter.env]
GAME_SERVER = "${paths.game}"
GAME_DATA = "${paths.data}"
[[adapter.path]]
key = "game"
label = "Game executable"
extensions = ["exe"]
default = "game/game.exe"
[[adapter.path]]
key = "data"
label = "Game data file"
extensions = ["wad"]
KeyTypeRequiredNotes
keystringyesIdentifier ([a-z0-9_]). Substituted as ${paths.<key>}. Must be unique within the [adapter].
labelstringyesHuman label shown in the operator’s Launcher panel file picker.
extensionsarray of stringnoFile-dialog filter hints (extensions without the dot), e.g. ["exe"].
defaultstringnoA path relative to the pack directory, used when the operator hasn’t set this key on their machine. Lets a bundle ship the game binary/assets beside the pack and run with zero setup.

The operator sets these paths once per machine in the Launcher panel; those values are stored per-machine and are not part of the pack. When a key has no machine value, the bridge falls back to default resolved against the pack directory. A machine value, when set, always wins over default. default must be relative: machine-specific absolute paths belong in the operator’s per-machine config, not the shipped pack, so an absolute default is rejected when the pack loads.

Every Pack must declare at least one queue. Within a queue, the bridge dispatches Invocations strictly one at a time; different queues are independent, so an Adapter can work N queues concurrently.

[queues.default]
ready_after = "applied"
KeyTypeRequiredNotes
ready_afterstringno"applied" (default) or "done": when the queue is free to dispatch the next Invocation. See Dispatch semantics.

Use ready_after = "done" for queues whose effects must fully complete in-game before the next Invocation begins.

Each Event the Pack exposes. Required sub-keys:

  • summary: short human description.
  • queue: the declared [queues.<name>] this Event dispatches through.
  • params_schema: a JSON Schema object describing the Invocation params.

Each property in params_schema.properties may be marked "x-mobrule-viewer-override" = true to let chat-time inputs override the default value at Cue-fire time.

Optional sub-keys:

KeyTypeNotes
titlestringHuman-friendly display name shown instead of the raw event name.
kindstring"composite" marks a broadcaster-configured weighted roll over member Events. See Composite Events.
membersarrayWhitelist of event names a composite Event’s faces may choose from. See Composite Events.
revealtableResolves a candidate option set from a live pack-state key at dispatch time. See Reveal.
viewer_inputtableSplits the viewer’s redemption text across several params. See Defining Events.

Optional. Declares a Pack-state key the Adapter (or platform) can write at runtime, e.g. a counter an overlay widget subscribes to.

[state.play_count]
writer = "adapter"
[state.play_count.params_schema]
type = "integer"
minimum = 0
KeyTypeRequiredNotes
writerstringyesWho may write the key: "adapter", "dispatcher", or "platform". "adapter" lets your Adapter send state_write frames for it; the other two let the dispatcher or platform update it.
persistboolnotrue to store the last value durably and replay it to the Adapter on reconnect. Default false.
params_schematablenoJSON Schema the written value must validate against.

Optional. Declares a named option dataset a param can source its choices from (via x-mobrule-source), instead of inlining the list. One of two backends: path (a pack-shipped file, served over /pack-assets/*) or options (an inline list expanded onto referencing params at load). Either way the choices are content-hashed into manifest_hash. See Option datasets.

[data.items]
path = "data/items.json"
[data.target_side]
options = [
{ value = 0, label = "Player", color = "#86efac" },
{ value = 1, label = "Opponent", color = "#ff7a7a" },
]
KeyTypeRequiredNotes
pathstringone of path/optionsPack-relative path to the JSON file (must stay inside the pack).
optionsarrayone of path/optionsInline choice list ({ value, label, color? }); expanded into x-mobrule-options on referencing params.

See Option datasets.

Optional. Broadcaster-configurable settings are declared as an array of [[pack.config.section]] tables, each holding its own fields. The Pack Config page covers the schema in full.

Optional. Declares your Pack’s stream overlay: where its built JS bundle lives (dir, with a fallback resolver if omitted) and which [state.<key>] entries it subscribes to (data.reads). See Overlays for the full contract.

Optional. Present ⇒ the Pack opts in to Chat-Plays: a chat-vote vocabulary (token → pad bit) plus a tally window and press duration. Absent ⇒ the Pack has no Chat-Plays at all. See Chat-Plays.

Optional. An array of { cue, event } pairs (plus an optional description) that materialise a Reward per entry when the Pack is installed, so a Pack works out of the box with zero Dashboard setup. See the Doom tutorial’s polish step for a worked example.

manifest = 1
id = "tv.mobrule.hello-world"
name = "Hello World"
version = "0.1.0"
mpp = "2"
authors = ["mobrule"]
[adapter]
command = "python3"
args = ["-u", "adapter.py"]
restart = "on_crash"
[queues.default]
ready_after = "applied"
[state.play_count]
writer = "adapter"
[state.play_count.params_schema]
type = "integer"
minimum = 0
[events.play_sound]
summary = "Log a play-sound notification."
queue = "default"
[events.play_sound.params_schema]
type = "object"
additionalProperties = false
required = ["sound_id"]
[events.play_sound.params_schema.properties.sound_id]
type = "string"
maxLength = 64
"x-mobrule-viewer-override" = true