|
| 1 | +--- |
| 2 | +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
| 3 | +# SPDX-License-Identifier: Apache-2.0 |
| 4 | +title: "Incremental Policy Update Reference" |
| 5 | +sidebar-title: "Policy Updates" |
| 6 | +description: "Command grammar, scope requirements, merge behavior, previews, and revision status for incremental sandbox policy updates." |
| 7 | +keywords: "Generative AI, Cybersecurity, Policy, CLI, Incremental Update, Hot Reload" |
| 8 | +position: 4 |
| 9 | +--- |
| 10 | + |
| 11 | +`openshell policy update` merges explicit operations into a sandbox's current |
| 12 | +`network_policies` map. It does not edit filesystem, Landlock, process, or |
| 13 | +middleware sections. Use `policy set` with an exported base policy for those |
| 14 | +broader changes. |
| 15 | + |
| 16 | +This reference describes the command's flags, input formats, and update |
| 17 | +behavior. For the policy lifecycle and editing workflow, see |
| 18 | +[Configure Sandbox Policies](/sandboxes/policies). Examples use `my-sandbox` |
| 19 | +for an existing sandbox; adapt rule names, executable paths, and destinations |
| 20 | +to its current base policy. |
| 21 | + |
| 22 | +## Command Flags |
| 23 | + |
| 24 | +One command can contain several compatible operations. The gateway applies the |
| 25 | +batch atomically and persists at most one new revision. |
| 26 | + |
| 27 | +| Flag | Purpose | |
| 28 | +|---|---| |
| 29 | +| `--add-endpoint <SPEC>` | Add or merge one endpoint and its declared binary scope. Repeat for multiple endpoints. | |
| 30 | +| `--remove-endpoint <HOST:PORT>` | Remove the host and port match from every authored network rule. A multi-port endpoint keeps its other ports. | |
| 31 | +| `--remove-rule <NAME>` | Remove a complete named `network_policies` entry. | |
| 32 | +| `--add-allow <SPEC>` | Append a REST or WebSocket method and path allow rule. | |
| 33 | +| `--add-deny <SPEC>` | Append a REST or WebSocket method and path deny rule. | |
| 34 | +| `--binary <PATH>` | Add binaries with endpoints, or declare the complete stored binary scope for an L7 append. Repeat as needed. | |
| 35 | +| `--rule-name <NAME>` | Name one new endpoint rule or select the existing rule for an L7 append. Required for `--add-allow` and `--add-deny`. | |
| 36 | +| `--any-binary` | Declare that the existing L7 target stores an empty binary scope. Cannot be combined with `--binary`. | |
| 37 | +| `--endpoint-path <PATH>` | Select one existing endpoint by its exact stored path. Pass `''` to select no path. | |
| 38 | +| `--dry-run` | Fetch the current policy and preview the local merge without saving a revision. | |
| 39 | +| `--wait` | Poll for the submitted revision's result. Cannot be combined with `--dry-run`. | |
| 40 | +| `--timeout <SECONDS>` | Set the `--wait` timeout. Defaults to 60 seconds. | |
| 41 | + |
| 42 | +`--any-binary` describes the target rule's stored merge scope. It is not an |
| 43 | +independent claim that every runtime identity mode admits every process. Prefer |
| 44 | +explicit binary paths in authored rules and introductory workflows. |
| 45 | + |
| 46 | +## Endpoint Specification |
| 47 | + |
| 48 | +`--add-endpoint` uses this grammar: |
| 49 | + |
| 50 | +```text |
| 51 | +host:port[:access[:protocol[:enforcement[:options]]]] |
| 52 | +``` |
| 53 | + |
| 54 | +| Segment | Accepted values and behavior | |
| 55 | +|---|---| |
| 56 | +| `host` | Required destination hostname. | |
| 57 | +| `port` | Required integer from 1 through 65535. | |
| 58 | +| `access` | `read-only`, `read-write`, or `full` for inspected endpoints. | |
| 59 | +| `protocol` | `tcp`, `rest`, `websocket`, or `sql`. Use full policy YAML for GraphQL, MCP, and JSON-RPC. | |
| 60 | +| `enforcement` | `enforce` or `audit`. Omission selects audit for inspected requests. | |
| 61 | +| `options` | Comma-separated options listed below. | |
| 62 | + |
| 63 | +Endpoint options are: |
| 64 | + |
| 65 | +| Option | Effect | |
| 66 | +|---|---| |
| 67 | +| `allowed-ip=<CIDR-or-IP>` | Add a destination IP allowance. Repeat the option in the comma-separated list for multiple values. | |
| 68 | +| `request-body-credential-rewrite` | Rewrite supported credential placeholders in inspected REST text bodies. | |
| 69 | +| `websocket-credential-rewrite` | Rewrite supported placeholders in client WebSocket text messages on REST compatibility or WebSocket endpoints. | |
| 70 | +| `allow-uninspected-credentials` | Accept the security-sensitive exposure of provider credentials on an uninspected traffic path. | |
| 71 | + |
| 72 | +Read-only HTTP access for curl: |
| 73 | + |
| 74 | +```shell |
| 75 | +openshell policy update my-sandbox \ |
| 76 | + --rule-name github_readonly \ |
| 77 | + --binary /usr/bin/curl \ |
| 78 | + --add-endpoint api.github.com:443:read-only:rest:enforce \ |
| 79 | + --wait |
| 80 | +``` |
| 81 | + |
| 82 | +Native PostgreSQL access for psql: |
| 83 | + |
| 84 | +```shell |
| 85 | +openshell policy update my-sandbox \ |
| 86 | + --rule-name postgres \ |
| 87 | + --binary /usr/bin/psql \ |
| 88 | + --add-endpoint db.internal.example:5432::tcp \ |
| 89 | + --wait |
| 90 | +``` |
| 91 | + |
| 92 | +Read-only access to an internal HTTP API with an explicit destination IP allowance: |
| 93 | + |
| 94 | +```shell |
| 95 | +openshell policy update my-sandbox \ |
| 96 | + --rule-name private_api \ |
| 97 | + --binary /usr/bin/curl \ |
| 98 | + --add-endpoint 'api.internal.example:443:read-only:rest:enforce:allowed-ip=10.20.0.0/16' \ |
| 99 | + --wait |
| 100 | +``` |
| 101 | + |
| 102 | +The empty access segment before `tcp` is required by the positional grammar. |
| 103 | +`protocol: tcp` rejects access, enforcement, and application-inspection options. |
| 104 | +The standard runtime initializes policy DNS and transparent capture before the |
| 105 | +workload starts, so a live update can add the first TCP endpoint. An alternate |
| 106 | +backend must advertise the required capability and a ready substrate. |
| 107 | + |
| 108 | +An inspected REST or WebSocket endpoint needs an allow shape. For incremental |
| 109 | +creation, supply an access preset. Create the endpoint in one command, then add |
| 110 | +explicit allow or deny rules in a separate command. |
| 111 | + |
| 112 | +## L7 Rule Specification |
| 113 | + |
| 114 | +`--add-allow` and `--add-deny` use this grammar: |
| 115 | + |
| 116 | +```text |
| 117 | +host:port[,port...]:METHOD:path_glob |
| 118 | +``` |
| 119 | + |
| 120 | +The host, complete port set, rule name, complete binary set, and optional |
| 121 | +endpoint path identify one existing REST or WebSocket endpoint. These flags do |
| 122 | +not create an endpoint or change its scope. |
| 123 | + |
| 124 | +Quote specifications that contain `*`, `**`, `?`, or bracket classes. Request |
| 125 | +path globs are not shell path globs. Both `*` and `**` can cross `/` boundaries, |
| 126 | +`?` matches one character, and bracket classes are supported. |
| 127 | + |
| 128 | +The following examples target an existing `github_api` rule whose only binary |
| 129 | +is `/usr/bin/gh` and whose REST endpoint is `api.github.com:443`. An allow append |
| 130 | +permits POST requests to issue-creation paths: |
| 131 | + |
| 132 | +```shell |
| 133 | +openshell policy update my-sandbox \ |
| 134 | + --rule-name github_api \ |
| 135 | + --binary /usr/bin/gh \ |
| 136 | + --add-allow 'api.github.com:443:POST:/repos/*/issues' \ |
| 137 | + --wait |
| 138 | +``` |
| 139 | + |
| 140 | +A deny append blocks POST requests to administration paths on that endpoint: |
| 141 | + |
| 142 | +```shell |
| 143 | +openshell policy update my-sandbox \ |
| 144 | + --rule-name github_api \ |
| 145 | + --binary /usr/bin/gh \ |
| 146 | + --add-deny 'api.github.com:443:POST:/admin/**' \ |
| 147 | + --wait |
| 148 | +``` |
| 149 | + |
| 150 | +For an existing `realtime` rule with only `/usr/bin/node` and a WebSocket |
| 151 | +endpoint at `realtime.example.com:443`, a text-message deny uses the |
| 152 | +`WEBSOCKET_TEXT` method. The path matches the stored upgrade request path, not |
| 153 | +message content: |
| 154 | + |
| 155 | +```shell |
| 156 | +openshell policy update my-sandbox \ |
| 157 | + --rule-name realtime \ |
| 158 | + --binary /usr/bin/node \ |
| 159 | + --add-deny 'realtime.example.com:443:WEBSOCKET_TEXT:/v1/admin/**' \ |
| 160 | + --wait |
| 161 | +``` |
| 162 | + |
| 163 | +Use `--endpoint-path` when a rule contains multiple endpoints with the same host |
| 164 | +and ports. This selector identifies the endpoint. It is separate from the |
| 165 | +request path appended by `--add-allow` or `--add-deny`. |
| 166 | + |
| 167 | +## Complete Scope Requirements |
| 168 | + |
| 169 | +A network rule grants each listed binary access to each listed endpoint and |
| 170 | +port, subject to application rules and other checks. A rule with two binaries |
| 171 | +and two endpoints therefore includes four connection pairs: |
| 172 | + |
| 173 | +| Binary | Endpoint | |
| 174 | +|---|---| |
| 175 | +| `/usr/bin/curl` | `api.example.com:443` | |
| 176 | +| `/usr/bin/curl` | `uploads.example.com:443` | |
| 177 | +| `/usr/bin/python3` | `api.example.com:443` | |
| 178 | +| `/usr/bin/python3` | `uploads.example.com:443` | |
| 179 | + |
| 180 | +If Python should reach only `api.example.com`, put that binary and endpoint in a |
| 181 | +separate rule. Adding Python to the original rule would also authorize the |
| 182 | +uploads endpoint. |
| 183 | + |
| 184 | +The CLI requires complete affected scope for L7 appends and for endpoint merges |
| 185 | +that could create new pairs. For example, an endpoint that stores ports 443 and |
| 186 | +8443 must be targeted with `443,8443`, even if the new method was observed only |
| 187 | +on 443. Likewise, repeat every stored binary path or use `--any-binary` only |
| 188 | +when the stored rule actually has an empty binary list. |
| 189 | + |
| 190 | +Inspect the stored base before composing an append: |
| 191 | + |
| 192 | +```shell |
| 193 | +openshell policy get my-sandbox --base |
| 194 | +``` |
| 195 | + |
| 196 | +Copy the rule name, binary paths, endpoint path, and complete port set from that |
| 197 | +view. Do not derive merge scope from `--full` when provider-owned rules are |
| 198 | +present; those rules are not part of the sandbox base you can incrementally |
| 199 | +edit. |
| 200 | + |
| 201 | +The gateway rejects incomplete or ambiguous declarations before revision |
| 202 | +persistence. Read the reported expected scope and correct the command. Do not |
| 203 | +fill scope mechanically without confirming that the broader effect matches your |
| 204 | +intent. |
| 205 | + |
| 206 | +## Remove Permissions |
| 207 | + |
| 208 | +Preview endpoint removal before applying it because `--remove-endpoint` is not |
| 209 | +scoped by `--rule-name`. It removes the matching host and port from every |
| 210 | +authored network rule: |
| 211 | + |
| 212 | +```shell |
| 213 | +openshell policy update my-sandbox \ |
| 214 | + --remove-endpoint api.example.com:443 \ |
| 215 | + --dry-run |
| 216 | + |
| 217 | +openshell policy update my-sandbox \ |
| 218 | + --remove-endpoint api.example.com:443 \ |
| 219 | + --wait |
| 220 | +``` |
| 221 | + |
| 222 | +To remove one named rule instead: |
| 223 | + |
| 224 | +```shell |
| 225 | +openshell policy update my-sandbox \ |
| 226 | + --remove-rule github_readonly \ |
| 227 | + --wait |
| 228 | +``` |
| 229 | + |
| 230 | +Removing an endpoint from a multi-port endpoint removes only the named port. |
| 231 | +When removal leaves an endpoint with no ports, OpenShell removes that endpoint. |
| 232 | +When a rule loses its final endpoint, OpenShell removes the rule instead of |
| 233 | +retaining an empty entry. Use `--remove-rule` when you intend to remove one |
| 234 | +specific map entry, and inspect the resulting base policy after either command. |
| 235 | + |
| 236 | +## Preview a Merge |
| 237 | + |
| 238 | +`--dry-run` shows the proposed policy without saving it. For example, this |
| 239 | +command previews a request-rule change to an existing `github_api` rule with |
| 240 | +only `/usr/bin/gh` and a REST endpoint at `api.github.com:443`: |
| 241 | + |
| 242 | +```shell |
| 243 | +openshell policy update my-sandbox \ |
| 244 | + --rule-name github_api \ |
| 245 | + --binary /usr/bin/gh \ |
| 246 | + --add-allow 'api.github.com:443:GET:/repos/**' \ |
| 247 | + --dry-run |
| 248 | +``` |
| 249 | + |
| 250 | +The command validates argument shapes, connects to the gateway, fetches the |
| 251 | +current sandbox configuration, and applies the merge locally. It creates no |
| 252 | +revision. An unavailable gateway therefore causes a connection failure. The |
| 253 | +preview does not establish that a later submission will pass every effective |
| 254 | +policy, provider, credential, or runtime validation. |
| 255 | + |
| 256 | +## Merge and Concurrency Behavior |
| 257 | + |
| 258 | +All compatible flags in one command form one atomic batch. They succeed or fail |
| 259 | +together and persist at most one revision. `--add-endpoint` cannot share a batch |
| 260 | +with `--add-allow` or `--add-deny` because their scope flags have different |
| 261 | +meanings. |
| 262 | + |
| 263 | +Concurrent writers use optimistic retry. The gateway reapplies the complete |
| 264 | +operation batch to the latest revision and validates the result again. A no-op |
| 265 | +merge reports an unchanged version and creates no revision. |
| 266 | + |
| 267 | +Rule names identify stored map entries for update and removal. The optional |
| 268 | +human-readable `name` inside a YAML rule is not the `--rule-name` selector when |
| 269 | +the two differ. Use the map key shown by `policy get --base`. |
| 270 | + |
| 271 | +Incremental updates are unavailable while a gateway-global policy is active. |
| 272 | +Delete the global override through the operator workflow before changing a |
| 273 | +sandbox policy. |
| 274 | + |
| 275 | +## Wait and Status Semantics |
| 276 | + |
| 277 | +Without `--wait`, success means the gateway accepted the submission. It does not |
| 278 | +mean the supervisor activated it. |
| 279 | + |
| 280 | +With `--wait`, the CLI polls until it observes a terminal outcome or timeout. A |
| 281 | +zero exit can represent a loaded revision, a no-op, or a revision superseded by |
| 282 | +another update. Always inspect current state before relying on the change: |
| 283 | + |
| 284 | +```shell |
| 285 | +openshell policy list my-sandbox |
| 286 | +openshell policy get my-sandbox --full |
| 287 | +``` |
| 288 | + |
| 289 | +A wait timeout means the CLI stopped polling. It does not establish whether the |
| 290 | +revision later loaded or failed. Inspect revision status and sandbox readiness |
| 291 | +before resubmitting, because an automatic retry can race with a late result. |
| 292 | + |
| 293 | +## Full Replacement |
| 294 | + |
| 295 | +`openshell policy set` handles middleware changes, advanced protocol shapes, and complete |
| 296 | +replacement. Start from the current base and preserve sections you do not intend |
| 297 | +to change. Follow [Replace the |
| 298 | +Policy](/sandboxes/policies#replace-the-policy) to prepare an editable |
| 299 | +YAML file using the CLI's readable output. |
| 300 | + |
| 301 | +For automation, the following alternative uses `jq` on the host to extract the |
| 302 | +base policy without display metadata or provider-owned rules: |
| 303 | + |
| 304 | +```shell |
| 305 | +set -o pipefail |
| 306 | +openshell policy get my-sandbox --base --output json \ |
| 307 | + | jq -e '.policy' > base-policy.json |
| 308 | +``` |
| 309 | + |
| 310 | +Edit `base-policy.json`, then submit the complete policy: |
| 311 | + |
| 312 | +```shell |
| 313 | +openshell policy set my-sandbox --policy base-policy.json --wait |
| 314 | +``` |
| 315 | + |
| 316 | +Refer to [Inspect the Current Policy](/sandboxes/policies#inspect-the-current-policy) |
| 317 | +for effective export, and use |
| 318 | +[Troubleshoot Sandbox Policies](/sandboxes/troubleshoot-policies) when submission |
| 319 | +and activation results differ. |
0 commit comments