Keep the managed npm tree root-owned so plugin installs load - #19
Closed
lundog wants to merge 1 commit into
Closed
Conversation
The chown oneshot handed all of /data to node:node on every boot. But this image runs OpenClaw as root (no USER in the Dockerfile, no user set on the daemon/oneshots), and OpenClaw's install policy refuses to load plugin code owned by a lower-privilege uid than the loading process: blocked plugin candidate: suspicious ownership (uid=1000, expected uid=0 or root) plugin present but blocked `openclaw plugins install` writes the plugin tree root-owned, so the recursive node-chown of /data is what flipped it to node-owned and got it blocked on the next gateway load. Keep /data node-owned for the gateway's state, but re-assert root ownership on the managed npm tree (/data/.openclaw/npm) after the node-chown (mkdir first so it exists before the first install). This matches OpenClaw's documented remediation for intentional root deployments, and re-running on every boot self-heals a tree a previous boot left node-owned. Docs: https://docs.openclaw.ai/tools/plugin#blocked-plugin-path-ownership
Contributor
Author
|
Closing, superseded! |
helix-nine
added a commit
to lundog/openclaw-startos
that referenced
this pull request
Jul 25, 2026
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 (Start9-Community#19, and the earlier Start9-Community#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>
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
The
chownoneshot instartos/main.tsrunschown -R node:node /dataon every boot. This image runs OpenClaw as root — there's noUSERin theDockerfile, and the gateway daemon and oneshots set nouser— and OpenClaw's install policy refuses to load plugin code owned by a lower-privilege uid than the loading process:openclaw plugins installwrites the plugin tree root-owned, so the recursive node-chown of/datais what flips the freshly installed plugin files tonode:nodeand gets them blocked on the next gateway load.This affects any runtime plugin install on the image (surfaced while installing the SimpleX channel plugin from a package action).
Fix
Keep
/datanode-owned so the gateway can write its state, but re-assert root ownership on the managed npm tree (/data/.openclaw/npm):/data(unchanged intent);mkdir -p /data/.openclaw/npmso the chown target exists on a fresh box before the first install;chown -R root:root /data/.openclaw/npmafter the node-chown.This is exactly OpenClaw's documented remediation for intentional root deployments — see Blocked plugin path ownership: "If you intentionally run OpenClaw as root, repair the managed plugin root to root ownership instead:
sudo chown -R root:root /path/to/openclaw-config/npm."Because the oneshot runs before the gateway on every boot (
primaryalreadyrequires: ['... chown']), it also self-heals: a box whose plugin tree a previous boot leftnode:nodeis repaired toroot:rootbefore the gateway loads plugins, so existing broken installs recover without a manualdoctor/registry --refresh.Verification
tsc --noEmitclean;ncc build startos/index.tssucceeds.0.4.0-beta.9hardware:openclaw plugins install @dangoldbj/openclaw-simplex@1.8.0 --force(as root,HOME=/data) writes the plugin tree root-owned whether thenpm/projectsparents are root- or node-owned — npm does not de-escalate to the directory owner, so the blanket node-chown was the sole cause of the node ownership.