openclaw - 💡(How to fix) Fix [Bug]: canvas.* commands declared by macOS node are blocked by gateway platform allowlist (sister bug to #57169)

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

The macOS node app advertises the canvas capability and declares 8 canvas.* commands in its connect payload, but the gateway's PLATFORM_DEFAULTS.macos allowlist (in src/gateway/node-command-policy.ts) does not contain a single canvas.* entry. As a result, every node.invoke call to a canvas.* command on a macOS node is rejected with:

node command not allowed: "canvas.present" is not in the allowlist for platform "macOS 26.5.0"

…even though the node clearly declares the command, the gateway UI shows it under the node's command list, and the runtime tool layer (tool-P9jDUTj2.js) knows how to invoke it. The information is present at every layer except the policy gate.

This is the same architectural pattern as #57169 (macOS screen.record), but for an entire command family.

Error Message

node.invoke resolves the platform allowlist and falls through to the standard rejection at src/gateway/server-methods/nodes.ts (the command not allowlisted branch), producing the user-visible error message from buildNodeCommandRejectionHint in server-methods-*.js: 3. Observe the rejection error above.

Root Cause

The macOS node app advertises the canvas capability and declares 8 canvas.* commands in its connect payload, but the gateway's PLATFORM_DEFAULTS.macos allowlist (in src/gateway/node-command-policy.ts) does not contain a single canvas.* entry. As a result, every node.invoke call to a canvas.* command on a macOS node is rejected with:

node command not allowed: "canvas.present" is not in the allowlist for platform "macOS 26.5.0"

…even though the node clearly declares the command, the gateway UI shows it under the node's command list, and the runtime tool layer (tool-P9jDUTj2.js) knows how to invoke it. The information is present at every layer except the policy gate.

This is the same architectural pattern as #57169 (macOS screen.record), but for an entire command family.

Fix Action

Fix / Workaround

Workaround (what every operator currently has to do)

Patch gateway.nodes.allowCommands to explicitly include every canvas command:

Code Example

node command not allowed: "canvas.present" is not in the allowlist for platform "macOS 26.5.0"

---

{
  "displayName": "<mac-node>",
  "platform": "macOS 26.5.0",
  "version": "2026.5.22",
  "caps": ["camera", "canvas", "location", "screen"],
  "commands": [
    "camera.list",
    "canvas.a2ui.push", "canvas.a2ui.pushJSONL", "canvas.a2ui.reset",
    "canvas.eval", "canvas.hide", "canvas.navigate",
    "canvas.present", "canvas.snapshot",
    "location.get", "screen.snapshot",
    "system.notify", "system.run", "system.which"
  ]
}

---

return `node command not allowed: "${command}" is not in the allowlist for platform "${platform}"`;

---

node.invoke node=<mac-node> invokeCommand="canvas.present" invokeParamsJson='{"url": "https://example.com"}'

---

"gateway": {
  "nodes": {
    "allowCommands": [
      "canvas.a2ui.push", "canvas.a2ui.pushJSONL", "canvas.a2ui.reset",
      "canvas.eval", "canvas.hide", "canvas.navigate",
      "canvas.present", "canvas.snapshot"
      // ...plus everything else outside PLATFORM_DEFAULTS
    ]
  }
}
RAW_BUFFERClick to expand / collapse

[Bug]: canvas.* commands declared by macOS node are blocked by gateway platform allowlist (sister bug to #57169)

Summary

The macOS node app advertises the canvas capability and declares 8 canvas.* commands in its connect payload, but the gateway's PLATFORM_DEFAULTS.macos allowlist (in src/gateway/node-command-policy.ts) does not contain a single canvas.* entry. As a result, every node.invoke call to a canvas.* command on a macOS node is rejected with:

node command not allowed: "canvas.present" is not in the allowlist for platform "macOS 26.5.0"

…even though the node clearly declares the command, the gateway UI shows it under the node's command list, and the runtime tool layer (tool-P9jDUTj2.js) knows how to invoke it. The information is present at every layer except the policy gate.

This is the same architectural pattern as #57169 (macOS screen.record), but for an entire command family.

Affected version

  • openclaw 2026.5.22 (gateway and macOS node app), reproduced live on 2026-05-26
  • macOS node version ui v2026.5.22, platform string "macOS 26.5.0"

Evidence

1) Node declares the commands

Confirmed via openclaw nodes list --json immediately after a fresh node-host (re)connect:

{
  "displayName": "<mac-node>",
  "platform": "macOS 26.5.0",
  "version": "2026.5.22",
  "caps": ["camera", "canvas", "location", "screen"],
  "commands": [
    "camera.list",
    "canvas.a2ui.push", "canvas.a2ui.pushJSONL", "canvas.a2ui.reset",
    "canvas.eval", "canvas.hide", "canvas.navigate",
    "canvas.present", "canvas.snapshot",
    "location.get", "screen.snapshot",
    "system.notify", "system.run", "system.which"
  ]
}

The gateway UI's "Nodes" panel renders the same list — i.e. the gateway knows.

2) Policy source has no canvas entries

Grepping the bundled gateway source for any canvas-related token under src/gateway/node-command-policy.ts returns zero hits. PLATFORM_DEFAULTS.macos is composed of CAMERA_COMMANDS, LOCATION_COMMANDS, DEVICE_COMMANDS, CONTACTS_COMMANDS, CALENDAR_COMMANDS, REMINDERS_COMMANDS, PHOTOS_COMMANDS, MOTION_COMMANDS, SYSTEM_COMMANDS, SCREEN_COMMANDS — no canvas group.

(Same code area as #57169 / node-command-policy.ts:15. The clawsweeper review on that issue identified the file at commit 0a50cbdf345d.)

3) Rejection path

node.invoke resolves the platform allowlist and falls through to the standard rejection at src/gateway/server-methods/nodes.ts (the command not allowlisted branch), producing the user-visible error message from buildNodeCommandRejectionHint in server-methods-*.js:

return `node command not allowed: "${command}" is not in the allowlist for platform "${platform}"`;

Reproduction

  1. Pair the macOS node app on a Mac (no special setup; canvas cap is always advertised on macOS).
  2. From any agent, attempt:
    node.invoke node=<mac-node> invokeCommand="canvas.present" invokeParamsJson='{"url": "https://example.com"}'
  3. Observe the rejection error above.
  4. Confirm openclaw nodes list --json shows canvas.present in the node's commands array, and the gateway Web UI renders it as a chip under the node card.

Workaround (what every operator currently has to do)

Patch gateway.nodes.allowCommands to explicitly include every canvas command:

"gateway": {
  "nodes": {
    "allowCommands": [
      "canvas.a2ui.push", "canvas.a2ui.pushJSONL", "canvas.a2ui.reset",
      "canvas.eval", "canvas.hide", "canvas.navigate",
      "canvas.present", "canvas.snapshot"
      // ...plus everything else outside PLATFORM_DEFAULTS
    ]
  }
}

This works, but triggers openclaw security audit to flag gateway.nodes.allow_commands_dangerous (severity: critical when gateway is exposed remotely, which is the default in any Tailscale-fronted setup). So operators get yelled at for filling in the gap the gateway left.

Expected behavior

One of:

  • Preferred: add a CANVAS_COMMANDS group and include the safe subset in PLATFORM_DEFAULTS.macos. Suggested split:

    CommandRecommended classificationReasoning
    canvas.presentsafe-defaultopens a local UI surface with a URL; no data exfiltration
    canvas.navigatesafe-defaultnavigates the existing canvas; no new privilege
    canvas.hidesafe-defaultcloses the canvas
    canvas.a2ui.push / pushJSONL / resetsafe-defaultpushes A2UI events to the local UI
    canvas.snapshotCANVAS_DANGEROUS_COMMANDS, opt-incaptures rendered display contents — comparable to screen.snapshot (which IS default-allowed today, so arguably canvas.snapshot could be too — open question)
    canvas.evalCANVAS_DANGEROUS_COMMANDS, opt-inexecutes arbitrary JS in the canvas context
  • Alternative: keep all canvas.* opt-in but improve the rejection message to name the operator action concretely (e.g. "add canvas.present to gateway.nodes.allowCommands") and link to docs explaining the safety classification. Current message ("not in the allowlist for platform") is opaque to anyone who hasn't read the source.

Wider architecture observation

This is now the second documented case (#57169 being the first) of macOS-only commands being declared by the node app but missing from PLATFORM_DEFAULTS.macos. Likely candidates for a follow-up audit:

  • New command families added to the macOS app since PLATFORM_DEFAULTS was last reviewed
  • The drift between apps/macos/Sources/OpenClaw/NodeMode/MacNodeModeCoordinator.swift (declares) and src/gateway/node-command-policy.ts (gates)

A maintainable fix would be a CI test that asserts every command declared by any node-app for a canonical platform is either in PLATFORM_DEFAULTS[platform] or in *_DANGEROUS_COMMANDS (forcing a deliberate classification decision per command, never silent drift).

Setup that reproduces this

  • Mac mini M4 gateway host, openclaw 2026.5.22, multi-agent (~20 agents), launchd-managed on 127.0.0.1:18789
  • MacBook Pro M4 as the macOS node, openclaw-macos ui v2026.5.22, Tailscale-paired, all platform permissions granted (accessibility, camera, location, microphone, notifications, screenRecording, speechRecognition, appleScript)
  • A conversational agent (agent=main) bound to the Mac node via per-agent tools.exec.node pin

Related

  • #57169 — same architecture, screen.record case (currently open, P2)
  • #57192 — PR open against #57169 (allowlist-only fix, conflicts pending per ClawSweeper review)
  • #57431 — closed regression "Canvas/Node browser tools broken on macOS after v2026.3.24" (different surface but adjacent area)

Happy to provide additional log captures or a nodes list --json snapshot from any specific connection state if useful.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

One of:

  • Preferred: add a CANVAS_COMMANDS group and include the safe subset in PLATFORM_DEFAULTS.macos. Suggested split:

    CommandRecommended classificationReasoning
    canvas.presentsafe-defaultopens a local UI surface with a URL; no data exfiltration
    canvas.navigatesafe-defaultnavigates the existing canvas; no new privilege
    canvas.hidesafe-defaultcloses the canvas
    canvas.a2ui.push / pushJSONL / resetsafe-defaultpushes A2UI events to the local UI
    canvas.snapshotCANVAS_DANGEROUS_COMMANDS, opt-incaptures rendered display contents — comparable to screen.snapshot (which IS default-allowed today, so arguably canvas.snapshot could be too — open question)
    canvas.evalCANVAS_DANGEROUS_COMMANDS, opt-inexecutes arbitrary JS in the canvas context
  • Alternative: keep all canvas.* opt-in but improve the rejection message to name the operator action concretely (e.g. "add canvas.present to gateway.nodes.allowCommands") and link to docs explaining the safety classification. Current message ("not in the allowlist for platform") is opaque to anyone who hasn't read the source.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING