OpenClaw EnterpriseDOCSGitHub

Console Agent sharing and removal

Overview

Trace how Agent detail shares one Agent with an existing Principal and removes one explicit Agent binding. The trace starts when Agent detail's Access tab mounts the sharing panel and stops at policy configuration; native gateway admission and runtime readiness belong to the native admin flow. See the parent flow and the sharing reference.

Entry Points

Flow

graph TD
  A["Access tab mounts sharing panel"] --> B["Read Namespace Roles and bindings"]
  B -->|policy denied| K["Keep other Agent panels available"]
  B --> C["Submit share for an existing Principal"]
  C --> D["Reread policy; find or create Namespace read Role"]
  D --> E["Bind Namespace read to the exact Namespace"]
  E --> F["Find or create Agent read/administer Role"]
  F --> G["Bind it to the selected Agent"]
  C --> P["Hold version and page changes"]
  G -->|confirmed steps| H["Show progress and direct grants"]
  H --> U["Release the hold"]
  C -->|uncertain| Q["Block writes until policy refresh"]
  H --> R["Remove one selected Agent binding"]

Execution Trace

1. Mount the panel and read policy

apps/controller/src/console/agents/access.mjs:renderAgentAccess

The Access tab mounts sharing independently of version/configuration reads and native admission. Agent detail leaves the tab out when the console's Installation-access answer already showed the person lacks Installation administration: the policy endpoints require it and the API audits each denial. The panel reads the selected Namespace's existing /iam/roles and /iam/access-bindings endpoints, one list at a time, so a person whose administration ended after the page loaded gets one audited 403. A policy 403 hides the panel and the tab, opens Configuration, and leaves the other panels usable; a current 401 retains global session expiry. The panel is built once per page. A return to the tab reads the policy again, with the controls disabled, so a 403 after administration ends takes the same path. Refresh sharing first rereads the Agent's and the Namespace's permission answers, which are not audited. Agent detail follows the Agent answer. When the console already settled that the person lacks Installation administration, the policy is read again only if the Namespace answer reports it, so administration granted again applies without a page reload; otherwise the tab gives way to Configuration with no audited read.

2. Serialize Role and binding writes

apps/controller/src/console/agents/access.mjs:matchesRole

Submission rereads policy, finds or creates an immutable Role by exact Namespace and permissions, then binds Namespace read to the exact Namespace. Only after that response does it find or create the exact Agent read/administer Role and bind it to the selected Agent. The panel rejects a subject that is not a prn_ Principal ID, such as an email, before any request, and reports a 404 during a share as an unknown Principal ID. The server validates the supplied subject and resource on each write. Confirmed progress survives later failure; unknown results disable mutations until an explicit current-policy refresh. Readback is configuration evidence, not a historical receipt, and never triggers a write.

3. Hold the page while writes run

apps/controller/src/console/agents/detail.mts:renderAgentDetail

Each request belongs to the page and stops when the page goes. So while a share or removal runs, the panel's onWriting callback has Agent detail hold version and page changes, as it does for a draft's writes. The held changes are the Versions rail, Create new version, version links, ← Agents, the sidebar, the Namespace selector, Back and Forward, the header's Refresh, and the page's own reloads. The page shows "This page stays open until sharing finishes." Tabs still switch. After Delete replaces the page with its deletion row, a write still in flight holds the page and its note shows above that row. The page's setWriteInFlight check, which also covers a draft's writes, makes beforeunload in console.mjs ask before a reload or tab close. The hold ends when the last request settles. At worst that is five requests of up to 15 s each, about 75 s. Session expiry and sign-out still leave.

4. Remove one explicit binding

apps/controller/src/console/agents/access.mjs:renderAgentAccess

Removal addresses only the selected Agent binding. The request client's existing success envelope handling also accepts the API's empty 204 deletion response. The panel retains discovery grants and explains other possible access sources.

Debugging and Verification

Search documentation