Locara

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-prompt for 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.ts literally 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-all is 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

  1. 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.
  2. Granular path/host scoping is essential. Locara should support:
    • fs.read: ["~/Documents/receipts/**"] — glob-pattern paths
    • fs.user-selected: true — only file-picker-granted paths
    • net: false or net.allowed_hosts: ["api.example.com"]
    • device.camera: true
    • device.microphone: true
    • tool.exec: ["wasm"] or ["wasm", "subprocess"] — capability scoping for tool runtime
  3. 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.
  4. 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.
  5. Avoid the --allow-all footgun. Locara should not have a “permit everything” mode. Apps that genuinely need everything (low-level system tools) are out of scope for v1.
  6. 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-selected capability that gates file-picker-mediated access cleanly.
  7. 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.
  8. Runtime permission queries are useful for apps that adapt to available capabilities. Worth supporting via SDK API.

References