Deno 2.5 names permission policies and records their use
Deno 2.5 stores named permission sets in project configuration and adds JSONL permission audit logs. Explicit selection keeps a stored policy separate from granting it, while traces help explain the capabilities exercised during a run.
A command-line permission list is a runtime policy, but it is easy to treat it as incidental invocation text. The list may differ between a test command, an application run, and compilation. Once those commands appear in several places, the policy becomes harder to compare and review.
Deno 2.5, released September 10, adds named permission sets in deno.json. It also adds a permission audit log and optional stack traces for permission requests. Those mechanisms describe different aspects of the same execution boundary, the authority a command selects and the operations observed while it runs.
This is relevant to TypeScript applications because the language's type checker does not determine which files a program may read or which processes it may start. Those are runtime capabilities. Deno makes more of that policy available as structured project configuration.
A name refers to a concrete capability set
The release shows named policies containing granular permissions. A data-processing task can refer to a set with access limited to a particular directory.
{
"permissions": {
"process-data": {
"read": ["./data"],
"write": ["./data"]
}
},
"tasks": {
"process": "deno run -P=process-data main.ts"
}
}
The command selects the policy through --permission-set=process-data or its short form, -P=process-data. The name identifies the configured values. It does not replace them with an informal description whose meaning every caller must reconstruct.
The configuration schema in the 2.5.0 source describes permission values as booleans, lists, or explicit allow-and-deny objects. It includes capabilities such as reading, writing, network access, environment access, and subprocess execution.
That representation allows a policy to state the scope of a capability. Reading one directory is a different grant from allowing all reads. Naming the set makes it reusable without requiring the command line to repeat every element.
I value the TypeScript ecosystem for the applications it makes possible, but runtime policy remains an architectural decision beyond the types. A named set makes that decision easier to inspect with the code that depends on it. The useful part is the concrete values and their explicit selection.
Storing a policy is separate from selecting it
Deno 2.5 supports a default set selected by -P without a name. The announcement also describes permissions associated with test, bench, or compile configuration.
An important part of that design is the opt-in step. The release demonstrates that finding test permissions in configuration does not silently grant them when deno test runs. The command reports that permissions were found and asks whether the caller intended to use -P.
That preserves a distinction between reading a project's configuration and authorizing the configured capabilities. A project can declare its expected test policy, while the invocation still makes the selection visible.
The distinction is especially useful when commands are copied between local development and automation. A short command can refer to a named, reviewable policy without hiding that a permission grant is taking place. The selected name becomes part of the command's contract.
It also avoids assigning authority based only on a label. A set called test can still contain a broad grant. Review has to examine the values, just as it would examine explicit flags. Naming improves organization without making the name proof of least privilege.
Audit records describe observed permission activity
The release adds DENO_AUDIT_PERMISSIONS, whose value is the path to a JSONL audit file. Each record includes a format version, timestamp, permission category, and associated value. The announcement demonstrates records for environment access and file operations.
A log entry can therefore associate a read request with a particular path or environment access with a variable name. That is more useful for understanding capability use than a single statement that the program ran with all permissions.
The audit log can also include stack traces when DENO_TRACE_PERMISSIONS=1 is enabled. The trace helps connect a request to the code path that caused it, which matters when a dependency performs the operation indirectly.
These observations can inform a more specific configured policy. A developer examining a file read can determine whether the path belongs in the intended task's capability set. A stack trace can help distinguish application behavior from initialization inside an imported package.
An observed run still has limited coverage. If a branch never executes, its permission requirements do not appear in that run's evidence. A log of successful activity is therefore not a complete declaration of every capability the program might need under different inputs.
Observation does not replace enforcement
The difference between an audit and a policy is consequential. Logging an operation explains what happened or what was requested. A permission set determines which capabilities the invocation selects. Treating an audit file as enforcement would confuse those responsibilities.
The release's example runs with broad permissions while collecting observations. That is useful for illustrating the records, but it should not be mistaken for a restricted production policy. The example shows collection, not a universal deployment recommendation.
My preference is to keep the declared policy and observed behavior available together. A reviewer can compare what the program is allowed to do with the capability requests a particular workload produces. Disagreement then has a concrete meaning, either the policy is broader than demonstrated need or the workload has not exercised every intended path.
The broader release improves framework compatibility
Deno 2.5 also expands TypeScript configuration compatibility, including rootDirs and bundler module resolution. The announcement connects those additions to checking common framework templates. That work concerns the static model of the application, while permission sets concern its runtime authority.
The separation is useful because successful checking does not imply that a program has the capabilities needed to execute. Conversely, granting a capability does not establish that a value satisfies a TypeScript interface. A runtime serving this ecosystem needs clear answers at both layers.
Named permission sets and audit logs provide two inspectable artifacts around execution. One states a selected capability policy, and the other records permission activity during a particular run. Keeping the selection explicit lets project configuration describe expected authority without silently granting everything it describes.