Files
deepseek-harness/run-as-runtime-user.sh
Miles WardandClaude Fable 5 400797ecac
Sync upstream and build images / sync-and-build (push) Successful in 1m45s
Wrap the pnpm family too, not just dsh
Verifying the pushed -r1 image found one hole: only dsh was wrapped, so a
root 'docker exec <c> pnpm ...' created PNPM_HOME/XDG_CONFIG_HOME owned by
root inside the state volume — which the unprivileged dsh could not then use
for plugin installs (self-healing on the next restart, since the entrypoint
re-chowns, but broken until then).

The wrapper is now generic (execs the resolved target directly; dsh's bin.js
is +x with a node shebang and pnpm's bin is an ELF, so no node prefix is
needed) and is installed over dsh, pnpm, pnpx, pn and pnx. Renamed
dsh-wrapper.sh -> run-as-runtime-user.sh to match what it does.

Verified: root 'docker exec pnpm --version' + 'pnpm store path' + 'dsh plugin
add' leave zero root-owned files in the volume; 23-case matrix still green.
Ships as 0.1.1-rc.2-r2.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-09-11 12:08:54 -04:00

24 lines
939 B
Bash

#!/bin/sh
# Wrapper installed over the `dsh` and `pnpm`-family bins so that a ROOT
# invocation re-execs as the runtime user.
#
# The container starts as root (see docker-entrypoint.sh) to repair bind-mount
# ownership, which also makes `docker exec <container> …` default to root. Left
# alone, the documented plugin workflow would then drop root-owned files into
# the state volume — `dsh plugin … add` writes the profile, and pnpm writes
# PNPM_HOME / XDG_CONFIG_HOME — and the unprivileged dsh process could not read
# or replace them on its next start. Re-exec'ing as PUID:PGID keeps the volume
# consistent however these tools are invoked.
#
# Set DSH_ALLOW_ROOT=1 to bypass (diagnostics only).
set -eu
BIN=__TARGET__
if [ "$(id -u)" = 0 ] && [ "${DSH_ALLOW_ROOT:-0}" != 1 ]; then
exec setpriv --reuid "${PUID:-1000}" --regid "${PGID:-1000}" --init-groups \
env HOME=/home/node "$BIN" "$@"
fi
exec "$BIN" "$@"