fix: run OpenClaw as node so installed plugins load - #21
Merged
Conversation
The gateway ran as root against a node-owned /data. OpenClaw's image is
designed to run as `node` (uid 1000) and its plugin loader only accepts
plugins owned by the gateway's own uid. `openclaw plugins install` wrote the
tree root-owned, then the boot-time `chown -R node:node /data` flipped it to
node, so the root gateway blocked it ("suspicious ownership, expected uid=0").
This surfaced installing the SimpleX channel plugin, but hit any runtime
plugin install (e.g. Codex).
Align to OpenClaw's model: run the gateway and every openclaw/start-cli exec
(plugin install/list/uninstall, auth login, check-login, state snapshot,
whatsapp login) as node. Keep root only where genuinely required — the chown
oneshot (only root can chown), update-ca-certificates (system trust store),
init seeding (fresh /data is root-owned before main's chown), and the
revoke-access cookie rm (a security action must always delete).
Supersedes the npm-subtree carve-out approach (#19, and the earlier #7).
Existing blocked installs self-heal on upgrade: the node-owned tree now
matches the node gateway, so no re-install is needed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
I’m happy to remove the version tag from my PR branch if you’d like to handle the release part. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
openclaw plugins installwrites the plugin tree root-owned, but the boot-timechown -R node:node /dataoneshot flips it tonodebefore the gateway loads — and the gateway runs as root (noUSERin theDockerfile, nouseron the daemon; the SDK defaults exec toroot). OpenClaw's plugin loader then blocks the node-owned tree:This is a root/node hybrid.
/databeing node-owned is correct — OpenClaw's official image runs asnode(uid 1000) and its loader only accepts plugins owned by the gateway's own uid (its documented Docker remediation is literallychown -R 1000:1000). The mistake is running the gateway as root against that node-owned tree. OpenClaw does not de-escalate ("plugins and tools execute within the same process context"), so a root gateway demands root-owned plugins while the node-chown guarantees node-owned ones. It surfaced installing the SimpleX plugin, but hit any runtime plugin install (e.g. Codex, see #7).Fix
Align to OpenClaw's actual model — run as
node— rather than patch the symptom:primarygateway and everyopenclaw/start-cliexec that touches/data(plugin install/list/uninstall,auth login, check-login, state snapshot, whatsapp login) now run as node.chownoneshot (only root can chown),update-ca-certificates(system trust store), init seeding (a fresh/datais root-owned beforemain's chown ever runs), and the revoke-access cookierm(a security action must always succeed at deleting).Plugin installs now write node-owned trees, the node gateway loads them, and file-exchange files OpenClaw writes come out node-owned too.
Relationship to the other open PRs
/data/.openclaw/npmsubtree — keeping the root/node hybrid and hard-coding the plugin path, which is why the same class of block already recurred (Codex → SimpleX). Removing the hybrid covers any current or future root-owned path under/datathe loader checks, not just npm.plugins.entries.<id>.enabled/allow) when Configure SimpleX skips the install. That is orthogonal to filesystem ownership (uid). Wire SimpleX file exchange (openclaw-simplex 1.8.0) and release 2026.7.1:3 #20 changes no ownership, so it still needs this fix to survive a restart — its install writes root-owned, the chown flips it to node, and the root gateway blocks it. When these merge, Wire SimpleX file exchange (openclaw-simplex 1.8.0) and release 2026.7.1:3 #20'srunOpenclawCli,readInstalledPlugin, and the newcheck-simplex-pluginoneshot must also run the openclaw CLI asnode, or Wire SimpleX file exchange (openclaw-simplex 1.8.0) and release 2026.7.1:3 #20's install path re-introduces the ownership block.Self-heal: existing boxes with a blocked (node-owned) plugin tree recover on upgrade — the node-owned tree now matches the node gateway, so no re-install is needed.
Version
Deliberately not bumped (still
2026.7.1:2) to avoid minting a second:3alongside #20. Coordinate the release: fold into #20's:3, or land this first as:3and rebase #20 to:4.Test plan
.s9pk. Complete first-run: Set Password, Configure AI Provider, Login to StartOS.start-cli package attach openclaw -n openclaw-sub -- id→uid=1000(node). Web UI health goes green.blocked plugin candidate: suspicious ownership, and the SimpleX channel comes up. Verify ownership:start-cli package attach openclaw -n openclaw-sub -- ls -la /data/.openclaw/npm/node_modules→ plugin dirs ownednode node.start-clihost administration still works as node:start-cli package attach openclaw -n openclaw-sub -- tail /data/.openclaw/workspace/MEMORY.mdshows the startup server-state snapshot, and the agent can list packages.start-cli auth session listfails.🤖 Generated with Claude Code