Deno — 默认拒绝权限的 TypeScript/JavaScript 运行时
已复核Deno 2.9.5 是一个默认把危险能力收起来的 JS/TS/Wasm 运行时。日常类比:厨房刀具默认锁在抽屉里,你要写明「这把刀只能切这根胡萝卜」。
固定 tag v2.9.5 的进程入口是 cli/main.rs → deno::main()。默认引擎 feature 是 V8(deno_v8 facade);Cargo 里另有 quickjs feature,但不是默认构建。工具链钉在 rust-toolchain.toml 的 1.95.0。
最小 HTTP 服务不再走 https://deno.land/std@0.224.0/http/server.ts。固定源码的一等入口是内置 Deno.serve:
Deno.serve((_req: Request) => { return new Response("Hello, world!");});未写选项时,ext/http/00_serve.ts 把 hostname 设成 "0.0.0.0"、port 设成 8000。
不读固定 2.9.5,旧笔记会漏掉三道已经写进 CLI 的边界:
- 权限不只是
--allow-net/--allow-read,还有--allow-import、--allow-sys、--allow-ffi和一整组--deny-* deno serve会暗示--allow-net=host:port,和deno run的默认拒绝不是同一条路- Deno 2 认真做 npm / JSR /
node_modules,不能再把「只靠 URL 导入」当成唯一模块合同
它和 bun 的对照正好相反:Bun 默认让脚本能做事;Deno 默认先问。
架构与权限流程
Section titled “架构与权限流程”从命令行到用户 handler,固定源码可以拆成五步:
-
解析旗标:
cli/args/flags.rs得到Flags+DenoSubcommand。run、serve、task、install、add、test、lint、fmt、compile都在cli/lib.rs的run_subcommand里分发。 -
构造权限:
Permissions有八个 unary 域——read/write/net/env/sys/run/ffi/import。PermissionState默认是Prompt(不是 Granted)。--allow-all/-A一次放开;--no-prompt(或DENO_NO_PROMPT)把未授权变成直接抛错。 -
启动 worker:
runtime/worker.rs用PermissionsContainer+JsRuntime装扩展。runtime/lib.rs再导出deno_fetch、deno_http、deno_kv、deno_node、deno_ffi等。 -
接请求:
Deno.serve或deno serve进入ext/http。DENO_SERVE_ADDRESS可以改成 TCP / unix / vsock / tunnel;自动压缩只在DENO_SERVE_AUTOMATIC_COMPRESSION为真时打开,缺省关闭。 -
解析依赖:URL、
jsr:、npm:、以及 workspace 的NodeModulesDirMode。deno add express无前缀时会补npm:。lockfile 由deno_resolver::lockfile管理。
案例 1:内置 Deno.serve,不要再粘旧 std URL
Section titled “案例 1:内置 Deno.serve,不要再粘旧 std URL”Deno.serve({ hostname: "127.0.0.1", port: 8080 }, (req) => { const url = new URL(req.url); return new Response(`path=${url.pathname}`);});deno run --allow-net=127.0.0.1:8080 server.tshandler 可以是第一参数,也可以放进 options.handler。只写函数、不写 options 时,监听 0.0.0.0:8000。若设置了 DENO_SERVE_ADDRESS,第一次 Deno.serve 会吃掉这次覆盖。
案例 2:文件权限按路径收,不要 -A
Section titled “案例 2:文件权限按路径收,不要 -A”const text = await Deno.readTextFile("./data.csv");console.log(text.slice(0, 120));deno run --allow-read=./data.csv script.ts读是 --allow-read/-R,写是 --allow-write/-W,可以带路径列表。本地调试用 -A 等于关掉默认拒绝;生产要把能力写进命令或 deno.json 的 --permission-set/-P。
案例 3:deno serve 与 deno run 的权限差
Section titled “案例 3:deno serve 与 deno run 的权限差”deno serve --port=8000 server.tsdeno task startserve_parse 默认 port=8000、host=0.0.0.0,并注释「implies --allow-net=host:port」。另有 --parallel、--open、--tunnel。不要假设 deno serve 还要你手写一遍同样的 --allow-net,也不要把这个暗示套到普通 deno run 上。
--allow-import/-I 管远程模块下载,帮助文本给出的默认主机是 deno.land:443、jsr.io:443、esm.sh:443、raw.esm.sh:443、cdn.jsdelivr.net:443、raw.githubusercontent.com:443、gist.githubusercontent.com:443。访问其他 registry 要显式加名单。
-
继续
import { serve } from "https://deno.land/std@0.224.0/http/server.ts":2.9.5 的一等 API 是Deno.serve。旧 std 路径会把读者锁在过期模块图上。 -
把
-A带进生产:默认状态是 Prompt。-A与--allow-all一次授予全部八域,包括ffi和run。 -
以为
deno serve和deno run权限相同:serve会按 host:port 暗示网络许可;run不会。 -
忽略
--allow-import:能读本地文件不等于能拉远程模块。默认 import 允许名单不含任意 GitHub raw 以外的私有源。 -
把
deno bundle当稳定产品:run_subcommand对Bundle打印 experimental 警告。打包合同随时会变。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 脚本、内部服务、边缘函数:需要把读文件、出网、起子进程写进命令行
- TypeScript 直接跑、同时要 lockfile / JSR / npm 的新项目
- 想用 Web 标准
Request/Response,并接受 Deno 的权限提示
不适用:
- 深度绑定 Node native addon、或必须完全按 Node 默认「什么都能做」
- 团队不允许改导入策略、只能走内网 npm registry,又不愿维护
--allow-import - 需要实验性
deno bundle当唯一生产打包器 - 把招聘市场和现有 Node 运维生态当作硬约束时,node-js 仍是更低风险的默认
固定版本边界
Section titled “固定版本边界”- 本文绑定
denoland/deno@17fadf33a8df3af9488b9f42efd1f2290d6dc7a3,tag 与cli/Cargo.toml均为2.9.5。 - 默认构建是 V8;
quickjs只是 Cargo feature,不能当成发行版引擎。 deno bundle在该 revision 标为 experimental。- 本文未安装 Deno、未跑权限提示、未启动 HTTP、未测冷启动,状态保持
UNVERIFIED。
- 默认拒绝是状态机,不是口号——八个 unary 权限,默认
Prompt,deny 与 allow 成对出现。 Deno.serve和deno serve是两层——JS API 默认0.0.0.0:8000;CLI 还会暗示--allow-net。- 远程导入是单独许可——
--allow-import的默认主机名单比「能上网」窄得多。 - Deno 2 的模块故事是 JSR + npm + lockfile——不能再只用 2020 年的 URL 导入解释它。
- 不传 options 时,
Deno.serve(handler)默认监听哪个地址和端口? PermissionState的默认值是 Granted 还是 Prompt?deno serve --port=8000 server.ts是否还要再写一遍--allow-net=0.0.0.0:8000才能监听?
检查点:
0.0.0.0:8000(可被DENO_SERVE_ADDRESS覆盖)。Prompt。未授权时先问;--no-prompt则直接拒绝。- 不必按
run那样手写。serve_parse暗示--allow-net=host:port。普通deno run没有这条暗示。
- 文档:docs.deno.com
- 固定源码:denoland/deno —— 本文绑定
17fadf33a8df3af9488b9f42efd1f2290d6dc7a3 - 权限说明:docs.deno.com/go/permissions
- bun —— 单二进制、默认放开能力的对照
- node-js —— 权限模型与模块解析的历史基线
- bun —— 同赛道运行时;默认能力与引擎选择相反
- node-js —— npm /
node:兼容要对照的平台 - hono —— 可在 Deno 上跑的多运行时框架
- javascript —— 运行时语义仍来自 JS 标准
- typescript —— 类型检查与「能跑
.ts」不是同一件事
- boa-engine —— Boa — Rust 写的 ECMAScript 解释器
- engine262 —— engine262 — 用 JavaScript 实现的 ECMA-262 参考引擎
- tauri —— Tauri — 用系统浏览器内核 + Rust 做轻量桌面应用
- wasmer —— Wasmer — 把 wasm 当成轻量容器到处跑