The permission-model posture

StashJS is designed to run cleanly under the Node permission model: mkdir -p .stash && node --permission --allow-fs-read=. --allow-fs-write=./.stash app.js. It is a process-level filesystem allowlist, not per-module isolation: the read grant spans the app directory (Node loads its module graph from disk) while only the store is writable, so a compromised dependency cannot reach outside the grant to the wider filesystem. It is the store's crypto-agnostic argument enforced by the runtime instead of by discipline.

What the library gives up for it

No child processes, no worker threads, no native addons, no WASI -- each would need its own grant and each widens the sandbox. No file descriptors as inputs, ever: existing fds bypass the model, so the API accepts paths only. And no node:sqlite index, even though it is a builtin: the permission model does not gate filesystem access made through it, so the one builtin that could plausibly earn its way in is the one that would quietly disable the sandbox.

What the sandbox does not do

The permission model follows symlinks out of granted paths, so the grant alone does not confine a store whose root has been seeded with a planted link -- containment stays the disk backend's own job, asserting every resolved path is still under the store root. And --permission does not gate the network: the actual guarantee is that StashJS opens no sockets at all.

Fail loudly when the grant is wrong

The library never probes process.permission.has() to branch behavior. If the grant is wrong, the ERR_ACCESS_DENIED surfaces -- a library that silently degrades when sandboxed is worse than one that fails loudly.