docs(gateway): clarify dynamic operator scopes (#122931)

This commit is contained in:
Peter Steinberger
2026-08-12 19:23:54 -07:00
committed by GitHub
parent 9992b893e7
commit 6076efc968
4 changed files with 34 additions and 2 deletions
+1
View File
@@ -46,6 +46,7 @@ These commands drive the gateway-owned `node.pair.*` store, separate from device
- commandless request: `operator.pairing`
- ordinary node commands: `operator.pairing` + `operator.write`
- admin-sensitive commands (`system.run`, `system.run.prepare`, `system.which`, `browser.proxy`, `browser.proxy.upload.v1`, `fs.listDir`, and `system.execApprovals.get/set`): `operator.pairing` + `operator.admin`
- These requirements classify node commands relayed through `node.invoke`. The top-level Gateway `fs.listDir` RPC needs `operator.write` for workspace-contained host browsing and `operator.admin` when `nodeId` is present.
- `remove` scope: `operator.pairing` can remove non-operator node rows; a device-token caller revoking its own node role on a mixed-role device additionally needs `operator.admin`.
## Invoke
+25 -2
View File
@@ -79,6 +79,7 @@ The result is used for both `hello.auth.scopes` and Gateway method
authorization. Identity grants are session-only: they do not create or modify
pairing records or request a device scope upgrade. Token, password, and no-auth
connections carry no verified identity and receive no grant.
Identity grants apply only to `operator`-role connections; `node`-role connections never receive them.
## Method scope is only the first gate
@@ -89,8 +90,18 @@ dispatch so authorization failures have one canonical structured response:
- `agent` needs `operator.write` for ordinary turns and `operator.admin` for
`/new` or `/reset` session lifecycle commands.
- `node.invoke` needs `operator.write` for ordinary relay commands and
`operator.admin` for `browser.proxy`, `browser.proxy.upload.v1`, `fs.listDir`,
and `terminal.upload`.
`operator.admin` when relaying `browser.proxy`, `browser.proxy.upload.v1`,
`fs.listDir`, or `terminal.upload` to a node.
- The top-level `fs.listDir` RPC needs `operator.write` for Gateway-host
requests and `operator.admin` when `nodeId` targets a node. Its handler limits
non-admin Gateway-host browsing to configured agent workspaces.
- `sessions.create` needs `operator.write` for ordinary creation, including a
`projectId`, and `operator.admin` for incognito sessions or any `execNode`
request. For non-admin callers, the handler limits `cwd` to configured agent
workspaces; `projectId` cannot be combined with `cwd` or `execNode`.
- `worktrees.branches` needs `operator.write`. Its handler limits non-admin
callers to workspace-contained paths or registered-project roots; other host
paths require `operator.admin`.
- `talk.config` needs `operator.read`; `includeSecrets: true` also needs
`operator.talk.secrets`.
- `talk.client.*`, `talk.session.*`, `talk.speak`, and `talk.mode` need
@@ -101,6 +112,15 @@ dispatch so authorization failures have one canonical structured response:
Persisting a selected model as the configured agent default is also
admin-only.
Project RPCs use these scopes:
| Method | Required scope and additional gate |
| -------------------------------------- | --------------------------------------------------------------------------------------------- |
| `projects.list` | `operator.read`; only callers satisfying `operator.write` receive `repoRoot` and `originUrl`. |
| `projects.add` | `operator.write` and the `controlPlaneWrite` method flag. |
| `projects.register`, `projects.remove` | `operator.admin`. |
| `projects.searchRemote` | `operator.read`. |
Some handlers then apply stricter checks based on the concrete thing being
approved or mutated:
@@ -191,6 +211,9 @@ command list:
| ordinary node commands | `operator.pairing` + `operator.write` |
| `system.run`, `system.run.prepare`, `system.which`, `browser.proxy`, `browser.proxy.upload.v1`, `fs.listDir`, or `system.execApprovals.get/set` | `operator.pairing` + `operator.admin` |
Here, `fs.listDir` is the node command declared for relay through `node.invoke`,
not the top-level Gateway RPC described above.
Approving a node declaration records its command surface. For `computer.act`,
the node advertises that surface only after Computer Control is enabled locally;
once the pairing update is approved, invoking it through `node.invoke` requires
+4
View File
@@ -116,6 +116,10 @@ Notes:
`system.which`, `browser.proxy`, `browser.proxy.upload.v1`, `fs.listDir`,
or `system.execApprovals.get/set`: `operator.pairing` + `operator.admin`
Here, `fs.listDir` is the node command relayed through `node.invoke`. The
top-level Gateway `fs.listDir` RPC needs `operator.write` for
workspace-contained host browsing and `operator.admin` when `nodeId` is present.
<Warning>
Node pairing approval records the trusted capability surface. It does **not** pin the live node command surface per node.
+4
View File
@@ -400,6 +400,10 @@ method scope (`operator.pairing`), based on the pending request's declared
| ordinary commands | `operator.pairing` + `operator.write` |
| includes `system.run`, `system.run.prepare`, `system.which`, `browser.proxy`, `browser.proxy.upload.v1`, `fs.listDir`, or `system.execApprovals.get/set` | `operator.pairing` + `operator.admin` |
In this table, `fs.listDir` is the node command relayed through `node.invoke`.
The top-level Gateway `fs.listDir` RPC needs `operator.write` for
workspace-contained host browsing and `operator.admin` when `nodeId` is present.
### Caps/commands/permissions (node)
Nodes declare capability claims at connect time: