Script Permissions¶
Starkite features a rule-based permission engine that restricts execution capabilities at runtime. The engine intercepts privileged operations—such as filesystem access, network connections, and command execution—and validates them against a permission profile.
For kernel-level isolation (such as filesystem invisibility and process containment), see Sandbox.
Default profile¶
By default, scripts execute under the deny-all profile. In this mode, the script can run pure computations and call basic utilities like load(), print(), and log(). Any attempt to access files, the network, environment variables, or external processes is blocked.
Built-in permission profiles¶
Starkite includes five built-in profiles, selectable via the --permissions flag or its boolean aliases:
| Profile | Flag Alias | Key Grants |
|---|---|---|
deny-all |
--deny-all |
Compute-only (strings, json, yaml, time), plus load(), print(), log() |
allow-fs |
--allow-fs |
File reads, file writes within $CWD, environment variables, SQLite |
allow-net |
--allow-net |
HTTP clients, SSH connections, remote SQL databases |
allow-local |
--allow-local |
Local command execution (within $CWD), HTTP servers, Kubernetes, MCP, AI |
allow-all |
--allow-all |
Unrestricted execution (exec anywhere, system process controls) |
Example:
Custom permission profiles¶
Define custom profiles in ~/.starkite/config.yaml under the permissions section. Each profile lists explicit allow and deny rules:
# ~/.starkite/config.yaml
permissions:
ci: { allow: ["k8s.read"] }
deploy:
allow:
- fs.read($CWD/**)
- fs.write($CWD/build/**)
- os.exec($CWD/**)
- http.client
deny:
- http.client(*.internal.*)
At runtime, select the profile by name:
Implicit default profile¶
To define a default profile that Starkite applies when no --permissions flag is provided, name the profile default:
When running scripts without specifying a permissions flag, the runtime automatically applies this default profile:
Profile composition¶
Custom profiles can inherit from built-in profiles using base. Appended rules extend base capabilities, and deny rules always take precedence:
# ~/.starkite/config.yaml
permissions:
deploy:
base: allow-fs
allow: ["k8s.write", "os.exec($CWD/**)"]
deny: ["fs.delete"]
almost-everything:
base: allow-all
deny: ["os.exec"] # everything except process execution
You can assign a built-in profile name directly as a string value. This is a shorthand for inheriting the built-in profile verbatim, without adding any rules:
# ~/.starkite/config.yaml
permissions:
default: allow-fs # Implicit default is allow-fs
everything: allow-all # Selectable via --permissions=everything
[!NOTE] The
basefield accepts built-in profile names only. Profiles cannot inherit from other custom profiles.
Rule grammar¶
Starkite permission rules use a structured format:
module.category[([function_list:]resource)]
- module: Target module name (e.g.,
fs,os). - category: Target category name (e.g.,
read,exec). - function_list: Optional comma-separated list of specific function names.
- resource: Optional path, host pattern, or glob.
Common rule patterns¶
| Pattern | Matches |
|---|---|
*.* |
Any module, category, and operation |
fs.* |
Any operation in the fs module |
fs.read |
Any read operation in the fs module on any path |
fs.read(/etc/**) |
Any read operation in the fs module on paths matching /etc/** |
fs.read(read_file:*) |
Only the read_file function on any path |
fs.read(read_file,read_bytes:/etc/**) |
read_file or read_bytes on paths matching /etc/** |
os.exec($CWD/**) |
Execution of any binary located under $CWD |
Evaluation is deny-first. Any operation not explicitly matched by an allow rule (or blocked by a deny rule) is denied.
Path expansion¶
Rule paths support $CWD and $HOME environment variables, expanding them to the current working directory and user home directory respectively. Standard globbing (*, **) applies:
allow:
- fs.read($CWD/**) # Read any file under the project directory
- fs.read($HOME/.config/myapp/*) # Read files under the specified user path
Without these variables, paths are matched verbatim.
Checked modules and categories¶
The permission engine only checks modules that access host resources. Pure-compute modules (such as strings, math, or json) bypass the permission engine.
| Module | Categories | Grated Operations Checked |
|---|---|---|
fs |
read, write, delete |
Path reads, writes, and deletions |
os |
exec, env, process |
Process execution, env reads/writes, directory/process controls |
http |
client, server |
Outbound HTTP requests, listening HTTP servers |
ssh |
connect, transfer |
Remote SSH execution, file uploads/downloads (SCP) |
k8s |
read, write, exec, config |
Cluster reads/writes, kubectl exec, kubeconfig loading |
sql |
open |
Database connections (scoped by driver, e.g., sqlite, postgres) |
ai |
generate |
LLM calls (model name checked as a resource) |
mcp |
client, server |
MCP connections and hosting |
io |
prompt |
Terminal user prompts |
Loaded modules¶
When you import a module using load(), it runs under the exact same permissions as the calling script. Loading external code does not bypass or expand the script's permission boundary. If an imported module attempts any operation not permitted by the calling script's profile, the runtime blocks the action and raises a permission error.
kite ./deploy.star --allow-fs
# deploy.star reads/writes local files → allowed
# the loaded module calls k8s.read(...) → denied (k8s is allow-local)
To resolve this, rerun the script with the required capabilities: