# Elide for Deno users

> Switching from Deno to Elide: what runs unchanged, how commands and permission flags map, benchmarks, strengths, weaknesses and the security model.

Elide runs TypeScript, Web APIs, `node:*` modules and npm packages; the `Deno` global is absent.

## Run the same code

```bash
elide app.ts                    # or: elide run app.ts
elide install --ecosystems=npm  # dependencies from package.json
elide test                      # node:test files
elide serve server.ts           # a module that default-exports { fetch }
```

- TypeScript runs with no compile step and no `tsconfig.json`.
- `fetch`, `Request`, `Response`, Streams, `URL` and Web Crypto are globals.
- Built-ins load with or without the `node:` prefix.
- A module that default-exports `{ fetch }`, as `deno serve` expects, serves under `elide serve` and `elide run`.

See [JavaScript and TypeScript](https://elide.help/docs/web), module status in the [API Overview](https://elide.help/docs/api-overview), and suite results
in the [Compatibility Matrix](https://elide.help/docs/compatibility).

## What changes

| Deno | Elide |
| --- | --- |
| `deno run app.ts` | `elide app.ts` |
| `npm:`, `jsr:` and `https:` imports | Bare specifiers resolved from `node_modules` |
| `deno add`, `deno install` | Add to `package.json`, then `elide install --ecosystems=npm` |
| `deno.lock` | `aube-lock.yaml` |
| `deno task build` | `elide run build`; reads `package.json` scripts, not `deno.json` tasks |
| `deno test`, `Deno.test` | `elide test` with `node:test` and `node:assert` |
| `deno run --inspect app.ts` | `elide run --debugger=cdp app.ts` |
| `deno run --watch app.ts` | No equivalent |
| `--env-file` | Call `process.loadEnvFile()` in code |
| `Deno.serve({ fetch })` | `Elide.serve({ fetch })` |
| `Deno.readTextFile`, `Deno.Command` | `node:fs` and `node:child_process` |
| Web `Worker` | `node:worker_threads` |
| `deno fmt`, `deno lint`, `deno compile` | No equivalent for JavaScript or TypeScript |

## Benchmarks

Hello-world startup with `hyperfine -N -w 10 -r 100` on a 24-core AMD Ryzen Threadripper PRO 9965WX Linux workstation:

| Runtime | Command | Mean |
| --- | --- | --- |
| Deno | `deno run hello.js` | 6.31 ± 0.30 ms |
| Elide | `elide run hello.js` | 1.43 ± 0.06 ms |
| Node | `node hello.js` | 11.70 ± 0.90 ms |
| Bun | `bun hello.js` | 1.63 ± 0.18 ms |

Elide's measured mean is lower than Bun's in this recording.

Hello-world serving with `wrk -t12 -c256 -d10s --latency`. Each server ran on the same 24 logical CPUs, `wrk` on 24
others.

| Runtime | Requests/s | p50 latency | p99 latency |
| --- | --- | --- | --- |
| Deno | 163,511 | 1.54 ms | 1.58 ms |
| Elide | 635,510 | 0.38 ms | 0.59 ms |
| Bun | 156,974 | 1.60 ms | 1.80 ms |

Each server used its defaults: Deno and Bun serve on one thread, Elide on several.

Versions, setup, and recordings: [Benchmarks](https://elide.help/docs/benchmarks).

## Strengths

- `elide serve --contexts=auto server.ts` runs several copies of the app, sized from the CPU cores, on Linux.
- Limits workers, open files, heap and run time, and logs each filesystem decision; see [Security model](#security-model).
- The same binary runs WebAssembly, compiles and runs Java and Kotlin, and runs Python after `elide setup python`.

## Weaknesses

- Everything is allowed until you pass a flag, and no flag denies all network or subprocess access.
- Node-API compatibility is 86.8% and WinterTC 95.9% on their test suites.
- `node:http2`, `node:cluster`, `node:inspector` and `node:wasi` throw `ERR_UNKNOWN_BUILTIN_MODULE`.
- `https.createServer` throws `ERR_HTTPS_SERVER_UNSUPPORTED`, and there is no `WebSocket` global.
- No macOS Intel build; macOS releases are Apple Silicon only.

## Security model

Deno denies access until an `--allow-*` flag grants it. Elide allows access until a flag restricts it.

| Deno | Elide |
| --- | --- |
| No flags: everything denied | No flags: everything allowed |
| `-A`, `--allow-all` | No flags |
| `--allow-read[=PATHS]` | `--allow-read[=PATHS]`; denies all other filesystem access |
| `--allow-write[=PATHS]` | `--allow-write[=PATHS]`; denies all other filesystem access |
| `--allow-net[=HOSTS]` | `--allow-net[=HOSTS]` |
| `--allow-run[=CMDS]` | `--allow-run[=CMDS]` |
| `--deny-net=HOSTS`, `--deny-run=CMDS` | `--deny-net=HOSTS`, `--deny-run=CMDS` |
| `--deny-read`, `--deny-write` | No equivalent; grant only the paths you need |
| `--allow-env[=NAMES]` | No equivalent; `process.env` is always readable, and `--allow-env` scopes `elide.pkl` |
| `--allow-ffi` | Allowed; `--no-native` denies native access |
| Prompt for a missing permission | No prompt; the call fails and names the flag to add |

Elide is also more permissive in two ways:

- `--sandbox` restricts the filesystem only. Network and subprocesses stay open until `--allow-net=HOSTS`,
  `--allow-run=CMDS` or a `--deny-*` flag scopes them.
- A bare `--allow-net` or `--allow-run`, or an empty value, allows everything.

Elide also has these controls:

| Flag | Effect |
| --- | --- |
| `--fs-audit` | Log each filesystem decision to stderr. |
| `--limit=workers=N`, `--limit=fds=N` | Limit live workers and open `node:fs` handles. |
| `--max-mem=SIZE` | Limit the heap size. |
| `--timeout=DURATION` | Stop the run after `DURATION` with exit `124`. |

A Deno command and the closest Elide command:

```bash
deno run --allow-read=. --allow-net=api.example.com app.ts
elide --allow-read=. --allow-net=api.example.com --allow-run=git --timeout=30s app.ts
```

`--sandbox=trusted` is the only available profile. See [Sandbox and Limits](https://elide.help/docs/sandbox).

## Next steps

- [Installation](https://elide.help/docs/installation)
- [JavaScript and TypeScript](https://elide.help/docs/web)
- [Sandbox and Limits](https://elide.help/docs/sandbox)
