The limitation: grants are per package
A single npm package can register many node types, but all of them share the same package name in the call stack. Sentinel cannot distinguishmy-package/nodes/foo.js from my-package/nodes/bar.js at the frame level — both resolve to my-package. This is intentional: the package is the unit you install, audit, and sign off on.
The consequence is that you cannot grant network:http to one node type and deny it to another if both live in the same package. Any grant you add applies to the entire package.
The solution: publish each trust boundary as its own scoped package
If you need different capability levels for different node types, publish each trust boundary as its own scoped package and group them under a parent that users install as a single dependency.Create a parent package as a dependency aggregator
The parent package has no node code of its own — it exists only to pull in the child packages as a single user-facing install target.
Give each child package its own npm identity and node-red field
Each child package has its own
node-red field so Node-RED discovers it directly, and its own npm name so Sentinel can grant it capabilities independently.How npm hoisting makes this transparent to users
When a user runsnpm install @my-company/nodes, npm (v7+) hoists the children to the top-level node_modules/. Node-RED discovers them directly because each has its own node-red field. Sentinel sees each child’s package name independently, so grants can be applied at exactly the right granularity.
This pattern is already established in the Node-RED ecosystem.
@node-red/nodes, @node-red/runtime, and @node-red/editor-api are all separate packages under the @node-red namespace, each with a distinct npm identity and independent install footprint.Full example
Parentpackage.json:
package.json (one per trust boundary):
settings.js with per-scope grants: