Every .melker file declares what it needs. The engine enforces it.
This page explains how permissions work, how they're sandboxed, and how you stay in control.
A JSON block that says exactly what the app can do.
Every .melker file contains a <policy> block at the top.
It declares the app's name, description, and the permissions it requires.
The engine reads this block before any code runs.
<policy> { "name": "RSS Reader", "description": "Read news from multiple feeds", "permissions": { "net": ["news.ycombinator.com", "feeds.bbci.co.uk"], "browser": true } } </policy>
This RSS reader can fetch from two specific hosts and open links in your browser. It cannot read files, run commands, or contact any other server. If it tries, the request fails immediately.
The policy is a contract between the app developer and you. You can read it before running the app, just like you'd inspect a web page's permissions. The key difference from traditional terminal programs: everything not declared is denied.
<policy> tag get
a minimal auto-policy: read access to the current directory and clipboard support.
No network, no writes, no subprocesses. Remote files (URLs) must always include a policy.
Reference: .melker file format
Runtime-level sandboxing, not application-level checks.
When you run melker app.melker, three things happen in sequence:
1. CLI parses the command, loads the file 2. Launcher reads the policy, shows the approval prompt 3. Runner executes the app in a sandboxed subprocess
The launcher converts the policy into Deno's --allow-* permission flags
and spawns the runner as a restricted subprocess. This is not a JavaScript-level
sandbox that can be bypassed with clever code. Deno enforces permissions at the
runtime level. If the app tries something it wasn't granted, the call
throws a PermissionDenied error.
deno run \ --allow-read=/tmp/...,/home/user/project \ --allow-net=earthquake.usgs.gov \ --no-prompt \ melker-runner.ts app.melker
The --no-prompt flag is critical. It means the sandboxed app cannot
interactively ask for new permissions. Violations fail immediately instead of
presenting a "do you want to allow?" dialog.
The experimental melker-node binary uses Node's --permission
model, which is less granular than Deno's. The same policy syntax works, but enforcement
differs in several ways:
| Capability | Deno | Node.js |
|---|---|---|
| Network filtering | Per-host (--allow-net=host) | Binary on/off (--allow-net) |
| Subprocess filtering | Per-command (--allow-run=cmd) | Binary on/off (--allow-child-process) |
| Environment variables | Per-variable (--allow-env=KEY) | Not restricted |
| System info | Per-interface (--allow-sys=hostname) | Not restricted |
| Deny overrides | --deny-* flags | Not available |
| Filesystem | Exact paths | Directory globs (path/*) |
"net": ["api.example.com"], the sandbox allows all network
access, not just that host. Similarly, "run": ["ffmpeg"] allows
any subprocess. The policy still documents intent, but Node cannot
enforce it at the same granularity. Deno remains the recommended runtime
for stronger sandboxing.
Reference: Policy architecture
Array permissions for granular control, boolean shortcuts for common patterns.
These accept a list of allowed values. Each maps directly to a Deno --allow-* flag.
| Permission | Controls | Example |
|---|---|---|
net | Network access by hostname | ["api.example.com"] |
read | Filesystem read paths | [".", "/data"] |
write | Filesystem write paths | ["./output"] |
run | Subprocess commands | ["ffmpeg", "git"] |
env | Environment variables | ["API_KEY"] |
ffi | Native library paths | ["/lib/native.so"] |
sys | System info interfaces | ["hostname", "uid"] |
Use ["*"] as a wildcard to allow all values for a given permission.
Common permission patterns have shortcuts that expand into the right combination
of net and run permissions. The expansion is smart: it checks
which commands actually exist on your system.
| Shortcut | What it grants |
|---|---|
map | Network access to OpenStreetMap, CartoDB, and other tile servers |
shader | Per-pixel shader callbacks on canvas elements (runtime-only, no Deno flag) |
clipboard | run access to clipboard commands (xclip, pbcopy, etc.) |
browser | run access to open / xdg-open for opening URLs |
ai | run + net for AI assistant tools (audio, API access) |
keyring | run access to OS keychain commands |
all | Everything (equivalent to --allow-all) |
| Value | Where | Meaning |
|---|---|---|
"*" | Any array | Wildcard: allow all |
"cwd" | read, write | Expands to the current working directory at runtime |
"samesite" | net | Expands to the host the app was loaded from (remote apps) |
{
"permissions": {
"net": ["api.example.com", "samesite"],
"read": ["cwd"],
"write": ["./output"],
"run": ["ffmpeg"],
"env": ["API_KEY"],
"clipboard": true,
"browser": true
}
}
Some permissions are granted automatically so apps don't need to declare boilerplate.
Every app gets a baseline set of permissions regardless of its policy. These cover the minimum needed for the engine itself to work.
The app can always read its own file, the system temp directory (for bundled code), the Deno module cache, and the Melker state directory. Local apps also get read access to the current working directory.
The system temp directory (for bundler output), the Melker state directory (for persisting app state across runs), and the log directory.
Terminal detection variables (TERM, COLORTERM,
KITTY_WINDOW_ID, etc.), path variables (HOME,
PATH, TMPDIR), and all MELKER_*
and XDG_* variables.
"write": ["cwd"] explicitly.
This prevents apps from silently modifying your files.
Reference: Environment permission analysis
You approve an app once. Re-approval is only needed when permissions change.
The first time you run a .melker file, Melker shows a prompt
listing the requested permissions. You can approve or decline.
Approvals are cached so you won't be asked again.
Local files: Approval is based on a hash of the policy JSON.
You can edit the app's code freely without triggering a new prompt.
Only changing the <policy> block requires re-approval.
Remote files (URLs): Approval is based on a hash of the entire file content plus the policy. Any change to a remote app triggers re-approval. This prevents a remote server from silently changing behavior.
# Show the policy without running the app melker --show-policy app.melker # Revoke approval for a specific app melker --revoke-approval app.melker # Clear all cached approvals melker --clear-approvals # Skip the prompt (CI, scripts, trusted sources) melker --trust app.melker
--trust means: It skips the approval prompt
but still runs the app with its declared policy permissions. If the app has a
<policy> tag, those permissions are applied normally. If there
is no declared policy, the default auto-policy is used (read access to the
current directory and clipboard).
Reference: CLI reference
Grant or revoke permissions at the command line without editing the app.
You can modify an app's effective permissions when you run it,
without touching the .melker file.
# Give an offline app network access for testing melker --allow-net=api.example.com app.melker # Enable the AI assistant melker --allow-ai app.melker # Enable shaders on an app that forgot to declare it melker --allow-shader app.melker
# Run an app but block clipboard access melker --deny-clipboard app.melker # Block a specific host while keeping the rest melker --deny-net=tracking.example.com app.melker
The precedence is: deny > allow > policy.
A --deny flag always wins, even if the policy or a --allow
flag grants the same permission.
From zero permissions to full system access, and everything in between.
{
"name": "Mandelbrot",
"permissions": {}
}
Pure computation. No network, no files, no subprocesses. The app runs entirely within the sandbox with only implicit permissions.
{
"name": "earthquake-dashboard",
"permissions": {
"net": ["earthquake.usgs.gov", "raw.githubusercontent.com"],
"map": true
}
}
Fetches earthquake data from USGS and tectonic plate boundaries from GitHub.
map: true adds the tile server hosts needed for the interactive map.
{
"name": "Color Selector",
"permissions": {
"ai": ["*"],
"clipboard": true
}
}
Uses the AI assistant to suggest color palettes and copies selected colors
to the clipboard. ai expands into the specific run
and net permissions needed for AI tools.
{
"name": "htop",
"permissions": {
"all": true
},
"comment": "Linux reads /proc, macOS uses sysctl/ps. Needs full access."
}
A system monitor needs to read /proc, run system commands,
and access environment variables. The comment field appears
in the approval prompt so the user understands why full access is requested.
See more examples: How Melker Works