Skip to content

Permissions

Workers run your task code, and the permission option controls what that code can access at runtime. You can restrict file access, network connections, environment variables, imports, and subprocesses.

Set the policy when you create the pool:

import { createPool, isMain, task } from "knitting";
export const work = task({
f: async (x: number) => x * 2,
});
if (isMain) {
using pool = createPool({
threads: 2,
permission: { mode: "strict" },
})({ work });
console.log(await pool.call.work(21)); // 42
}

There are three ways to configure permissions:

  • Leave permission out. Knitting uses the strict defaults and allows imports, including web imports.
  • Use permission: {} or permission: { mode: "strict" }. Knitting uses the conservative strict defaults and lets you add only the access your tasks need.
  • Use permission: "unsafe". Knitting disables runtime permission flags and removes inherited Node permission flags from the worker.

For most applications, keep strict mode and add a small allow-list. Use "unsafe" only when a dependency needs access that the runtime cannot express with the strict policy.

The console option controls whether worker console.* calls are forwarded to the host. It defaults to false in strict mode and true in unsafe mode.

createPool({ permission: { mode: "strict", console: true } })({ work });
createPool({ permission: "unsafe" })({ work });

Object mode lets you grant access one capability at a time. Anything you do not list stays denied:

createPool({
permission: {
mode: "strict",
allowImport: true, // allow task-module imports
read: ["./data"], // path allow-list (or `true` for all)
write: ["./out"],
net: ["api.example.com"], // host allow-list (or `true` for all)
env: { allow: ["NODE_ENV"] },
run: ["git"], // subprocess allow-list
console: true,
},
})({ work });
FieldControls
read / writeFilesystem allow-lists. true means unrestricted access.
denyRead / denyWriteExplicit denials applied after the allow-list.
net / denyNetNetwork host allow- and deny-lists.
allowImportModules the worker may import. true allows all imports.
envEnvironment access through { allow, deny, files }.
run / denyRunSubprocess execution allow- and deny-lists.
consoleWhether worker console.* output reaches the host.

Knitting uses each runtime’s own permission mechanism, so the exact coverage varies by runtime (see how each runtime enforces it). Permissions are a guardrail, not a complete boundary for untrusted code. For stronger isolation, combine them with process workers.

Strict mode starts with a conservative policy:

  • reads and writes are limited to the current working directory (cwd);
  • writes to node_modules are denied;
  • sensitive files and directories are denied, including .env, .git, .npmrc, .docker, .secrets, ~/.ssh, ~/.gnupg, ~/.aws, ~/.azure, ~/.config/gcloud, and ~/.kube;
  • sensitive POSIX paths are denied, including /proc, /sys, /dev, and /etc;
  • deno.lock and bun.lock* can still be read.

permission: "unsafe" turns off runtime permission flags and removes inherited Node permission flags from the worker’s execArgv.

Knitting translates the same policy into each runtime’s native permission system. The result is slightly different on Node.js, Deno, and Bun.

Node workers receive --permission or --experimental-permission, along with the relevant allow flags:

  • --allow-fs-read
  • --allow-fs-write
  • --allow-worker
  • --allow-child-process
  • --allow-addons
  • --allow-wasi

Node’s worker flags are allow-list based. That means Knitting cannot represent every protocol-level deny-list rule as a native Node flag.

When enabled, Deno workers receive a Worker.deno.permissions policy.

Knitting applies it only when one of these is true:

  • it detects --unstable-worker-options (using a Linux /proc check); or
  • KNITTING_DENO_WORKER_PERMISSIONS=1 is set.

Bun does not currently provide worker permission flags. Knitting accepts the permission values for API compatibility, but Bun cannot enforce them through runtime flags yet.

If a task needs to start another process, object mode also supports runtime-specific overrides:

  • node.allowChildProcess?: boolean
  • deno.allowRun?: boolean — a legacy option, superseded by the top-level run allow-list.

Both default to false in strict mode. Prefer the top-level run allow-list when you need to permit specific commands.