Reading Config Files in Deno: A Practical Guide to the Permission Model
Learn how Deno’s permission system lets you safely read a config file, request runtime permissions, and balance security with developer friction. A step‑by‑step example and trade‑offs are included.
02 Oct 2026, 19:14 UTC

Why Permissions Matter in Deno
Deno was built with security in mind. Unlike Node.js, it does not grant file system, network, or environment access by default. Every operation that touches the outside world must be explicitly allowed. This “least‑privilege” approach protects your code from accidental leaks and malicious imports.
The Permission Flag Syntax
When you launch a script, you can pre‑grant permissions with flags such as --allow-read or --allow-write. Flags can be scoped to a specific path or glob pattern, which keeps the attack surface small.
# Read only the config file in the current directory
$ deno run --allow-read=./config.json read_config.ts
Running the same script without the flag will throw a PermissionDenied error at the point of access.
Runtime Permission Requests
Sometimes you don’t want to hard‑code all permissions at launch. Deno’s Deno.permissions.request lets you prompt the user for access at runtime. This is handy for interactive tools or when you want to defer permission until the operation is actually needed.
async function readConfig() {
const perm = await Deno.permissions.request({
name: "read",
path: "./config.json",
});
if (perm.state !== "granted") {
console.error("Read permission denied – aborting.");
return;
}
const data = await Deno.readTextFile("./config.json");
console.log(JSON.parse(data));
}
readConfig();
In a headless CI environment, prompts will block execution, so you should use flags or pre‑grant permissions in scripts that run automatically.
Inspecting Permissions Programmatically
Modules that run in a sandbox or need to adapt to the current permission state can use the Permissions API. The Deno.permissions.query method returns the current state for a requested permission. This is useful for feature flags or graceful degradation.
async function checkReadPermission() {
const status = await Deno.permissions.query({
name: "read",
path: "./config.json",
});
console.log(`Read permission is ${status.state}`);
}
checkReadPermission();
Other API methods – grant and revoke – let you change the permission state at runtime, though revoking a permission that is already in use will fail.
Putting It All Together: A Real‑World Example
- Create a config file
touch config.json cat > config.json <<'EOF' { "port": 8080, "debug": true } EOF- Create the script
import { serve } from "https://deno.land/std@0.220.0/http/server.ts"; async function startServer() { const perm = await Deno.permissions.request({ name: "read", path: "./config.json", }); if (perm.state !== "granted") { console.error("Cannot read config – exiting."); Deno.exit(1); } const configText = await Deno.readTextFile("./config.json"); const config = JSON.parse(configText); console.log(`Starting server on port ${config.port}`); serve((_req) => new Response("Hello"), { port: config.port }); } startServer();- Run with minimal permissions
# No read flag – will prompt $ deno run --allow-net server.ts- Run with pre‑granted read permission
# Grant read to config.json only $ deno run --allow-read=./config.json --allow-net server.ts
In a CI pipeline, you would use the second command to avoid a blocking prompt. The script will fail immediately if the permission is missing, making the error obvious.
Trade‑Offs and Limitations
- Developer friction: Adding flags to every script can become tedious, especially in a large monorepo. Wrapper scripts or a
deno.jsonconfig file can centralize permissions. - Granularity vs. simplicity: Specifying a path (e.g.,
--allow-read=./config.json) is safer than a blanket--allow-read, but it requires you to anticipate every file a module might touch. - Runtime prompts: Useful for interactive tools, but they block automated runs. Use
--allow-readin CI or setDENO_ALLOW_READ=./config.jsonas an environment variable. - Permission revocation: Once granted, permissions persist for the process lifetime. Revoking a permission that is already in use will throw an error, so design your code to request permissions only when needed.
Practical Checklist
- Start scripts with the tightest possible flags.
- Use
Deno.permissions.requestonly for truly interactive scenarios. - Test the script without flags to confirm that missing permissions surface as errors.
- In CI, use a wrapper script or
deno.jsonto set required permissions automatically. - Document the required permissions in README or module documentation so contributors know what is needed.
Conclusion
Deno’s permission model forces you to think about security at every step. By declaring minimal flags, requesting permissions only when necessary, and inspecting permission state programmatically, you keep your code both safe and maintainable. The example above shows how a simple server can read its configuration file securely while remaining flexible for interactive use or automated deployment.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.