[ Product · Runtime firewall ]
Android-style permissions for node_modules.
Your phone asks before an app reads your contacts. Node never asks before a package reads your secrets. Capwall gives every dependency an explicit grant and stops anything outside it, while your own code runs untouched.
# generated by capwall init, reviewed in git packages: stripe: allow: [env.read, http.request] pg: allow: [net.connect, dns.resolve] chalk: allow: [] some-package: deny: [child_process]
[ Before / after ]
Same package. Different powers.
| A package deep in node_modules wants to… | Plain Node | With Capwall |
|---|---|---|
Read STRIPE_SECRET_KEY | ✕ Allowed, silently | ✓ Only if granted env.read |
| Spawn a shell | ✕ Allowed, silently | ✓ Only if granted child_process |
| Phone home to an unknown server | ✕ Allowed, silently | ✓ Only if granted net.connect |
| Rewrite files on disk | ✕ Allowed, silently | ✓ Only if granted fs.write |
| Run code from a string | ✕ Allowed, silently | ✓ Only if granted dynamic-code |
[ 11 capabilities ]
Every dangerous power, named.
Grant them per package with capwall allow, revoke with capwall deny.
[ Under the hood ]
Three layers. One gate.
ESM imports, gated on resolve
A node --import hook sees every import before it resolves and knows which package asked.
CommonJS goes through the same check
Module._load is patched so require() of child_process, net or fs is attributed and checked too.
process.env and fetch, wrapped
Globals a package can reach without importing anything are guarded, so a stray env read is still caught.
ERR_CAPWALL_CAPABILITY_DENIED and the process dies. No silent leak.[ Attestation · in-toto + DSSE ]
Prove the lock you scanned is the lock you ran.
Every pipeline step signs an Ed25519 envelope recording its materials and products.
$ capwall attest verifyFails unless the lock the runtime enforced is bit-identical to the one the scanner produced. Runtime telemetry is recorded as a runtime bill of materials.
[ Try it ]
Watch it block a malicious package live.
$ cd examples/vulnerable-app
$ capwall run -- node app.js
ERR_CAPWALL_CAPABILITY_DENIED