The headline supply-chain feature. Most npm packages will never declare capabilities themselves — control sits entirely in the host app's package.json. The compiler walks each imported package's HIR, cross-references stdlib call sites against the host's policy, and refuses to compile on violation. Package authors do nothing.
Example host config
{
"perry": {
"permissions": {
"lodash": [],
"axios": ["net:https://api.example.com"],
"left-pad": [],
"@scope/utils": ["crypto"],
"*": ["crypto"]
}
}
}
If lodash's compiled HIR contains a call to fs.readFile, the build fails at the offending line:
lodash calls fs.readFile at node_modules/lodash/template.js:42 but is not granted fs:read in your perry.permissions; either add the capability or replace the dep.
Most real-world npm supply-chain incidents (event-stream, ua-parser-js, several 2023/2024 incidents) would be caught by a default-deny policy on *.
Mechanism
HIR pass per imported source module; cross-reference each stdlib call site against the host's policy; fail compilation on violation. Zero runtime cost — pure compile-time refusal, no FFI-boundary checks emitted.
The user's own root code defaults to * (full capability); per-dep entries override the * default.
Capability tokens (initial set)
fs:read, fs:write, fs:exec
net:fetch, net:listen, net:<host> (literal or glob)
proc:env, proc:exec, proc:argv
crypto
time (Date.now / performance.now — optional, side-channel paranoia)
* for unrestricted
Token taxonomy is extensible as Perry's stdlib grows.
Acceptance
Part of the supply-chain hardening series. The big lever. Host-app-controlled. Zero runtime cost (compile-time analysis only).
The headline supply-chain feature. Most npm packages will never declare capabilities themselves — control sits entirely in the host app's
package.json. The compiler walks each imported package's HIR, cross-references stdlib call sites against the host's policy, and refuses to compile on violation. Package authors do nothing.Example host config
{ "perry": { "permissions": { "lodash": [], "axios": ["net:https://api.example.com"], "left-pad": [], "@scope/utils": ["crypto"], "*": ["crypto"] } } }If
lodash's compiled HIR contains a call tofs.readFile, the build fails at the offending line:Most real-world npm supply-chain incidents (event-stream, ua-parser-js, several 2023/2024 incidents) would be caught by a default-deny policy on
*.Mechanism
HIR pass per imported source module; cross-reference each stdlib call site against the host's policy; fail compilation on violation. Zero runtime cost — pure compile-time refusal, no FFI-boundary checks emitted.
The user's own root code defaults to
*(full capability); per-dep entries override the*default.Capability tokens (initial set)
fs:read,fs:write,fs:execnet:fetch,net:listen,net:<host>(literal or glob)proc:env,proc:exec,proc:argvcryptotime(Date.now / performance.now — optional, side-channel paranoia)*for unrestrictedToken taxonomy is extensible as Perry's stdlib grows.
Acceptance
package.jsonperry.permissions: { "<pkg>": ["cap1", "cap2"], "*": ["default"] }*, configurableperry audit(separate issue) shows which capabilities each dep would need to compile cleanly"net:*.example.com","net:https://api.acme.com/v1/*"lodashthat lands infs.readFileis attributed to whichever package's source module contains the call sitenet:<host>and the URL allowlist enforce the same constraint at different granularities)Part of the supply-chain hardening series. The big lever. Host-app-controlled. Zero runtime cost (compile-time analysis only).