Grants and the sandbox¶
A profile's Lua scripts run in a sandbox: they can do the things a voice
command needs, and nothing more. A grant is a capability you add in your own
engine.toml to let one profile reach past that boundary. This page describes
what the sandbox allows by default and what each grant opens up.
The sandbox by default¶
When a profile's script runs, it has the full amri.* API for input, speech,
sound, variables, profile and category control, sandboxed file reads, and the
ordinary parts of Lua. The risky parts of the Lua standard library are removed,
so a downloaded profile cannot run arbitrary programs or read arbitrary files.
What a sandboxed script keeps:
| Available | Notes |
|---|---|
string.*, math.*, table.* |
The full libraries. |
os.date, os.time |
Current date and time only. |
tonumber, tostring, type, pairs, ipairs, pcall, xpcall |
Basic globals. |
amri.* |
Input, speech, sound, variables, profiles, tasks, sandboxed file reads. |
amri.file.read, amri.file.exists |
A relative path always resolves inside the profile folder, with no grant. |
What is removed, regardless of grants:
os.execute os.exit os.getenv os.remove os.rename os.tmpname
io.* load loadfile dofile require package
debug collectgarbage
Because these functions are never installed in the sandboxed state, the sandbox is enforced by construction. There is no per-call permission check to turn off, and no way for a script to reach a removed function.
Grants widen it¶
A capability that reaches past the sandbox is gated behind a grant. Each grant is
a boolean you set per profile. Without the grant, run, shell, and kill are
not available to a script at all, and a profile that calls them without the grant
is refused at load. The file and input grants are quieter: an absolute-path file
read returns nothing, and an escape-class key is dropped, when the grant is not
held.
The per-profile grants, and the functions each one unlocks:
| Grant | What it allows | Unlocks |
|---|---|---|
run |
Run a program directly, no shell | amri.sys.run, amri.sys.exec_async |
shell |
Run a shell command string | amri.sys.shell |
kill |
Send a signal to a process | amri.sys.kill |
file_absolute |
Read files by absolute path, outside the profile folder | amri.file.read, amri.file.exists with an absolute path |
unrestricted_input |
Emit escape-class keys held back by default: bare Super, Alt+Tab, Ctrl+Alt+Delete, Ctrl+Alt+Backspace, Ctrl+Alt+Fn, and control characters in amri.input.text |
amri.input.* |
Two more capabilities are user opt-ins that live in engine.toml, but are not
per-profile grant flags:
read_keyboardlets Amri read the keyboard and mouse globally, which backsamri.sys.key_pressedandamri.sys.mouse_button. It is one switch under[input], not a per-profile setting, so an imported profile cannot turn on keyboard reading by itself.permit_system_overrides_forlets a named profile replace a built-inamricommand. It lives under[security]. See System overrides.
Where grants live¶
Grants live only in engine.toml, the file you control. They are never read
from a profile. A profile can request a capability, but only you grant it by
editing this file. This is the same consent location that holds your profile
trust decisions and the system-override allowlist.
Per-profile grants go under [grants."<profile>"], keyed on the profile name:
# engine.toml, the file you control.
# Grant one profile the ability to launch a program and read an absolute path.
[grants."elite_dangerous"]
run = true
file_absolute = true
# Read the keyboard and mouse globally (a single switch, all profiles).
[input]
read_keyboard = true
# A global kill-switch: force a capability off for EVERY profile, overriding
# any per-profile grant above. Any flag set here wins.
[deny]
shell = true
# Let a named profile override a built-in `amri` command.
[security]
permit_system_overrides_for = ["my_personal_overlay"]
Grants and deny are top-level tables
[grants."<profile>"] and [deny] are their own top-level tables, not
entries under [security]. Only permit_system_overrides_for lives under
[security], and read_keyboard lives under [input].
Deny overrides grant
A flag set under [deny] forces that capability off for every profile,
whatever a [grants."<profile>"] table says. Use it to switch a whole
capability off across the machine without editing each profile's grants.