Deno’s Permission Model
What it is: Deno’s “secure by default” permission system. Code has zero capabilities until granted via CLI flags or runtime prompts. Granular, deny-list-aware, well-documented. Status: Mature, the canonical example of capability-based runtime security in JS/TS world. Most relevant to Locara: This is the closest existing model to what Locara needs at the framework level. Steal liberally.
Background
Deno (Ryan Dahl, 2018) was a deliberate redesign of Node.js with security as a primary axis. The big idea: Node’s “any package can read all your files” default was a supply-chain catastrophe waiting to happen (and which has happened many times). Deno flipped the default — programs have no capabilities until explicitly granted.
Key design decisions
- Capabilities granted via CLI flags.
deno run --allow-read=foo.txt --allow-net=api.example.com script.ts. - Granular path/host filters.
--allow-read=path1,path2— only these paths.--allow-net=*.example.com— only these hosts.--allow-env=AWS_*— only these env vars (wildcards allowed).
- Deny lists override allow lists.
--deny-net=internal.local --allow-net→ can do everything except internal.local. - Runtime prompts when a capability is needed and not granted — Y/n, with options to deny once / allow once / always allow / always deny.
--no-promptfor headless / CI use.--allow-all/-A= full capabilities (sandbox effectively disabled).- Categories of permissions:
--allow-read,--allow-write(filesystem)--allow-net(network)--allow-env(env vars)--allow-run(subprocess, can restrict to specific binaries)--allow-sys(system info: OS version, memory, etc.)--allow-ffi(foreign function interface — risky)
- Permissions API for in-program permission queries / requests at runtime.
- Unstable APIs require explicit
--unstable-*flags. - Workers inherit a subset of parent permissions.
What worked
- The default is genuinely safe. A vanilla
deno run script.tsliterally cannot read files or hit network. This is what “secure by default” should mean. - Granular allowlists — path-level read access is enforced at the runtime, not just docs.
- Runtime prompts are user-friendly. Y/n with “always” options strikes a good balance between security and UX.
- Deny lists are crucial for “let it do most things but absolutely not this.” Apple/Chrome don’t have this; it’s a real feature.
- Documentation is excellent — the permission model is one of the first things in the docs, treated as central.
- Adoption in serverless / edge runtimes (Deno Deploy) shows the model works in production.
What failed / criticisms
--allow-allis a footgun. Many users default to it because permission errors are annoying. Defeats the purpose.- Granularity is coarse for some use cases. “Allow read on this entire directory tree” might be too permissive for a sensitive app; sub-tree granularity would help.
- Permissions are file-system-path-based, but symlinks, mounts, and
..traversal create edge cases. - No capability for “open file picker, then access user-selected file” like macOS Powerbox. Apps either have to broaden read permission or shell out to the OS picker awkwardly.
- No way to scope by intent. “Allow network access for image downloads” vs “allow network for telemetry” — both are just
--allow-net=*. - Adoption hasn’t displaced Node despite the security advantages. Status quo wins.
Specific learnings for Locara
- Default-deny is the right architecture. Locara apps start with zero capabilities. Manifest declarations are the only way to gain them. Deno proves this is workable.
- Granular path/host scoping is essential. Locara should support:
fs.read: ["~/Documents/receipts/**"]— glob-pattern pathsfs.user-selected: true— only file-picker-granted pathsnet: falseornet.allowed_hosts: ["api.example.com"]device.camera: truedevice.microphone: truetool.exec: ["wasm"]or["wasm", "subprocess"]— capability scoping for tool runtime
- Deny lists matter. Locara should support both allow and deny, with deny taking precedence. Useful for apps that want broad capabilities except clearly excluded resources.
- Runtime prompts for ambiguous cases. macOS TCC handles camera/mic prompts. Locara handles capability-changes on update via prompts. Don’t avoid prompts; design them well.
- Avoid the
--allow-allfootgun. Locara should not have a “permit everything” mode. Apps that genuinely need everything (low-level system tools) are out of scope for v1. - First-class user-selected files. macOS App Sandbox / Powerbox is the right pattern here; Deno doesn’t have an equivalent and it’s a gap. Locara should have a
fs.user-selectedcapability that gates file-picker-mediated access cleanly. - Document the model deeply. Deno’s docs are a model. Locara’s capability docs should be treated as a primary product surface, not an afterthought.
- Runtime permission queries are useful for apps that adapt to available capabilities. Worth supporting via SDK API.
References
- https://docs.deno.com/runtime/fundamentals/security/
- https://deno.com/blog/v1.36 (permission improvements)
- “Deno permissions tutorial” articles
- Comparison with Node: https://deno.land/manual/runtime/permission_apis