diff --git a/README.md b/README.md index 1e46bff..6ee572b 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,11 @@ First [install Deno](https://docs.deno.com/runtime/getting_started/installation/ then run directly (dependencies are fetched automatically): ```bash -deno run https://raw.githubusercontent.com/grantmcdermott/jgd/refs/heads/main/server/main.ts +# macOS / Linux +deno run --allow-net --allow-read --allow-write --allow-env https://raw.githubusercontent.com/grantmcdermott/jgd/refs/heads/main/server/main.ts + +# Windows +deno run -A https://raw.githubusercontent.com/grantmcdermott/jgd/refs/heads/main/server/main.ts ``` Or, clone the repo and run locally: @@ -109,6 +113,19 @@ Or, clone the repo and run locally: cd server && deno task start && cd .. ``` +> [!NOTE] +> On **Windows**, R connections default to a named pipe, and Deno has no +> permission combination narrower than `-A` (`--allow-all`) that can satisfy +> binding one — that's why the Windows command above needs it (`deno task +> start` above already grants `-A` for the same reason). +> +> To keep permissions scoped on Windows instead, add `--tcp ` (e.g. +> `--tcp 8888`) to connect over localhost TCP rather than a named pipe: +> +> ```bash +> deno run --allow-net --allow-read --allow-write --allow-env https://raw.githubusercontent.com/grantmcdermott/jgd/refs/heads/main/server/main.ts --tcp 8888 +> ``` + Once the Deno server is running, open `http://127.0.0.1:/` in your browser (the URL is printed on startup). Then you start executing plotting commands from any R session. diff --git a/server/deno.json b/server/deno.json index 9e763af..c4af711 100644 --- a/server/deno.json +++ b/server/deno.json @@ -8,24 +8,23 @@ "@astral/astral": "jsr:@astral/astral@^0.5" }, "tasks": { - // --allow-net is unrestricted (not scoped to 127.0.0.1): since Deno - // PR denoland/deno#34395, Deno.listen/connect({transport:"unix"}) also - // requires an --allow-net=unix: grant, in addition to - // --allow-read/--allow-write, to bind/connect the R unix socket. The - // socket path is randomly generated at runtime when --socket isn't - // passed explicitly, so it can't be allow-listed up front; unscoped - // --allow-net is the documented escape hatch for that case. - "start": "deno run --allow-net --allow-read --allow-write --allow-env main.ts", - "dev": "deno run --allow-net --allow-read --allow-write --allow-env --watch main.ts", - // -A is required here (not just --allow-net/read/write/env): on Windows, - // node:net's PipeWrap.bind/listen/connect (used for named pipes) routes + // -A (--allow-all) is required on Windows: R connections default to a + // named pipe there, and node:net's PipeWrap.bind/listen/connect routes // any non-drive-letter path like \\.\pipe\... through Deno's permission // checker's check_special_file, which demands every permission category // (read/write/net/env/sys/run/ffi/import) be fully granted -- i.e. -A - // itself, with no narrower combination accepted. These tasks exercise - // that code path directly (in-process pipe tests) or indirectly (via - // TestServer spawning main.ts), so they need -A on Windows; -A here for - // all platforms keeps the task portable without OS-specific branching. + // itself, with no narrower combination accepted. Separately, on + // Linux/macOS, Deno.listen({transport:"unix"}) requires an unscoped + // --allow-net (since denoland/deno#34395) because the R socket path is + // randomly generated at runtime and can't be allow-listed up front. + // -A satisfies both without OS-specific branching (deno.json tasks + // can't conditionally vary flags by platform). -A is hardcoded into + // these tasks, so passing --tcp here does NOT narrow permissions + // (deno task start -- --tcp 8888 still runs with -A). To actually get + // scoped permissions with --tcp, invoke `deno run` directly instead of + // this task -- see the README's Deno-server quick-start section. + "start": "deno run -A main.ts", + "dev": "deno run -A --watch main.ts", "test": "deno test -A --parallel --ignore=tests/e2e tests/", "test:e2e": "deno test -A tests/e2e/", "test:all": "deno task test && deno task test:e2e"