SANDBOX_SAFE_OBJECT_ACTIONS
The ten action names a mod may pass to
api.objectAction(objectId, action). It is declared as
ModObjectAction in the mod scripting declarations and enforced at three points, independently, against one
list:
- In the sandbox frame —
SAFE_OBJECT_ACTIONSinapps/web/src/mods/sandbox/modSandbox.htmlthrowsUnsupported object action from sandbox: <action>synchronously for anything else. - On the host, when the message arrives —
SANDBOX_SAFE_OBJECT_ACTIONSinapps/web/src/mods/SandboxedModRunner.ts, checked byisSandboxSafeObjectAction. A message posted straight at the host without going through the frame is rejected here and reported as a mod diagnostic. - On the host, at dispatch — the runner's
objectActionimplementation (apps/web/src/ui/App.tsx) checks the sameisSandboxSafeObjectActionbefore building the intent, and throwsUnsupported script object action: <action>otherwise. The innermost gate is deliberately no laxer than the outer ones.
The object-action capability is not this gate. The capability decides whether you may call the method
at all; this list decides what you may ask for, and it is re-checked host-side whatever the manifest says.
The values#
| Value | What the host does | Kinds it is meaningful for | Also available to a table script |
|---|---|---|---|
flip |
Toggles faceDown and rotates 180° about local X. On a deck it also flips every card entry. |
card, deck. On any other kind the rotation and the flag both change anyway. |
yes, handle.flip() |
rotate |
Rotates 90° about local Y. | every kind. | yes, handle.rotate() |
lock |
Marks the entity locked and switches its rigidbody to static. | every kind. | yes, handle.lock() |
unlock |
Clears the lock and restores the authored body type. | every kind. | yes, handle.unlock() |
shuffle |
Shuffles a deck's contents. Refused outright on anything that is not a deck, even from a host-side mod. |
deck. |
yes, handle.shuffle() |
draw |
Draws the first item off the container. Guards itself: does nothing on any other kind. | deck, bag. |
yes, handle.draw() |
deal |
Deals from the container to the seated players. Guards itself. | deck, bag. |
yes, handle.deal() |
split |
Splits a stacked deck in two. Guards itself: does nothing unless the deck holds more than one card. | deck. |
no — in the union, but ObjectHandle has no method |
combine |
Merges the entity into a stack. | card, deck. |
no — same reason |
roll |
Throws a die with a randomized impulse and lets physics settle it. Does not guard itself. | die. On a card it throws the card. |
yes, handle.roll() |
What a mod cannot ask for, and why#
Thirteen of the engine's 23 actions are unreachable from a mod under any capability:
| Withheld | Reason |
|---|---|
delete |
A mod is untrusted code fetched from a repository. One that can destroy entities can quietly dismantle a table it did not create. |
tap, untap |
No participant path sends them either — the per-kind gate refuses both for every kind — so a mod is not being singled out. |
lift, flick |
Drag mechanics: the physical result of a pointer gesture, meaningless without the gesture. |
press |
A button's own click. It is raised by a player pressing the button, not requested — no surface, table script or mod, can synthesize one. |
reveal-all, reveal-team-a, reveal-team-b |
Hidden-information reveals. An action that can flip who sees a card is an action that can be used to cheat, so the host owns them outright. |
peek, search, search-pull, search-close |
Host-authoritative private reveals of a pile, to one player. A mod that could open one would manufacture a reveal for itself; it hears about them instead, through public, identity-free table events. |
By design. A mod and a table script get different budgets because they carry different trust: a table script is authored in the editor by whoever built the scene, while a mod is code from a GitHub repository gated by a declared capability list. Neither list is expected to widen. Build a mod around
api.createObjectand the ten actions it does have; a game that genuinely needs to remove pieces belongs in a table script.
split and combine are more reachable from a mod than from a table script, because a mod passes the
action name as a string and the typed table-script API has no method for either.
See also#
api.objectAction— the method, its coercions and its throw.- Action vocabularies — the 19/13/10 comparison in full.
- TableObjectAction (engine) — all 19.
- ObjectAction (table script) — the table-script list of 13.
- Mod capabilities — what
object-actiongrants, and what it does not.
