Skip to main content
By default, Sentinel blocks every privileged operation for every third-party package. A package that needs a capability must be explicitly granted it in settings.js.

The sentinel.allow map

Grants live in the sentinel.allow map inside settings.js. Each key is an npm package name exactly as it appears in node_modules/; the value is an array of capability strings.
// settings.js
module.exports = {
    sentinel: {
        allow: {
            "my-custom-node": ["registry:register"],
        },
    },
};
The unit of trust is the npm package, not the individual node type. A single npm package can register many node types, but all of them share the same package name in the call stack. Sentinel cannot distinguish my-package/nodes/foo.js from my-package/nodes/bar.js at the frame level — both resolve to my-package.
Sentinel only applies capability checks to packages loaded from the Node-RED userDir ({userDir}/node_modules/ or {userDir}/nodes/). Node-RED’s own built-in nodes (inject, debug, function, http request, etc.) are part of the Node-RED installation itself and live outside the userDir, so Sentinel never gates them. You only need to add grants for third-party packages that users install into their userDir.

registry:register — required for every node package

Every node package must be granted registry:register so Sentinel allows it to call RED.nodes.registerType() at startup. Without this grant, Sentinel blocks the call, the node type is never registered, and Node-RED logs “Waiting for missing types” indefinitely.
// settings.js — minimal grant for a node package that needs no other privileges
module.exports = {
    sentinel: {
        allow: {
            "my-custom-node": ["registry:register"],
        },
    },
};
Plugins registered via the node-red.plugins key in package.json do not call registerType and do not need registry:register.

How Sentinel identifies the calling package

Sentinel identifies the calling package at runtime by walking the call stack and extracting the node_modules/<package> segment from the nearest frame that does not belong to Node-RED or Sentinel itself. The match is against the npm package name exactly as it appears on disk.

Common grants

A node that reads this.credentials directly in its own constructor or message handler.
// settings.js
module.exports = {
    sentinel: {
        allow: {
            // A node that reads its own credentials (this.credentials) directly.
            // See "Credential access patterns" for config-node and cross-node cases.
            "node-red-contrib-influxdb": ["registry:register", "node:credentials:read"],
        },
    },
};
A flow-auditing plugin that needs to inspect the runtime topology.
// settings.js
module.exports = {
    sentinel: {
        allow: {
            "node-red-contrib-flow-auditor": [
                "registry:register",
                "node:list",       // RED.nodes.eachNode()
                "node:wires:read", // read node.wires (output topology)
                "flows:read",      // RED.runtime.flows.getFlows() / getFlow(id)
            ],
        },
    },
};
A tracing or APM plugin that hooks the message pipeline. hooks:on-send fires before routing; hooks:post-deliver fires after delivery.
// settings.js
module.exports = {
    sentinel: {
        allow: {
            "node-red-contrib-tracer": ["registry:register", "hooks:on-send", "hooks:post-deliver"],
        },
    },
};
A node that registers its own admin UI routes. http:admin covers httpAdmin; http:node covers httpNode.
// settings.js
module.exports = {
    sentinel: {
        allow: {
            "node-red-contrib-dashboard": ["registry:register", "http:admin", "http:node"],
        },
    },
};
A node that genuinely needs to run OS commands.
// settings.js
module.exports = {
    sentinel: {
        allow: {
            "node-red-contrib-exec": ["registry:register", "process:exec"],
        },
    },
};
A node that reads files from disk, for example a CSV reader.
// settings.js
module.exports = {
    sentinel: {
        allow: {
            "node-red-contrib-file-in": ["registry:register", "fs:read"],
        },
    },
};
A node that makes outbound HTTP calls. network:http covers http.request/https.request. Add specific URLs to sentinel.networkPolicy.allowlist to restrict further.
// settings.js
module.exports = {
    sentinel: {
        allow: {
            "node-red-contrib-http-request": ["registry:register", "network:http"],
        },
    },
};
A plugin (no node types) that listens to runtime events. Plugins registered via the node-red.plugins key in package.json do not call registerType, so no registry:register is needed.
// settings.js
module.exports = {
    sentinel: {
        allow: {
            "node-red-contrib-audit-logger": ["events:listen"],
        },
    },
};

The blocked-operation warning format

When Sentinel blocks a call, it prints a warning to the Node-RED console that tells you exactly which grant to add:
[@allanoricil/nrg-sentinel] BLOCKED fs.readFileSync() — my-custom-node lacks fs:read
  Call stack:
    at Object.<anonymous> (/data/node_modules/my-custom-node/index.js:42:5)
  To allow, add to settings.js:
    sentinel: { allow: { "my-custom-node": ["fs:read"] } }

Using .sentinel-grants.json for UI-managed grants

Sentinel also looks for a .sentinel-grants.json file in the userDir (next to settings.js). This file is the backing store for the Sentinel editor panel — the Node-RED UI exposes admin API routes that read and write it directly, so operators can manage grants through the browser without touching settings.js. This separation is intentional. In hardened deployments settings.js is mounted read-only so it cannot be modified at runtime. .sentinel-grants.json lives in the writable userDir, giving the UI panel a place to persist changes. The two files have different ownership: settings.js is managed by deployment tooling; .sentinel-grants.json is managed by the UI. The file has two top-level sections:
{
    "packages": {
        "node-red-contrib-my-package": ["registry:register", "node:credentials:read"]
    },
    "nodeTypes": {
        "my-config-node": {
            "node:credentials:read": ["node-red-contrib-trusted-consumer"]
        }
    }
}
  • packages — dynamic caller grants, equivalent to settings.sentinel.allow. Entries here are merged with settings.js at runtime.
  • nodeTypes — per-node-type target permissions. Keyed on the target node’s type, each entry lists which caller packages are allowed to perform a given operation on nodes of that type.

Build docs developers (and LLMs) love