Open-Source Wikis

/

Deno

/

Systems

/

Permissions

denoland/deno

Permissions

Active contributors: Bartek Iwańczuk, David Sherret, Nayeem Rahman

Purpose

Deno's defining feature is that user code runs sandboxed by default. The deno_permissions crate (runtime/permissions/) implements that sandbox: it defines the permission categories, parses CLI --allow-*/--deny-* flags, gates every privileged op, and prompts the user when permission is needed at runtime.

runtime/permissions/lib.rs is the largest single file in the repo at 10,921 lines, and the model it implements has been remarkably stable since v1.0.

Directory layout

runtime/permissions/
├── Cargo.toml                  # deno_permissions crate
├── lib.rs                      # 10,921 lines: PermissionsContainer + every category
├── broker.rs                   # external permission broker (IPC) support
├── ipc_pipe.rs                 # IPC pipe used by broker.rs
├── prompter.rs                 # interactive prompt rendering
├── runtime_descriptor_parser.rs # parses --allow-*/--deny-* descriptor strings
└── which.rs                    # PATH search abstraction (used by run perms)

Permission categories

Each category has an enum (UnaryPermission<T> for the parametrized ones), a check_* method on PermissionsContainer, and corresponding --allow-*/--deny-* CLI flags.

Category What it gates Examples
read Filesystem reads Deno.readFile, import "file:..." resolution
write Filesystem writes Deno.writeFile, Deno.mkdir
net Network access Deno.connect, fetch, Deno.listen
env Environment variable read/write Deno.env.get/set
sys System info Deno.osRelease, Deno.systemMemoryInfo, hostname
run Subprocess execution Deno.Command, child_process.spawn
ffi Foreign function interface Deno.dlopen
import Remote imports import "https://..."
import_signing Trust gate for signed imports imports with integrity hashes

Each category supports list semantics: --allow-net=api.example.com,1.2.3.4, --deny-read=/etc, etc. The descriptor parser is in runtime_descriptor_parser.rs.

Key abstractions

Type File Role
PermissionsContainer runtime/permissions/lib.rs The shared, mutable handle threaded through the runtime
Permissions runtime/permissions/lib.rs The inner state (per-category permission sets)
PermissionState runtime/permissions/lib.rs Granted / GrantedPartial / Prompt / Denied
PermissionDeniedError runtime/permissions/lib.rs JsError of class NotCapable raised when a check fails
RuntimePermissionDescriptorParser runtime/permissions/runtime_descriptor_parser.rs Parses descriptors at runtime (for Deno.permissions.request)
PromptResponse runtime/permissions/prompter.rs Allow / Deny / AllowAll
permission_prompt runtime/permissions/prompter.rs Renders the interactive (y/n/A/d) prompt
BrokerResponse runtime/permissions/broker.rs IPC-driven permission decisions
AUDIT_SINK runtime/permissions/lib.rs Static sink that logs permission grants/denies (file or OTel)

How it works

graph TD
    CliFlags["--allow-net=foo<br/>--deny-write<br/>…"] --> Parse["RuntimePermissionDescriptorParser"]
    Parse --> Container["PermissionsContainer<br/>(constructed in cli/factory.rs)"]
    Container --> OpState["op_state.put(container)"]
    OpState -->|every op| Op["op_*"]
    Op -->|"check_read(&path, 'api')"| Container
    Container -->|"granted"| Allow["proceed"]
    Container -->|"prompt"| Prompter["permission_prompt"]
    Prompter -->|"y"| Allow
    Prompter -->|"n"| Deny["PermissionDeniedError"]
    Container -->|"denied"| Deny
    Container --> Broker["external broker?"]
    Broker -->|"yes"| BrokerSock["IPC pipe (broker.rs)"]
    BrokerSock --> Allow
    Container --> Audit["AUDIT_SINK"]
    Audit --> File["log file or OTel"]

Check call sites

Every privileged op in ext/* follows the same pattern:

state
    .borrow_mut::<PermissionsContainer>()
    .check_read(&path, "Deno.openSync()")?;

The string is the API name shown to the user in the prompt and in the resulting error. Search for check_read, check_write, check_net, check_env, check_sys, check_run, check_ffi, check_import to find every gate point — there are several hundred.

Prompt flow

When a check hits a permission in the Prompt state:

  1. lib.rs calls prompter::permission_prompt(name, api, is_unary, get_stack).
  2. prompter.rs renders a TTY prompt via deno_terminal, with optional stacktrace.
  3. The user responds y (allow once), Y (allow all for this category), n (deny once), N (deny all), or A (allow all categories).
  4. The result is recorded back into the container so subsequent calls don't re-prompt for the same descriptor.

If stdout/stderr aren't TTYs, prompts are auto-denied. The --no-prompt flag forces this behavior.

Broker mode

For sandboxed deployments, an external "permission broker" can be configured via env vars (see broker.rs). When a broker is configured, maybe_check_with_broker sends the permission request over a Unix-domain pipe to the broker process, which makes the decision. This is what Deno Deploy uses to centralize permission policy across many runtime instances.

Audit sink

Permission grants and denies can be logged to either a file or an OpenTelemetry stream via AUDIT_SINK. This is opt-in (off by default) and is used by enterprise deployments to track permission usage.

Integration points

  • CLI flag entry: cli/args/flags.rs (the --allow-*/--deny-* flags) → cli/args/flags_net.rs for network flag parsing.
  • Container construction: cli/factory.rs builds the PermissionsContainer from parsed flags.
  • Worker insertion: runtime/worker.rs::MainWorker::bootstrap_from_options and runtime/web_worker.rs::WebWorker::bootstrap_from_options insert the container into op state.
  • Op call sites: every ext/<name>/*.rs op that does anything privileged calls check_*.
  • JS entry: Deno.permissions.query/request/revoke are implemented by ops in runtime/ops/permissions.rs (or wherever the runtime ops module re-exports them).

Entry points for modification

  • New permission category: add an enum variant to Permissions, add a check_<name> method on PermissionsContainer, add --allow-<name>/--deny-<name> flags, add JS-side Deno.permissions.query("<name>") support, document the category.
  • New descriptor type for an existing category (e.g., a more granular net descriptor): extend runtime_descriptor_parser.rs and the matching UnaryPermission impl in lib.rs.
  • Change the prompt UI: edit prompter.rs. Note that automated tests rely on the existing prompt format — update spec tests accordingly.
  • Add an audit log field: extend the OtelAuditFn signature and the file-sink writer in lib.rs.

Key source files

File Purpose
runtime/permissions/lib.rs The whole permission model: categories, PermissionsContainer, check_* methods, PermissionDeniedError
runtime/permissions/prompter.rs Interactive prompt rendering, PERMISSION_EMOJI, permission_prompt
runtime/permissions/broker.rs External permission broker over IPC (used by Deno Deploy)
runtime/permissions/ipc_pipe.rs The IPC pipe primitive used by broker.rs
runtime/permissions/runtime_descriptor_parser.rs Parses descriptor strings like read=/foo,/bar
runtime/permissions/which.rs PATH search for --allow-run
cli/args/flags.rs The --allow-* / --deny-* flag definitions
cli/args/flags_net.rs Net-permission flag parsing (CIDR ranges, port lists)
cli/factory.rs Constructs the PermissionsContainer for a worker

For the broader runtime context see Runtime. For how this fits into request flow, see Architecture.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Permissions – Deno wiki | Factory