ferrijs-permissions 0.1.0

The capability model behind the ferrijs runtime: what a script may read, write, reach on the network, see in the environment and learn about the host.
Documentation

What a script is allowed to do, decided in one place.

QuickJS itself has no ambient authority: a fresh realm can compute and nothing else. Everything a script can reach beyond that — a file, a socket, an environment variable, a fact about the host — is a capability the embedding runtime installed, and every one of those installs asks this crate before acting. Deny is the default for each kind; a host grants the least it can.

The model is Deno's and Node's, which are the two that survived:

  • [Permissions] is the policy: five kinds ([Kind]), each a [Allow] of none, everything, or a list, plus a [Deny] list per kind that overrides the allow (read: all, deny read: /etc).
  • [Container] holds the policy for ONE realm for the realm's whole life. It can only ever narrow ([Container::revoke], irreversible, like Node's process.permission.drop and Deno's revoke). It also carries a [Hook] the host may install to grant on demand (a prompt) and an [Audit] that sees every decision.

What the model deliberately does NOT have is a dynamic scope: no "narrow the policy around this call and carry it into the callbacks it registers". That is Java's stack-inspection Security Manager, removed by JEP 411 as brittle, slow, and impossible to keep complete across an API surface. A host with two trust levels runs them in two realms, each with its own container, the way workerd gives each isolate its own bindings.

Paths are checked twice: as written, after lexical normalisation, and as the filesystem will actually resolve them, after following every symlink in the longest existing prefix. Both must fall under a granted root, so a link planted inside an allowed directory cannot point out of it. Node documents the opposite (links are followed out) as a hazard the operator must avoid; this does not leave it to them.