# Deno Administrator & Developer Guide

This guide is the operator-and-developer reference for the Deno runtime: how it is built, how the source is organized, how to deploy and operate it, what its security model guarantees, and how to extend or contribute to it. Every fact is taken from the source tree at commit `7529c84` (Deno CLI version 2.7.12).

---

## 1. Executive Summary

Deno is a JavaScript / TypeScript / WebAssembly runtime distributed as a single self-contained binary. The codebase is a 70+ crate Rust workspace combining V8 (the JavaScript engine), Tokio (the async runtime), and a substantial set of in-house Rust crates that implement Web APIs, Node compatibility, the package management system, the language tooling, and the editor language server. The whole binary statically links V8.

Top-level facts:

| Item | Value |
|------|-------|
| License | MIT |
| Primary languages | Rust, TypeScript, JavaScript |
| Build system | Cargo workspace (resolver = "2") |
| Edition | Rust 2024 |
| JavaScript engine | V8 (`v8` crate, currently v147.x) |
| Async runtime | Tokio |
| TypeScript transpiler | `deno_ast` (SWC-based) + `deno_typescript_go_client` (TypeScript-Go for type-checking) |
| Distribution | Single-binary releases for 5 target triples; install scripts; brew/winget/scoop/chocolatey |
| Workspace member count | ~70 (cli, runtime, ext/*, libs/*, tests/*) |
| CLI subcommand count | 38 (see Subcommand Architecture below) |
| Permission categories | 9 (read, write, net, env, sys, run, ffi, hrtime, import + all/scripts) |
| Documented env vars | 26 (see Environment Variables in User Guide) |
| Web platform extensions (`ext/`) | 32 crates |
| Shared library crates (`libs/`) | 24 crates |

The Deno CLI binary (`cli/main.rs`) is just `deno::main()` — a thin wrapper that calls into the `deno` library crate (`cli/lib.rs`). Everything else is layered crates.

> [!NOTE]
> This guide focuses on the project as it is checked in. Deno Deploy (the hosted runtime) is a separate product and is treated here only as a destination for `deno deploy`.

---

## 2. Architecture

### 2.1 Layered Architecture

```
+----------------------------------------------------------------------+
|                            cli/ (binary)                             |
|  CLI parsing (clap), tools/* (run, fmt, lint, test, compile, ...),   |
|  factory wiring, LSP, type-checker driver                            |
+----------------------------------------------------------------------+
|                    runtime/ (deno_runtime crate)                     |
|  MainWorker, WebWorker, Deno.* namespace, permissions enforcement,   |
|  bootstrap JS, integrates ext/* into a JsRuntime                     |
+----------------------------------------------------------------------+
|     ext/ (32 crates: web/fetch/net/http/fs/crypto/node/webgpu/...)   |
|  Each crate contributes ops + JS/TS bindings for one Web API or      |
|  Node compatibility surface; loaded as deno_core::Extension          |
+----------------------------------------------------------------------+
|                    libs/core/ (deno_core crate)                      |
|  JsRuntime, ExtensionBuilder, op2 macro, ModuleLoader, snapshotting, |
|  the ops bridge between JS and Rust                                  |
+----------------------------------------------------------------------+
|                              v8 crate                                |
|  Rust bindings to the V8 JavaScript engine (statically linked)       |
+----------------------------------------------------------------------+
```

Below the engine, Tokio drives all async I/O. Above the CLI, no other layers exist — the binary IS the user interface.

### 2.2 Component Inventory

#### `cli/` — the user-facing binary

| Module | Purpose |
|--------|---------|
| `main.rs` | Entry point. Calls `deno::main()`. |
| `lib.rs` | Top-level orchestration: parse flags, build `CliFactory`, dispatch to subcommand tools. |
| `args/flags.rs` | Clap definitions for every subcommand and flag (~15 KLOC). The single source of truth for the CLI surface. |
| `args/mod.rs` | `CliOptions`, `CliLockfile`, env-var resolution, config file discovery. |
| `factory.rs` | `CliFactory` — lazy DI container that builds the resolver, module loader, npm installer, type checker, etc. on demand. |
| `tools/` | One module per subcommand: `run.rs`, `serve.rs`, `task.rs`, `repl.rs`, `compile/`, `bundle/`, `bench.rs`, `lint/`, `init.rs`, `installer.rs`, `jupyter.rs`, `pm.rs` (add/remove/install/outdated), `publish/`. |
| `lsp/` | The `deno lsp` language server (Tower-LSP). |
| `tsc/` | TypeScript compiler integration (TS-Go IPC + the legacy `deno_tsc` wrapper). |
| `module_loader.rs` | Implements `deno_core::ModuleLoader` for the CLI: handles JSR / npm / HTTPS / file URLs. |
| `npm.rs`, `node.rs`, `jsr.rs` | Per-scheme resolvers built atop the `libs/resolver` crate. |
| `cache/` | Disk caches (deps, type-check, emit, npm, codecov). |
| `worker.rs` | Constructs the `LibMainWorkerFactory` from `deno_lib`. |
| `standalone/` | Loader for `deno compile` binaries (reads the eszip appended to the executable). |
| `rt/` | `denort` runtime variant — minimal CLI used inside compiled binaries. |

#### `runtime/` — the embeddable runtime (`deno_runtime` crate)

| Item | Purpose |
|------|---------|
| `worker.rs` | `MainWorker` — the foreground JS worker. Wraps `JsRuntime`, sets up the `Deno` namespace, runs bootstrap JS. |
| `web_worker.rs` | `WebWorker` — implements the standard `Worker` Web API in a dedicated OS thread. |
| `worker_bootstrap.rs` | The Rust side of the bootstrap (passes runtime metadata to JS). |
| `js/` | Bootstrap JavaScript files (`99_main.js`, etc.) executed before user code to construct the `Deno` namespace. |
| `permissions.rs`, `permissions/` | `deno_permissions` integration: parsing flags, enforcing checks for ops. |
| `ops/` | Ops registered directly by the runtime (FS metadata, OS info hooks). |
| `cpu_profiler/` | V8 CPU profiler integration. |
| `subprocess_windows/` | Windows-specific subprocess infrastructure (`CreateProcessW` wrapper). |
| `transpile.rs` | Optional in-runtime TS→JS transpilation (HMR, REPL). |
| `snapshot.rs` | Build-time and load-time V8 snapshot machinery. |

The `runtime/README.md` notes that this crate is deliberately a *slimmed-down* version of the CLI: it omits TypeScript type-checking, the doc/lint/fmt tools, and CLI argument parsing — it provides only "JavaScript execution with Deno's operating system bindings (ops)." Third parties embed this crate to get a Deno-compatible `MainWorker` without dragging in the whole `cli/`.

#### `ext/` — Web platform and Node compatibility extensions (32 crates)

Each `ext/<name>` crate contributes ops + JS bindings for one Web API surface (or Node-compatibility surface). They are loaded as `deno_core::Extension` instances when the `JsRuntime` is built.

| Crate | Surface |
|-------|---------|
| `ext/web` | DOM-style globals: `URL`, `URLSearchParams`, `TextEncoder`, `TextDecoder`, `Blob`, `File`, `FileReader`, `EventTarget`, `Event`, `MessageEvent`, `AbortController`, `AbortSignal`, structured-clone, performance timeline, queueMicrotask. |
| `ext/console` | The `console` global. |
| `ext/url` | URL/URLSearchParams (separate so `ext/web` can compose). |
| `ext/webidl` | IDL conversion helpers used by other ext crates. |
| `ext/fetch` | `fetch`, `Request`, `Response`, `Headers`, `Body`. Backed by reqwest/hyper. |
| `ext/net` | `Deno.connect`, `Deno.listen`, TCP/UDP/Unix sockets. |
| `ext/tls` | TLS configuration shared between fetch and net. |
| `ext/http` | The Hyper-based HTTP server backing `Deno.serve`. |
| `ext/websocket` | WebSocket client and server. |
| `ext/webstorage` | `localStorage` / `sessionStorage` (SQLite-backed). |
| `ext/broadcast_channel` | `BroadcastChannel` Web API. |
| `ext/crypto` | Web Crypto API (`crypto.subtle`). |
| `ext/fs` | Filesystem ops (`Deno.readFile`, `Deno.writeFile`, etc.). |
| `ext/io` | Stream IO primitives. |
| `ext/cache` | Web `Cache` API. |
| `ext/cron` | `Deno.cron` (unstable). |
| `ext/kv` | `Deno.openKv` (unstable, SQLite-backed locally). |
| `ext/os` | OS info / `Deno.osRelease`, `Deno.hostname`, etc. |
| `ext/process` | `Deno.Command`, `Deno.spawn`. |
| `ext/signals` | Signal handling (`Deno.addSignalListener`). |
| `ext/ffi` | `Deno.dlopen` (foreign-function interface). |
| `ext/napi`, `ext/napi/sym` | N-API for native Node addons. |
| `ext/node` | Node compatibility — `node:fs`, `node:http`, `node:buffer`, etc. polyfills + ops. The largest single ext crate. |
| `ext/node_crypto` | Node `crypto` module (separated for size). |
| `ext/node_sqlite` | Node `sqlite` built-in. |
| `ext/image` | Image decode (powers `createImageBitmap`). |
| `ext/webgpu` | WebGPU. |
| `ext/telemetry` | OpenTelemetry tracing/metrics for the runtime. |
| `ext/bundle` | Bundling APIs (powers `deno bundle`). |
| `ext/rt_helper` | Internal helpers for `denort` (compiled-binary runtime). |

The naming convention is consistent: `ext/<name>/lib.rs` defines an `Extension` via `deno_core::Extension::builder()`; `ext/<name>/<name>.js` (or `.ts`) is the JS portion; ops are written in Rust with the `#[op2]` macro from `libs/ops`.

#### `libs/` — shared library crates (24 crates)

These are the crates that the CLI and the runtime depend on. Several are also published independently to crates.io (`deno_core`, `deno_ast`, `deno_lockfile`, etc.).

| Crate | Purpose |
|-------|---------|
| `libs/core` | `deno_core` — the JsRuntime, op infrastructure, snapshotting, the ModuleLoader trait. The foundation of everything. |
| `libs/core_testing`, `libs/core/examples`, `libs/core/io`, `libs/core/arena`, `libs/core/runtime`, `libs/core/modules` | `deno_core` sub-modules and helpers. |
| `libs/ops`, `libs/ops/op2`, `libs/ops/conversion`, `libs/ops/webidl`, `libs/ops/compile_test_runner` | The `op2!` proc-macro and op-definition machinery used by every `ext/` crate. |
| `libs/serde_v8`, `libs/serde_v8/magic`, `libs/serde_v8/utilities`, `libs/serde_v8/examples` | Zero-copy serialization between V8 values and Rust types. |
| `libs/resolver`, `libs/resolver/loader`, `libs/resolver/cache`, `libs/resolver/cjs`, `libs/resolver/npm` | Module resolution: deno.json discovery, import map application, JSR / npm / file resolution. |
| `libs/config`, `libs/config/deno_json`, `libs/config/workspace`, `libs/config/glob` | Parses and validates `deno.json[c]`, manages workspaces, resolves include/exclude globs. |
| `libs/lockfile` | `deno.lock` parsing, integrity checking, and rewriting. |
| `libs/cache_dir`, `libs/cache_dir/global`, `libs/cache_dir/file_fetcher` | The `$DENO_DIR` cache implementation (deps, npm, type-check, emit). |
| `libs/npm`, `libs/npm/resolution`, `libs/npm/examples` | npm package resolution and metadata. |
| `libs/npm_cache` | Disk caching of npm packages. |
| `libs/npm_installer` | Installs packages into `node_modules/` (when `--node-modules-dir=auto`). |
| `libs/npmrc` | `.npmrc` parser for registry/auth config. |
| `libs/node_resolver` | Implements Node's resolution algorithm (extensionless imports, `package.json` `exports`/`imports`). |
| `libs/node_shim` | Provides Node-compatible globals where necessary. |
| `libs/package_json` | `package.json` parsing. |
| `libs/dotenv` | `.env` file loader. |
| `libs/dcore`, `libs/dcore/src` | Lower-level deno helpers shared between cli and runtime. |
| `libs/eszip`, `libs/eszip/examples` | The eszip format used by `deno compile` and the bundler. |
| `libs/inspector_server` | Standalone V8 inspector server. |
| `libs/typescript_go_client`, `libs/typescript_go_client/src` | Rust client that talks to TypeScript-Go (the Go-based TS type checker bundled with the CLI). |
| `libs/crypto` | Crypto helpers shared by ext/crypto and ext/node_crypto. |
| `libs/maybe_sync` | Cross-thread synchronization helpers. |
| `libs/napi_sys`, `libs/napi_sys/src` | Low-level N-API definitions. |

#### `tests/` — integration test infrastructure

| Folder | Purpose |
|--------|---------|
| `tests/integration` | The Rust-side integration test runner used by `cargo test`. |
| `tests/specs` | The spec-based "files in directory = one test" suite (~5000+ specs). Each spec has a `__test__.jsonc` describing args, env, expected stdout/stderr/exit. |
| `tests/unit`, `tests/unit_node` | JS/TS unit tests run inside Deno itself. |
| `tests/wpt` | Web Platform Tests subset. |
| `tests/napi` | N-API addon test programs. |
| `tests/ffi`, `tests/sqlite_extension` | FFI and sqlite-extension test addons. |
| `tests/bench` | Microbenchmarks. |
| `tests/registry` | Local mock JSR/npm registry used by tests. |
| `tests/util/server`, `tests/util/lib` | Test HTTP servers and helpers. |
| `tests/node_compat` | Subset of upstream Node tests run for compatibility coverage. |

### 2.3 Request Lifecycle: `deno run main.ts`

```
$ deno run --allow-net main.ts
        |
        v
cli/main.rs::main()  --> deno::main() (cli/lib.rs)
        |
        v
flags_from_vec_with_initial_cwd(args)   parses clap, returns Flags struct
        |
        v
CliFactory::from_flags(flags)           lazy DI container
        |
        v
factory.create_main_worker_factory()    builds LibMainWorkerFactory
        |   - Resolves deno.json (if present)
        |   - Builds module loader (handles file://, https://, jsr:, npm:, node:)
        |   - Builds permission set from flags + deno.json
        |   - Builds NPM installer if needed
        |
        v
worker.create_main_worker(...)          MainWorker (deno_runtime)
        |   - Constructs JsRuntime (deno_core)
        |   - Loads all ext/* extensions (web, fetch, net, http, fs, crypto, node, ...)
        |   - Snapshot is loaded if available, otherwise extensions are bootstrapped fresh
        |   - Runs runtime/js/99_main.js to set up the Deno namespace
        |   - Applies bootstrap options (args, location, runtime version)
        |
        v
main_worker.execute_main_module(specifier)
        |   - Module loader fetches main.ts (from disk)
        |   - If TS/TSX, deno_ast transpiles to JS (cached in $DENO_DIR/gen)
        |   - JsRuntime instantiates and evaluates the module graph
        |   - Top-level await is awaited
        |
        v
main_worker.run_event_loop(false)       drives Tokio + V8 microtasks until idle
        |
        v
Process exits with main_worker.exit_code()
```

The same orchestration drives every executed-script subcommand (`run`, `serve`, `test`, `bench`, `eval`, `repl`, `task`). Each builds slightly different `WorkerOptions` and may register additional setup (test runner installs `Deno.test`, REPL installs the REPL state, etc.).

### 2.4 Subcommand Architecture

Every subcommand has:

1. A clap `Command::new(...)` builder in `cli/args/flags.rs` (lines 2219+, one `fn <cmd>_subcommand()` per subcommand).
2. A `<cmd>_parse(...)` function (also in `flags.rs`) that consumes the parsed `ArgMatches` and writes into `Flags`.
3. A handler in `cli/tools/<cmd>.rs` (or `cli/tools/<cmd>/mod.rs` for multi-file subcommands).

The 38 subcommands defined in the root command (`flags.rs:2140-2179`):

| Group | Subcommands |
|-------|-------------|
| Execution | `run`, `serve`, `task`, `repl`, `eval` |
| Dependency mgmt | `add`, `install`, `uninstall`, `outdated`, `approve-scripts`, `remove`, `update` |
| Tooling | `bench`, `bundle`, `cache`, `check`, `clean`, `compile`, `coverage`, `doc`, `fmt`, `info`, `init`, `create`, `jupyter`, `lint`, `test`, `publish`, `vendor` |
| Cloud / sandbox | `deploy`, `sandbox` |
| Lifecycle | `upgrade`, `lsp`, `types`, `completions` |
| Aliases / hidden | `x` (alias for `npm:`), `json_reference` (hidden, used for docs site generation) |

Aliases: When the CLI is invoked as `dx`, `denox`, or `dnx`, `flags_from_vec_with_initial_cwd` rewrites the args to insert `x` as the first argument (`flags.rs:1697-1711`). This makes `dx eslint .` equivalent to `deno x eslint .` (which itself is `deno run npm:eslint .`).

### 2.5 The op2 Bridge: How JS Calls Rust

Every Deno API that does I/O goes through an `op` — an asynchronous or synchronous function defined in Rust and exposed to JS through V8 fast-call bindings.

```rust
// ext/fs/lib.rs (illustrative)
#[op2(async)]
async fn op_read_file_async(
  #[state] state: &PermissionsContainer,
  #[string] path: String,
) -> Result<Vec<u8>, AnyError> {
  state.check_read(Path::new(&path), "Deno.readFile()")?;
  tokio::fs::read(path).await.map_err(Into::into)
}
```

The `#[op2]` macro generates V8 fast-call entry points and serde_v8 bindings. The op is registered with an `Extension`:

```rust
deno_core::extension!(
  deno_fs,
  deps = [deno_web, deno_io],
  ops = [op_read_file_async, op_write_file_async, ...],
  esm_entry_point = "ext:deno_fs/30_fs.js",
  esm = ["30_fs.js"],
  options = { fs: Arc<dyn FileSystem> },
);
```

When the CLI builds a `JsRuntime`, it passes a list of these extensions. At snapshot time, the JS portions are evaluated and snapshotted. At runtime, only the Rust ops + any non-snapshotted extensions remain to load.

### 2.6 Snapshotting

Building the binary involves a **build-time JavaScript snapshot**: all bootstrap JS for `deno_core` + every `ext/*` is parsed and serialized into a binary blob that ships inside the executable. At startup, V8 deserializes the snapshot, skipping the cost of parsing and evaluating ~2 MB of JS. The cli `Cargo.toml` enables `include_js_files_for_snapshotting` only on its `build-dependencies`, so the snapshot is generated by `build.rs` and the embedded blob is the only JS that ships.

Disable with the `hmr` Cargo feature for development: this drops the snapshot and reloads JS from disk, which is slower but allows live editing.

---

## 3. System Requirements

### 3.1 Runtime requirements (running `deno`)

| Resource | Minimum | Notes |
|----------|---------|-------|
| OS | Linux 4.18+, macOS 11+, Windows 10+ | x86_64 and ARM64 supported on Linux/macOS; x86_64 only on Windows |
| RAM | 64 MB free | V8 baseline; real apps need more |
| Disk | ~150 MB for binary + cache headroom in `$DENO_DIR` | `deno compile` outputs are 80–100 MB each |
| libc (Linux) | glibc 2.28+ | Pre-built binaries are linked against glibc; for older systems build from source or use musl |

The pre-built binary is statically linked against V8 and the bundled C++ runtime; it does not depend on any system JS engine.

### 3.2 Build requirements (building from source)

| Tool | Why |
|------|-----|
| Rust toolchain pinned in `rust-toolchain.toml` | The workspace requires a specific channel |
| Python 3 | V8 build scripts |
| C++ toolchain (clang preferred; MSVC on Windows; gcc supported) | Compiles V8 |
| protoc | Some workspace crates use Protocol Buffers |
| Git | Source control; builds use git revision metadata |
| ~10 GB free disk | V8 build artefacts |
| 8 GB+ RAM | Linker memory pressure on V8 |

V8 itself may compile for 30+ minutes on first build. Subsequent incremental builds are minutes.

### 3.3 Platform targets supported by `deno compile`

| Target | OS / arch |
|--------|-----------|
| `x86_64-unknown-linux-gnu` | Linux x86_64 |
| `aarch64-unknown-linux-gnu` | Linux ARM64 |
| `x86_64-pc-windows-msvc` | Windows x86_64 |
| `x86_64-apple-darwin` | macOS Intel |
| `aarch64-apple-darwin` | macOS Apple Silicon |

Cross-compile by passing `--target` to `deno compile`. The `denort` runtime for the target is downloaded and cached automatically on first use.

---

## 4. Configuration Mastery

### 4.1 Configuration Discovery Order

Deno resolves configuration in this order (highest to lowest precedence):

1. Command-line flags (`--config`, `--lock`, `--import-map`, `--allow-*`).
2. Environment variables (`DENO_DIR`, `DENO_NO_PACKAGE_JSON`, etc.).
3. Per-task `--env-file` if specified.
4. `deno.json` / `deno.jsonc` discovered by walking from `--initial-cwd` (or the current working directory) upward.
5. `package.json` discovered the same way (when not disabled by `DENO_NO_PACKAGE_JSON`).
6. Built-in defaults.

Use `--no-config` to disable `deno.json` discovery entirely; `--config=path` to pin a specific file.

> [!IMPORTANT]
> In workspace setups, each member's `deno.json` inherits the root `imports` map but can override individual specifiers. Task definitions do not inherit — each member must declare its own tasks.

### 4.2 The `deno.json` Schema

The full JSON schema is `cli/schemas/config-file.v1.json` (~600 lines). The top-level keys, exhaustively:

| Key | Type | Purpose |
|-----|------|---------|
| `name` | string | Package name (for JSR publish) |
| `version` | string | Package version (semver) |
| `exports` | string \| object | Module entry point(s) |
| `imports` | object | Bare specifier → URL/spec mapping |
| `scopes` | object | Per-prefix import map overrides |
| `tasks` | object | Named scripts for `deno task` |
| `fmt` | object | Formatter settings |
| `lint` | object | Linter settings |
| `test` | object | Test discovery and runner settings |
| `bench` | object | Benchmark discovery and runner settings |
| `compilerOptions` | object | TypeScript compiler options (subset of `tsc`) |
| `compile` | object | `deno compile` defaults: `include`, `exclude`, `permissions` |
| `permissions` | object | Named `permissionSet`s reusable across commands |
| `allowScripts` | boolean \| array \| object | npm lifecycle script policy |
| `nodeModulesDir` | "auto" \| "manual" \| "none" | `node_modules/` strategy |
| `unstable` | array of strings | Enable unstable feature flags |
| `workspace` | array of strings | Member directories for monorepos |
| `lock` | string \| boolean | Lockfile path or `false` to disable |
| `vendor` | string | Vendor directory for `deno vendor` |
| `license` | string | SPDX license identifier |
| `publish` | object | `include` / `exclude` for JSR publish |
| `links` | array | Local development links (workspace siblings via path) |

#### `permissionSet` schema (excerpt)

```jsonc
"permissionSet": {
  "all": boolean,
  "read":   true | false | string[] | { allow: ..., deny: ..., ignore: ... },
  "write":  true | false | string[] | { allow: ..., deny: ... },
  "import": true | false | string[] | { allow: ..., deny: ... },
  "env":    true | false | string[] | { allow: ..., deny: ..., ignore: ... },
  "net":    true | false | string[] | { allow: ..., deny: ... },
  "run":    true | false | string[] | { allow: ..., deny: ... },
  "ffi":    true | false | string[] | { allow: ..., deny: ... },
  "sys":    true | false | string[] | { allow: ..., deny: ... }
}
```

The asymmetry — only `read`, `env`, `sys` accept the `ignore` key — reflects which categories support being silently no-op'd vs. denied.

### 4.3 Workspace (Monorepo) Configuration

A root `deno.json` declares `workspace`:

```jsonc
{ "workspace": ["./packages/core", "./packages/cli", "./packages/web"] }
```

Each member has its own `deno.json` with `name` / `version` / `exports`. Commands operate over the full workspace:

| Behavior | Detail |
|----------|--------|
| `deno test` | Runs tests in every member |
| `deno fmt` / `deno lint` | Operates on every member |
| `deno publish` | Publishes every member to JSR |
| `--filter <pattern>` | Restrict to matching member names |
| `imports` resolution | Workspace members resolve each other by their `name` automatically |

`libs/config/workspace` implements discovery and member resolution.

### 4.4 Lockfile (`deno.lock`)

`libs/lockfile` defines the format. Top-level keys:

| Key | Purpose |
|-----|---------|
| `version` | Lockfile schema version (currently 4) |
| `specifiers` | Map of `jsr:` / `npm:` specifier → resolved version |
| `jsr` | Per-package integrity hashes |
| `npm` | npm dependency graph with integrity, dist tarball URL, dependencies |
| `redirects` | Recorded HTTP redirects for stable resolution |
| `remote` | Integrity hashes for `https://` imports |
| `workspace` | Per-workspace-member resolved deps |

Pass `--frozen` to fail when the lockfile would change (CI default for reproducible builds). `--no-lock` disables lockfile use entirely.

### 4.5 `compilerOptions` — TypeScript Settings

A subset of `tsconfig.json`. Notable supported keys:

`strict`, `strictNullChecks`, `noImplicitAny`, `noFallthroughCasesInSwitch`, `noImplicitOverride`, `noImplicitThis`, `noUnusedLocals`, `noUnusedParameters`, `useDefineForClassFields`, `experimentalDecorators`, `emitDecoratorMetadata`, `lib`, `jsx`, `jsxFactory`, `jsxFragmentFactory`, `jsxImportSource`, `jsxImportSourceTypes`, `target`, `types`, `checkJs`, `allowJs`.

Notably *not* supported: `outDir`, `rootDir`, `module`, `moduleResolution`, `paths`, `baseUrl` — Deno controls module resolution itself via `imports` / `scopes`.

`compilerOptions.lib` defaults are derived from the unstable flags. With no flags, `["deno.window"]` is used. Adding `"dom"` enables browser globals; `"deno.unstable"` adds typings for unstable APIs.

### 4.6 Environment Variable Precedence

Some env vars have CLI flag equivalents. Where both are set:

| Env var | CLI override | Winner |
|---------|--------------|--------|
| `DENO_DIR` | none | env |
| `DENO_INSTALL_ROOT` | `deno install --root=PATH` | CLI |
| `DENO_COVERAGE_DIR` | `--coverage=DIR` | CLI |
| `DENO_NO_UPDATE_CHECK` | none | env |
| `DENO_AUTH_TOKENS` | none | env |
| `DENO_CERT` | none | env (combined with system store via `DENO_TLS_CA_STORE`) |
| `HTTP_PROXY` / `HTTPS_PROXY` | none | env |
| `NO_COLOR` | none | env |
| `FORCE_COLOR` | none | env (overrides `NO_COLOR` if both set) |

Full list of supported env vars: see User Guide Section 7 (sourced from `cli/args/flags.rs:1452-1591`).

---

## 5. Security

### 5.1 Threat Model

Deno's primary security posture is to make a *script* safe for the user to run, not to make the *host machine* safe for the user. Untrusted code runs inside the V8 sandbox and can only escape via Deno-controlled ops, all of which are gated by the permission system. Secondary properties:

- The V8 process is the security boundary; there is no in-process privilege separation.
- Permissions are checked at op-call time, not at module-load time. A malicious module loaded with `--allow-net` can still attempt FS reads — the check fires when it tries.
- Permissions cannot be widened at runtime. They can only be narrowed via `--deny-*` or via `Deno.permissions.revoke()`.
- The permission prompt is interactive and bypassable in CI by `--no-prompt`.

### 5.2 Permission Model Internals

`runtime/permissions/lib.rs` defines `PermissionsContainer`, the central state for a worker. Each permission category is a struct (e.g. `UnaryPermission<NetDescriptor>`) holding allow-list, deny-list, ignore-list, and prompted state.

Op call flow:

1. The op extracts `&PermissionsContainer` from the `OpState`.
2. It calls a typed check method, e.g. `state.check_net(&host, port, "Deno.connect")`.
3. The check evaluates: deny list → prompted-deny → granted-allow → allow-list → prompted-allow → prompt user (or deny if `--no-prompt`).
4. On allow, the op proceeds; on deny, it returns `PermissionDenied` to JS.

`is_allow_all()` short-circuits to `true` when `--allow-all` was used. Without `-A`, every op call goes through the full check.

> [!WARNING]
> Permissions are checked at op-call time, not at module-load time. A module imported with `--allow-net` can still attempt file system reads — the denial fires only when the syscall-level op is invoked. Always apply the principle of least privilege.

### 5.3 Known Security Considerations

| Area | Note |
|------|------|
| `--allow-import` | Required to fetch *new* remote modules. Without it, only cached modules can be imported. This is a meaningful supply-chain control. |
| `--allow-net=*` / bare `--allow-net` | Grants all hosts. Always pass an allow-list in production. |
| Permission grants in `deno compile` | Baked into the binary; cannot be widened. Reduces attack surface for shipped binaries. |
| npm lifecycle scripts | Disabled by default (`allowScripts: false`). Approve explicitly via `deno approve-scripts` or `deno install --allow-scripts=npm:foo`. |
| FFI (`--allow-ffi`) | Loads arbitrary dynamic libraries; equivalent to running native code. Treat as `--allow-all`. |
| Subprocess (`--allow-run`) | Spawned children inherit *no* Deno permissions but full OS capabilities of the parent process. The child is outside Deno's sandbox. |
| HTTPS module integrity | Lockfile records hashes; `--frozen` fails on mismatch. |
| `DENO_TRACE_PERMISSIONS=1` | Adds stack traces to prompts. Useful for debugging supply-chain issues. |

### 5.4 Hardening Recommendations

- Use `--no-prompt` in CI and in production binaries to convert silent denials into errors.
- Use `--allow-net=` with explicit hosts; never bare `--allow-net`.
- Use `--allow-read=` and `--allow-write=` with explicit paths.
- Set `unstable` only to the features actually needed; do not blanket-enable.
- Pin Deno versions in CI (`actions/setup-deno@vN` with `deno-version: vX.Y.Z`).
- Commit `deno.lock` and run `deno install --frozen` (or rely on the `--frozen` default in CI mode).
- Use `DENO_NO_UPDATE_CHECK=1` in air-gapped environments.
- For network apps, run behind a reverse proxy and set `DENO_TRUST_PROXY_HEADERS=1` only when the proxy is trusted to set the client address.
- Approve npm lifecycle scripts package-by-package, never globally.

> [!CAUTION]
> `--allow-ffi` loads arbitrary native dynamic libraries, effectively granting the same capabilities as `--allow-all`. Treat any FFI usage as a full trust boundary escape.

---

## 6. Operations and Maintenance

### 6.1 Cache Layout (`$DENO_DIR`)

Default path: `$HOME/.cache/deno` (Linux), `$HOME/Library/Caches/deno` (macOS), `%LOCALAPPDATA%\deno` (Windows). Override with `DENO_DIR`.

```
$DENO_DIR/
  deps/                 HTTPS module cache (URL → file)
  npm/                  npm package cache (registry → tarball)
  registries/           jsr:/npm: metadata cache
  gen/                  Transpiled JS for TS modules (keyed by source hash)
  check/                Type-check artefacts and diagnostics cache
  emit/                 Emit cache (build outputs)
  binary/               Downloaded denort runtimes for `deno compile --target`
  binary_versions.json  Tracker for downloaded denort versions
  kv/                   Default storage for Deno.openKv() (SQLite files)
  webcache/             Web Cache API storage
  webstorage/           localStorage / sessionStorage SQLite files
  repl.log              REPL history (override with DENO_REPL_HISTORY)
  upgrade_check/        Last-update-check timestamp
```

`deno clean` removes the entire `$DENO_DIR`. To selectively reload, use `--reload[=URL]`.

> [!TIP]
> Set `DENO_DIR` to a shared network path for team-wide cache sharing. The cache is content-addressed and safe for concurrent access from multiple Deno processes.

### 6.2 Logging

Deno respects `RUST_LOG` (Rust `env_logger` syntax):

```bash
RUST_LOG=info deno run main.ts
RUST_LOG=deno=debug,deno_runtime=trace deno run main.ts
RUST_LOG=deno_core::module_loader=trace deno run main.ts
```

Useful target prefixes: `deno`, `deno_runtime`, `deno_core`, `deno_npm`, `deno_resolver`, `deno_lsp`, `tokio`. Combine with `DENO_TRACE_PERMISSIONS=1` to debug permission issues.

### 6.3 Monitoring (OpenTelemetry)

`ext/telemetry` exposes the runtime via OpenTelemetry. Configure with standard OTel env vars:

| Var | Purpose |
|-----|---------|
| `OTEL_DENO_TRACING` | `true`/`false` to toggle tracing |
| `OTEL_DENO_METRICS` | `true`/`false` to toggle metrics |
| `OTEL_DENO_CONSOLE` | `ignore` / `capture` / `replace` — controls console.* trace export |
| `DENO_UNSTABLE_OTEL_DETERMINISTIC` | Integer seed for deterministic span IDs (testing) |
| Standard `OTEL_EXPORTER_OTLP_*` | Endpoint, headers, etc. |

The runtime exports HTTP request spans, op spans, and JS-emitted spans (when user code uses `@opentelemetry/api`).

> [!NOTE]
> OpenTelemetry support requires `--unstable-otel` or adding `"otel"` to the `unstable` array in `deno.json`. Without this flag, the `OTEL_*` environment variables are ignored.

### 6.4 Inspector and Debugging

```bash
deno run --inspect main.ts                # listen on 127.0.0.1:9229
deno run --inspect-brk main.ts            # break before user code
deno run --inspect-wait main.ts           # wait for debugger to attach
deno run --inspect=0.0.0.0:9229 main.ts   # listen on all interfaces (BE CAREFUL)
```

Open `chrome://inspect` in Chrome to attach. The inspector speaks the standard V8 Debugger Protocol.

The standalone inspector server (`libs/inspector_server`) is also used by `deno lsp` for IDE debugging integration.

> [!WARNING]
> Never expose the inspector on `0.0.0.0` in production. The V8 inspector protocol allows arbitrary code execution — an attacker with network access to the inspector port gains full control of the process.

### 6.5 Upgrade and Rollback

```bash
deno upgrade                # latest stable
deno upgrade canary         # canary
deno upgrade <version>      # specific version
deno upgrade --output PATH  # specify output binary path
deno upgrade --dry-run      # show what would happen, don't change anything
deno upgrade --force        # overwrite when target version equals current
```

The upgrade subcommand downloads from `https://github.com/denoland/deno/releases`, verifies the SHA256, and atomically replaces the binary. To roll back to a known version: `deno upgrade <previous-version>`. There is no built-in version manager; for multi-version coexistence use a tool like `dvm` or install side-by-side via different `DENO_INSTALL_ROOT` directories.

### 6.6 Cron and Long-Running Servers

For production HTTP servers using `Deno.serve`:

- Run with `--parallel` to spawn one worker per CPU.
- Use a process supervisor (systemd, supervisord, Docker restart policies) — Deno does not daemonize.
- For Linux, set `DENO_USR2_MEMORY_TRIM=1` to allow SIGUSR2 to free memory.
- Use `DENO_USE_CGROUPS=1` so V8's heap limit reflects container memory limits, not host RAM.

For scheduled work: `Deno.cron("name", "* * * * *", handler)` (requires `--unstable-cron`). State for cron is stored in Deno KV.

### 6.7 Backup

| Item | Backup target |
|------|---------------|
| Deno KV data | `$DENO_DIR/kv/*.db` (SQLite) — copy with KV closed, or use SQLite `.backup` |
| Web storage | `$DENO_DIR/webstorage/` |
| Web cache | `$DENO_DIR/webcache/` |
| `deno.lock` | Always commit to source control |

The dependency cache (`deps/`, `npm/`) is reproducible from `deno.lock` + network and does not need backup.

### 6.8 Deployment Topologies

Single-process: a `deno serve` instance behind nginx/Caddy. Set `--port`, `--host=127.0.0.1`, and proxy.

Multi-process: spawn N instances bound to different ports, load-balance with the proxy. Deno does not provide cluster-mode; use OS-level process management.

Single-binary deployment: `deno compile -A --output app main.ts` produces a self-contained executable. Ship it directly. The binary embeds the runtime, so the target machine needs no Deno installation.

Container: `denoland/deno:latest` is the official image. For minimal images, use `gcr.io/distroless/cc` and copy a `deno compile`d binary in.

---

## 7. Build and Distribution

### 7.1 Cargo Workspace

Root `Cargo.toml` declares the workspace with `resolver = "2"` and lists all 70+ members. Workspace-level `package` metadata (authors, edition, license, repository) is inherited by all crates. Workspace-level `dependencies` pin every external dependency version once and inject `path = "./..."` for internal crates so they are always built from source.

Build the binary:

```bash
cargo build --release -p deno
./target/release/deno --version
```

Build with debug symbols and dhat heap profiling:

```bash
cargo build --profile=release-with-debug --features=dhat-heap -p deno
```

Build the slim runtime crate alone (for embedding):

```bash
cargo build -p deno_runtime
```

### 7.2 Cargo Features (cli crate)

| Feature | Default | Purpose |
|---------|---------|---------|
| `upgrade` | yes | Enables `deno upgrade` and the background update check. Disabled by Linux distros that ship Deno as a system package. |
| `__vendored_zlib_ng` | yes | Vendor zlib as zlib-ng for performance. |
| `dhat-heap` | no | Enables [dhat](https://github.com/nnethercote/dhat) heap profiling. |
| `hmr` | no | Disables snapshot loading; re-evaluates JS sources on every run for live editing. |
| `lsp-tracing` | no | Adds tracing/OpenTelemetry instrumentation to `deno lsp`. |
| `panic-trace` | no | Custom panic backtrace via `deno_panic`. |

### 7.3 Build-Time Snapshot

`cli/build.rs` builds a V8 startup snapshot. The snapshot embeds:

- `deno_core` infrastructure JS
- All `ext/*` JS bootstrap code (each `Extension` contributes its `esm` files at snapshot time)
- `runtime/js/99_main.js` — the Deno namespace setup

Without snapshotting, startup parses ~2 MB of JS on every invocation. With it, V8 mmap-loads a binary blob and starts in milliseconds.

`cli/snapshot/` holds the persisted snapshot artifacts pulled into the binary by `include_bytes!`.

### 7.4 Release Process

1. Tag a commit with `vX.Y.Z` on `main`.
2. CI (GitHub Actions) builds binaries for all 5 target triples.
3. Each binary is signed and uploaded to a GitHub Release.
4. The install scripts (`install.sh`, `install.ps1`) detect the latest release and download the appropriate binary.
5. `deno upgrade` queries the same GitHub Releases API.

`Releases.md` in the repo root is the canonical changelog.

### 7.5 Dependency Management for the Workspace

External dependencies are pinned in the root `[workspace.dependencies]` block. Internal deno crates use `path` references so workspace builds always use local source. Deno-specific helper crates (`deno_ast`, `deno_lockfile`, `deno_doc`, `deno_lint`, etc.) are pinned to exact versions via `=X.Y.Z`.

To upgrade an internal crate: bump the `=X.Y.Z` line in `[workspace.dependencies]`, run `cargo update -p <crate>`, and verify with `cargo check --workspace`.

### 7.6 Cross-Compile

Cargo cross-compile setup is in `.cargo/config.toml`. CI uses `cross` and platform-specific runners (Apple Silicon and ARM Linux are native; Windows is MSVC).

Cross-compile a `deno compile` binary (different from cross-compiling `deno` itself):

```bash
deno compile --target aarch64-unknown-linux-gnu --output myapp-arm64 main.ts
```

The `denort` runtime for the target architecture is downloaded to `$DENO_DIR/binary/` on first cross-compile.

---

## 8. Module Reference

### 8.1 The deno_core Trinity

`deno_core` is the foundation. The three central types:

#### `JsRuntime`

`libs/core/runtime/jsruntime.rs`. Owns a V8 Isolate and Context. Provides:

- `new(options)` — construct a runtime with extensions, snapshot, module loader, op state seed.
- `execute_script(name, source)` — synchronously execute JS, returns the V8 value.
- `load_main_module(specifier, code)` — load a module graph rooted at `specifier`.
- `mod_evaluate(module_id)` — evaluate a loaded module; returns a future for the result.
- `run_event_loop(opts)` — drive Tokio + microtasks until idle.

#### `Extension`

`libs/core/extensions.rs`. Bundles ops, ESM modules, and state seed for a feature. Built via the `extension!` macro:

```rust
extension!(deno_my,
  deps = [deno_web, deno_url],
  ops = [op_foo, op_bar],
  esm_entry_point = "ext:deno_my/01_main.js",
  esm = ["01_main.js", "02_helpers.js"],
  options = { foo: Arc<dyn FooSrv> },
);
```

`deps` enforces extension-load ordering. `options` are passed at runtime via `MyExtensionOptions { ... }`.

#### `ModuleLoader`

Trait in `libs/core/modules/`. Implementors handle `resolve()` (specifier → URL) and `load()` (URL → source). The CLI's loader at `cli/module_loader.rs` handles HTTPS, JSR, npm, file, data, and node schemes.

### 8.2 The op2 Macro

`libs/ops/op2/`. The `#[op2]` proc-macro generates V8 fast-call entry points. Variants:

| Form | Purpose |
|------|---------|
| `#[op2]` | Sync op |
| `#[op2(async)]` | Async op (returns `Future`) |
| `#[op2(fast)]` | Force fast-call code path (sync only, restricted parameter types) |
| `#[op2(reentrant)]` | Allows re-entering JS from within the op (rare) |
| `#[op2(stack_trace)]` | Capture JS stack trace at call site (perf cost) |
| `#[op2(no_side_effects)]` | Hint to V8 for further optimization |

Parameter attributes:

| Attr | Meaning |
|------|---------|
| `#[string]` | Cheap string conversion |
| `#[buffer]` | Treat as `Uint8Array`/`Buffer`, no copy |
| `#[serde]` | Use serde_v8 for serialization |
| `#[state]` | Inject from `OpState` |
| `#[smi]` | Small integer fast path |
| `#[bigint]` | BigInt as `i64`/`u64` |

### 8.3 Module Graph and Resolution

The graph is built by `deno_graph` (an external crate, but wholly authored by the Deno team). It traverses imports starting from the entry point, applies the import map, resolves npm/jsr specifiers via the local resolver, and produces a module graph used for type-checking, bundling, vendoring, publishing, and execution.

Key types:

| Type | Purpose |
|------|---------|
| `ModuleGraph` | The full graph: nodes (modules), edges (imports), deps, redirects. |
| `ModuleSpecifier` | A normalized URL (`url::Url`). |
| `WorkspaceResolver` | Resolves bare specifiers within a workspace. |
| `NpmResolver` | npm version resolution + node_modules layout. |
| `JsrResolver` | JSR resolution + version selection. |

### 8.4 The Type-Checking Pipeline

`cli/type_checker.rs` and `cli/tsc/`. As of v2, Deno uses **TypeScript-Go** (a Go port of `tsc`) for type-checking. The Rust client (`libs/typescript_go_client/`) talks to the Go process over IPC.

Flow:

1. `deno check main.ts` builds the module graph.
2. The graph is serialized and sent to the TS-Go subprocess.
3. TS-Go runs the type checker and returns diagnostics.
4. Diagnostics are formatted and printed by `deno_runtime::fmt_errors`.
5. Successful type-check results are cached in `$DENO_DIR/check/`.

The legacy `tsc` integration (in `cli/tsc/`) remains for cases where TS-Go cannot handle the graph (some experimental syntax, `compilerOptions.experimentalDecorators` interactions). Both paths produce the same diagnostic format.

### 8.5 The Language Server (`deno lsp`)

`cli/lsp/`. A Tower-LSP server that implements the standard LSP plus Deno-specific extensions:

| Custom request | Purpose |
|----------------|---------|
| `deno/cache` | Cache deps for the workspace |
| `deno/performance` | Performance metrics |
| `deno/reloadImportRegistries` | Refresh JSR/npm completion data |
| `deno/task` | List runnable tasks |
| `deno/testing/*` | Test discovery, run requests, results |
| `deno/virtualTextDocument` | Read transient documents (e.g. opened from a URL) |

The LSP shares the resolver, type-checker, and module loader with the rest of the CLI; opening a workspace in an editor exercises the same resolution path as `deno run`.

### 8.6 The Standalone Runtime (`denort`)

`cli/rt/`. A reduced binary that ships inside `deno compile` outputs. It contains the runtime + ext extensions but omits the type checker, formatter, linter, doc generator, REPL, and LSP. The `eszip` appended to the executable contains the user's bundled module graph; on startup, `denort` loads the eszip into memory and executes the entry point.

`ext/rt_helper` provides shared helpers between `cli/` and `cli/rt/`.

### 8.7 The Bundler

`ext/bundle` + `cli/tools/bundle/`. The bundler is built on esbuild (via WASM). It produces ESM bundles for browser/Deno targets and is the engine behind:

| Use | Mechanism |
|-----|-----------|
| `deno bundle main.ts` | Direct user invocation |
| `deno compile` | Bundler produces the eszip embedded in the binary |
| `Deno.bundle(...)` | Programmatic API (unstable) |

### 8.8 The Package Manager (cli/tools/pm)

`cli/tools/pm/`. Implements `deno add`, `deno remove`, `deno install`, `deno uninstall`, `deno outdated`, `deno update`, and `deno approve-scripts`. Backed by:

- `libs/npm` — npm package resolution
- `libs/npm_cache` — disk cache
- `libs/npm_installer` — node_modules layout
- `libs/npmrc` — registry/auth config
- `libs/resolver/npm` — runtime npm resolution

---

## 9. Code Patterns

### 9.1 Cargo Conventions

- **Workspace-pinned everything.** Every external dep is in `[workspace.dependencies]`. Crates use `workspace = true`.
- **Per-crate features for slim builds.** `deno_runtime` has `transpile`, `snapshot`, `hmr` features so embedders can opt in/out of features that pull in heavy deps.
- **Internal crates ship to crates.io.** `deno_core`, `deno_ast`, `deno_lockfile`, `deno_doc`, `deno_lint`, etc. are independently versioned and published.

### 9.2 Async Patterns

- The runtime is single-threaded Tokio (`current_thread` flavor) — V8 isolates are not Send.
- `WebWorker` spawns a fresh OS thread with its own Tokio runtime.
- Long-running ops yield via `tokio::spawn_blocking` for FS-heavy work; otherwise they run on the main thread.
- Cancellation is cooperative via `CancelHandle` (a `deno_core` type).

### 9.3 Error Handling

`deno_error::JsErrorBox` and `AnyError` are the universal error types crossing the JS/Rust boundary. Errors implement `JsError` for proper translation to JS exception classes (`TypeError`, `RangeError`, `Deno.errors.PermissionDenied`, etc.). Most ops return `Result<T, AnyError>`.

### 9.4 Snapshot-Friendly Code

Anything in an `ext/` JS file that runs at extension load runs at *snapshot* time (during the build), not at runtime. This means:

- No I/O, no Tokio.
- No date/time captures (would freeze build time into the snapshot).
- No network access — `URL`s constructed at snapshot time embed the build host's machine state.

Code that must run per-process (e.g. reading env vars, getting `Date.now()`) goes in the `99_main.js` bootstrap or in lazily-evaluated code paths.

### 9.5 State Management

`OpState` is a per-`JsRuntime` typemap (keyed by Rust type) that ops draw from. Idiomatic pattern: store an `Arc<MyService>` in `OpState`, pull it via `#[state]` in op signatures.

`PermissionsContainer` lives in `OpState` and is mutated only by the runtime (not by ops).

---

## 10. Testing Strategy

### 10.1 Test Categories

| Type | Where | Run with |
|------|-------|----------|
| Rust unit tests | `cli/`, `runtime/`, `ext/*`, `libs/*` | `cargo test --workspace` |
| Spec-based integration tests | `tests/specs/*/__test__.jsonc` | `cargo test --test integration -- specs::` |
| JS unit tests | `tests/unit/*` (run inside Deno) | `tools/test.ts` |
| Node compatibility tests | `tests/unit_node/*`, `tests/node_compat/*` | `tools/node_compat/run_all_test_unmodified.ts` |
| WPT (Web Platform Tests) | `tests/wpt/` | `tools/wpt.ts` |
| FFI / NAPI / SQLite extension tests | `tests/ffi`, `tests/napi`, `tests/sqlite_extension` | Compiled and exercised by spec tests |
| Benchmarks | `tests/bench/` | `cargo bench` or `deno bench` |

The spec format: each test directory has a `__test__.jsonc` describing args, env, expected exit code, expected stdout pattern, expected stderr pattern. The integration test runner discovers them recursively.

### 10.2 Local Mock Registries

`tests/registry/` ships a self-contained JSR-shaped and npm-shaped registry served by the `tests/util/server` HTTP server during tests. This allows tests to reference deterministic packages like `npm:@denotest/cjs-multiple-exports` without hitting npm.org.

### 10.3 Adding a Spec Test

1. Create `tests/specs/<group>/<test_name>/`.
2. Add `__test__.jsonc`:

   ```jsonc
   { "args": "run -A main.ts", "output": "main.out" }
   ```

3. Add `main.ts` and `main.out` (the expected stdout/stderr).
4. Run `cargo test --test integration -- specs::<group>::<test_name>`.

---

## 11. Operating in Production

### 11.1 Container Image

```dockerfile
FROM denoland/deno:2.7.12 AS build
WORKDIR /app
COPY deno.json deno.lock ./
RUN deno install --frozen
COPY . .
RUN deno cache --frozen main.ts
CMD ["deno", "run", "--no-prompt", "--allow-net", "--allow-read=./data", "main.ts"]
```

For minimal image size, use `deno compile` and a distroless base:

```dockerfile
FROM denoland/deno:2.7.12 AS build
WORKDIR /app
COPY . .
RUN deno compile --output myapp -A main.ts

FROM gcr.io/distroless/cc
COPY --from=build /app/myapp /usr/local/bin/myapp
ENTRYPOINT ["/usr/local/bin/myapp"]
```

### 11.2 systemd Unit

```ini
[Unit]
Description=My Deno Service
After=network.target

[Service]
ExecStart=/usr/local/bin/deno run --no-prompt --allow-net --allow-read=/var/lib/myapp /var/lib/myapp/main.ts
Restart=on-failure
RestartSec=5
User=myapp
Environment=DENO_DIR=/var/cache/deno
Environment=DENO_NO_UPDATE_CHECK=1
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
```

### 11.3 Kubernetes

Run `deno serve --parallel --host 0.0.0.0 --port 8080 main.ts` in the container. Expose port 8080. The pod's CPU limit governs the parallel worker count when `DENO_USE_CGROUPS=1` is set.

For graceful shutdown: `Deno.serve` already handles SIGTERM. Kubernetes' default `terminationGracePeriodSeconds: 30` is sufficient for most workloads.

### 11.4 Resource Limits

| Setting | How |
|---------|-----|
| V8 heap limit | `--v8-flags=--max-old-space-size=4096` (MB) |
| File descriptor limit | OS-level (`ulimit -n` or systemd `LimitNOFILE`) |
| CPU pinning | OS-level (`taskset`, cgroups, k8s `cpu` limits) |
| Memory pressure response | `DENO_USR2_MEMORY_TRIM=1` + `kill -USR2 <pid>` to free memory (Linux only) |

---

## 12. Troubleshooting (Operator-Focused)

For developer-side troubleshooting, see the User Guide. This section covers operational issues.

> [!WARNING]
> Production Deno processes should always run with `--no-prompt` to convert interactive permission prompts into hard denials. An unattended process that blocks on a prompt will stall indefinitely.

### Issue: Deno binary updates itself unexpectedly in a Linux distro package

> **Cause:** `deno upgrade` overwrites the binary. The distro tracks a different version.
> **Solution:** Build with `cargo build --release --no-default-features --features __vendored_zlib_ng -p deno` to disable the `upgrade` feature.

### Issue: Type-check is slow in CI

> **Cause:** Cold cache; TypeScript-Go starting up; remote modules being fetched.
> **Solution:** Cache `$DENO_DIR/check/` and `$DENO_DIR/deps/` between CI runs. Use `deno cache main.ts` as a separate step before `deno test`.

### Issue: V8 process exceeds container memory

> **Cause:** Default heap limit ignores cgroups.
> **Solution:** Set `DENO_USE_CGROUPS=1` so V8 sees the container limit. Alternatively, pass `--v8-flags=--max-old-space-size=<MB>`.

### Issue: `deno compile` binary fails on target with "GLIBC_X.YZ not found"

> **Cause:** The pre-built `denort` for Linux is built against newer glibc than the target.
> **Solution:** Build `denort` from source on a matching glibc base, or target a musl variant via cross-compilation.

### Issue: Module fetch fails behind corporate proxy

> **Cause:** Proxy credentials or CA cert.
> **Solution:** Set `HTTPS_PROXY` and `HTTP_PROXY`. For private CA: `DENO_CERT=/path/to/ca-bundle.pem` and possibly `DENO_TLS_CA_STORE=system,mozilla`.

### Issue: `deno serve --parallel` only spawns one worker

> **Cause:** `--parallel` without an integer defaults to detected CPU count from cgroups; a misconfigured cgroup may report 1.
> **Solution:** Pass an explicit count: `--parallel=8`. Verify cgroup with `nproc` inside the container.

### Issue: Inspector is reachable from outside the container

> **Cause:** `--inspect=0.0.0.0:9229` was used. Deno's inspector has no auth.
> **Solution:** Always use `--inspect=127.0.0.1:9229` and tunnel via SSH or kubectl port-forward. Never expose the inspector publicly.

> [!TIP]
> When debugging CI cache misses, run `deno info --json main.ts` to dump the full module graph with resolved URLs and cache paths. Compare against the previous run to identify what changed.

### Issue: KV database corruption after kill -9

> **Cause:** SQLite WAL not checkpointed.
> **Solution:** Use `kill -TERM` and let Deno's signal handler close KV cleanly. For recovery, open the SQLite file with `sqlite3` and run `PRAGMA wal_checkpoint(TRUNCATE);`.

### Issue: Lockfile drift across team members

> **Cause:** Different platforms resolve packages slightly differently (npm optional deps).
> **Solution:** Commit the lockfile as-is and use `--frozen` in CI. For platform-specific optional deps, use `npmrc` settings.

### Issue: `deno upgrade` fails with "could not download release"

> **Cause:** GitHub rate-limited the unauthenticated request, or the network blocks `github.com`.
> **Solution:** Set `GH_TOKEN` (or `GITHUB_TOKEN`) for an authenticated request. For air-gapped environments, mirror releases internally and use `--canary-url=...` or `deno upgrade --output PATH` after manual download.

---

## 13. Contributing

### 13.1 Where to Start

- Code of conduct: [.github/CODE_OF_CONDUCT.md](https://github.com/denoland/deno/blob/main/.github/CODE_OF_CONDUCT.md)
- Contributing guide: [.github/CONTRIBUTING.md](https://github.com/denoland/deno/blob/main/.github/CONTRIBUTING.md)
- Good first issues: <https://github.com/denoland/deno/labels/good%20first%20issue>

### 13.2 Local Development

```bash
git clone https://github.com/denoland/deno.git
cd deno
cargo build -p deno              # debug build (~5 min after V8 caches)
./target/debug/deno --version
cargo test -p deno_runtime       # unit tests for runtime crate
cargo test --test integration -- specs::run::  # integration specs for `run`
```

For tight iteration on JS bootstrap code, use the `hmr` Cargo feature: `cargo build --features hmr -p deno`. With HMR, edits to `runtime/js/*` and `ext/*/*.js` take effect on the next `deno run` without rebuilding the snapshot.

### 13.3 Style and Lint

- Rust: `cargo fmt --check` and `cargo clippy -- -D warnings`. CI fails on either.
- JS/TS: `deno fmt` and `deno lint`. CI runs both.
- Spec tests: copyright header is mandatory (`// Copyright 2018-2026 the Deno authors. MIT license.`).

### 13.4 PR Process

1. Branch from `main`.
2. Open a PR with a clear description and (where relevant) a spec test.
3. CI runs the full matrix: build on all 5 targets + spec tests + WPT subset + lint + format.
4. Maintainers review. Most PRs require one approval; substantial changes require two.
5. Squash-merge into `main` with a Conventional Commits-style message.

### 13.5 Architecture for New Contributors

| Want to add | Start here |
|-------------|------------|
| A new `Deno.*` API | `ext/<closest_existing>/lib.rs` + JS in same crate. Add ops with `#[op2]`. Wire from `runtime/lib.rs`. |
| A new CLI subcommand | `cli/args/flags.rs` (clap definition + parse) + `cli/tools/<cmd>.rs` (handler). |
| A new node:* polyfill | `ext/node/polyfills/<module>.ts`. Wire in `ext/node/polyfill_proxy.ts`. |
| A new lint rule | `deno_lint` external crate (https://github.com/denoland/deno_lint). |
| A new lockfile field | `libs/lockfile/lib.rs`. Bump schema version if breaking. |

---

## 14. Quick Reference

### 14.1 Rebuild After Pulling

```bash
cargo build -p deno --release
```

### 14.2 Run Specific Spec Test

```bash
cargo test --test integration -- specs::run::specific_test_name
```

### 14.3 Re-Generate the JSON Schema for `deno.json`

The schema lives at `cli/schemas/config-file.v1.json` and is hand-maintained. Update it whenever a new top-level key is added.

### 14.4 Useful Files

| Concern | Path |
|---------|------|
| All CLI flags and subcommands | `cli/args/flags.rs` |
| All env vars Deno reads | `cli/args/flags.rs:1452-1591` (`ENV_VARS`) |
| Permission model | `runtime/permissions/lib.rs` |
| Module loader | `cli/module_loader.rs` |
| `deno.json` schema | `cli/schemas/config-file.v1.json` |
| Lockfile format | `libs/lockfile/` |
| ext extension contract | `libs/core/extensions.rs` |
| op2 macro | `libs/ops/op2/` |
| Workspace member list | `Cargo.toml` (root) |

### 14.5 GitHub Issues Referenced in Troubleshooting

| Issue | Topic |
|-------|-------|
| [#33291](https://github.com/denoland/deno/issues/33291) | `crypto.verify` Invalid PEM |
| [#33296](https://github.com/denoland/deno/issues/33296) | TLS to Postgres via `npm:pg@8` times out |
| [#33254](https://github.com/denoland/deno/issues/33254) | Windows `writeFileSync` EEXIST |
| [#32821](https://github.com/denoland/deno/issues/32821) | `deno compile` binary hang on 2.7.5+ |
| [#33288](https://github.com/denoland/deno/issues/33288) | `node:http` Server vs `node:net` Server inheritance |
| [#33284](https://github.com/denoland/deno/issues/33284) | LSP Test Explorer divergence with `--env-file` |
| [#33289](https://github.com/denoland/deno/issues/33289) | LSP global types not recognized until file is opened |

---

_Generated May 2026 from denoland/deno at 7529c84. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
