Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ jobs:

- id: publication
name: Select base and optional managed-image publication
if: ${{ inputs.pr_number == '' || steps.select_pr_source.outputs.selection == 'base-cohort' || inputs.jobs != '' || inputs.targets == '' || contains(inputs.targets, 'managed-image-') }}
env:
EXPECTED_SHA: ${{ steps.publication_mode.outputs.expected_sha }}
GITHUB_TOKEN: ${{ github.token }}
Expand All @@ -309,6 +310,7 @@ jobs:
node --experimental-strip-types --no-warnings tools/e2e/base-image-publication.mts --wait-seconds "$wait_seconds" --poll-seconds 30

- name: Download immutable Deep Agents Code base contract
if: ${{ inputs.pr_number == '' || steps.select_pr_source.outputs.selection == 'base-cohort' || inputs.jobs != '' || inputs.targets == '' || contains(inputs.targets, 'managed-image-') }}
env:
GITHUB_TOKEN: ${{ github.token }}
PUBLICATION_HEAD_SHA: ${{ steps.publication.outputs.head_sha }}
Expand All @@ -318,6 +320,7 @@ jobs:

- id: validate_dcode_base
name: Validate immutable Deep Agents Code base
if: ${{ inputs.pr_number == '' || steps.select_pr_source.outputs.selection == 'base-cohort' || inputs.jobs != '' || inputs.targets == '' || contains(inputs.targets, 'managed-image-') }}
env:
PUBLICATION_HEAD_SHA: ${{ steps.publication.outputs.head_sha }}
PUBLICATION_RUN_ATTEMPT: ${{ steps.publication.outputs.run_attempt }}
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,6 @@
COPY scripts/lib/gateway-supervisor.sh /usr/local/lib/nemoclaw/gateway-supervisor.sh
COPY scripts/lib/sandbox-rlimits.sh /usr/local/lib/nemoclaw/sandbox-rlimits.sh
COPY scripts/lib/openclaw_device_approval_policy.py /usr/local/lib/nemoclaw/openclaw_device_approval_policy.py
COPY scripts/lib/clean_runtime_shell_env_shim.py /usr/local/lib/nemoclaw/clean_runtime_shell_env_shim.py
COPY scripts/lib/normalize_mutable_config_perms.py /usr/local/lib/nemoclaw/normalize_mutable_config_perms.py
COPY scripts/lib/refresh-openclaw-wechat-placeholder.py /usr/local/lib/nemoclaw/refresh-openclaw-wechat-placeholder.py
COPY scripts/openclaw-config-guard.py /usr/local/lib/nemoclaw/openclaw-config-guard.py
Expand Down Expand Up @@ -1571,10 +1570,10 @@
# CHAT_UI_URL is a non-loopback address (Brev Launchable, remote deployments)
# since terminal-based pairing is impossible in those contexts.
# Default: "0" (device auth enabled for local deployments — secure by default).
ARG NEMOCLAW_DISABLE_DEVICE_AUTH=0

Check warning on line 1573 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "NEMOCLAW_DISABLE_DEVICE_AUTH") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
# Internal audit provenance for the opt-out above. Standard onboarding rewrites
# this to managed-onboard; direct image builders retain operator provenance.
ARG NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE=operator

Check warning on line 1576 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ARG "NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
# Compatibility build arg for older custom Dockerfiles and rebuild tooling.
# NemoClaw-managed images intentionally do not consume it; gateway auth tokens
# are generated at container startup and are never baked into image layers.
Expand Down Expand Up @@ -1608,7 +1607,7 @@
# NEMOCLAW_MESSAGING_PLAN_B64 intentionally remains ARG-only: Docker exposes it
# to build RUN processes without retaining the full plan in the final image env.
# Direct ARG interpolation into inline source is a code injection vector (C-2).
ENV NEMOCLAW_MODEL=${NEMOCLAW_MODEL} \

Check warning on line 1610 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "NEMOCLAW_DEVICE_AUTH_OPT_OUT_SOURCE") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/

Check warning on line 1610 in Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (OpenClaw)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "NEMOCLAW_DISABLE_DEVICE_AUTH") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
NEMOCLAW_INFERENCE_PROVIDER_ID=${NEMOCLAW_INFERENCE_PROVIDER_ID} \
NEMOCLAW_UPSTREAM_PROVIDER=${NEMOCLAW_UPSTREAM_PROVIDER} \
NEMOCLAW_PRIMARY_MODEL_REF=${NEMOCLAW_PRIMARY_MODEL_REF} \
Expand Down Expand Up @@ -1659,7 +1658,9 @@
&& command -v setpriv >/dev/null 2>&1

WORKDIR /sandbox
RUN test "$(id -u sandbox):$(id -g sandbox):$(pwd)" = "998:998:/sandbox"
RUN test "$(id -u sandbox):$(id -g sandbox):$(pwd)" = "998:998:/sandbox" \
&& chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile \
&& chmod 644 /sandbox/.bashrc /sandbox/.profile
USER sandbox

# Write openclaw.json with gateway config but WITHOUT the real auth token.
Expand Down Expand Up @@ -1935,7 +1936,6 @@
/usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh \
/usr/local/lib/nemoclaw/sandbox-rlimits.sh \
&& chmod 644 /usr/local/lib/nemoclaw/openclaw_device_approval_policy.py \
/usr/local/lib/nemoclaw/clean_runtime_shell_env_shim.py \
&& chmod 555 /usr/local/lib/nemoclaw/normalize_mutable_config_perms.py \
&& if [ -d /usr/local/lib/nemoclaw/preloads-compiled-channels ]; then \
find /usr/local/lib/nemoclaw/preloads-compiled-channels -path '*/runtime/*.js' -type f \
Expand Down
7 changes: 3 additions & 4 deletions Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -340,17 +340,16 @@ RUN mkdir -p /sandbox/.openclaw/agents/main/agent \
COPY scripts/lib/sandbox-rlimits.sh /usr/local/lib/nemoclaw/sandbox-rlimits.sh

# Pre-create shell init files for the sandbox user. Runtime environment hooks
# are installed system-wide below; user rc files stay clean and locked so
# per-user startup files are not part of the trust boundary.
# are installed system-wide below; personal files belong to the agent.
# hadolint ignore=SC2028
RUN printf '%s\n' \
'# NemoClaw sandbox shell init' \
> /sandbox/.bashrc \
&& printf '%s\n' \
'# NemoClaw sandbox login init' \
> /sandbox/.profile \
&& chown root:root /sandbox/.bashrc /sandbox/.profile \
&& chmod 444 /sandbox/.bashrc /sandbox/.profile
&& chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile \
&& chmod 644 /sandbox/.bashrc /sandbox/.profile

# System-wide proxy hooks. The per-home rc files above only fire for shells
# that find `~/.bashrc` / `~/.profile` (sandbox user, HOME=/sandbox). SSH
Expand Down
4 changes: 3 additions & 1 deletion agents/hermes/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -1572,7 +1572,9 @@ RUN cp /etc/nemoclaw/hermes.config-hash /sandbox/.hermes/.config-hash \
# Keep the shared NemoClaw state root consistent across every shipped agent.
# The root-owned sticky directory protects the managed-startup transaction
# receipt while leaving the named plugin state directories sandbox-writable.
RUN chown root:root /sandbox/.nemoclaw \
RUN chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile \
&& chmod 644 /sandbox/.bashrc /sandbox/.profile \
&& chown root:root /sandbox/.nemoclaw \
&& chmod 1755 /sandbox/.nemoclaw \
&& chown -R root:root /sandbox/.nemoclaw/blueprints \
&& chmod -R 755 /sandbox/.nemoclaw/blueprints \
Expand Down
4 changes: 2 additions & 2 deletions agents/hermes/Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -414,8 +414,8 @@ RUN printf '%s\n' \
'[ -f /tmp/nemoclaw-proxy-env.sh ] && . /tmp/nemoclaw-proxy-env.sh' \
'export PATH="/usr/local/bin:/opt/hermes/.venv/bin:${PATH}"' \
> /sandbox/.profile \
&& chown root:root /sandbox/.bashrc /sandbox/.profile \
&& chmod 444 /sandbox/.bashrc /sandbox/.profile
&& chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile \
&& chmod 644 /sandbox/.bashrc /sandbox/.profile

# Install Hermes Agent from the selected GitHub release.
# The image prebakes only the extras selected for the managed Hermes image:
Expand Down
6 changes: 1 addition & 5 deletions agents/hermes/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2383,7 +2383,7 @@ TUIENVEOF
# nemoclaw-configure-guard begin
hermes() {
case "$1" in
setup|doctor)
setup)
echo "Error: 'hermes $1' cannot modify config inside the sandbox." >&2
echo "NemoClaw manages sandbox config from the host for integrity checks." >&2
echo "" >&2
Expand All @@ -2400,10 +2400,6 @@ GUARDENVEOF
}

write_runtime_shell_env
# SECURITY FIX: Lock .bashrc/.profile after all static shims are in place.
# Hermes connect sessions source the dynamic guard from /tmp/nemoclaw-proxy-env.sh
# so startup never needs to rewrite files directly under /sandbox after caps drop.
lock_rc_files "$_SANDBOX_HOME"

# ── Legacy layout migration ──────────────────────────────────────
path_has_immutable_bit() {
Expand Down
10 changes: 4 additions & 6 deletions agents/langchain-deepagents-code/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
strings "$binary" | grep -Fq '/usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh'

# hadolint ignore=DL3006
FROM ${BASE_IMAGE}

Check warning on line 68 in agents/langchain-deepagents-code/Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (Deep Agents Code)

Default value for global ARG results in an empty or invalid base image name

InvalidDefaultArgInFrom: Default value for ARG ${BASE_IMAGE} results in empty or invalid base image name More info: https://docs.docker.com/go/dockerfile/rule/invalid-default-arg-in-from/

# The supplied base may end as a non-root runtime user. Reset the build user
# explicitly before installing the root-owned managed-startup handoff.
Expand Down Expand Up @@ -199,7 +199,7 @@
COPY agents/langchain-deepagents-code/validate-read-only-mcp-call.py /opt/nemoclaw-deepagents-code/validate-read-only-mcp-call.py
COPY agents/langchain-deepagents-code/dcode-wrapper.sh /usr/local/lib/nemoclaw/dcode-wrapper.sh
COPY agents/langchain-deepagents-code/dcode-launcher.sh /usr/local/lib/nemoclaw/dcode-launcher.sh
COPY agents/langchain-deepagents-code/dcode-login-profile.sh /usr/local/lib/nemoclaw/dcode-login-profile.sh
COPY agents/langchain-deepagents-code/dcode-login-profile.sh /etc/profile.d/nemoclaw-dcode.sh
COPY agents/langchain-deepagents-code/dcode-session-supervisor.py /usr/local/lib/nemoclaw/dcode-session-supervisor.py
COPY scripts/lib/entrypoint-env-wrapper.sh /usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh
COPY agents/langchain-deepagents-code/start.sh /usr/local/bin/nemoclaw-start
Expand All @@ -224,7 +224,7 @@
&& test -f /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh \
&& test ! -L /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh \
&& test "$(stat -c '%u:%g:%a' /usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh)" = '0:0:444' \
&& chmod 444 /opt/nemoclaw-deepagents-code/generate-config.ts /opt/nemoclaw-deepagents-code/agents/langchain-deepagents-code/generate-config.ts /opt/nemoclaw-deepagents-code/src/lib/inference/managed-dcode/identity.ts /opt/nemoclaw-deepagents-code/managed-dcode-runtime.py /opt/nemoclaw-deepagents-code/patch-managed-deepagents-code.py /opt/nemoclaw-deepagents-code/validate-nemotron-ultra-profile.py /opt/nemoclaw-deepagents-code/progressive_tool_disclosure.py /opt/nemoclaw-deepagents-code/nemoclaw_observability.py /opt/nemoclaw-deepagents-code/validate-progressive-tool-disclosure.py /opt/nemoclaw-deepagents-code/validate-observability.py /opt/nemoclaw-deepagents-code/validate-read-only-mcp-call.py /usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh /usr/local/lib/nemoclaw/dcode-login-profile.sh /usr/local/lib/nemoclaw/nemoclaw_read_only_mcp.py \
&& chmod 444 /opt/nemoclaw-deepagents-code/generate-config.ts /opt/nemoclaw-deepagents-code/agents/langchain-deepagents-code/generate-config.ts /opt/nemoclaw-deepagents-code/src/lib/inference/managed-dcode/identity.ts /opt/nemoclaw-deepagents-code/managed-dcode-runtime.py /opt/nemoclaw-deepagents-code/patch-managed-deepagents-code.py /opt/nemoclaw-deepagents-code/validate-nemotron-ultra-profile.py /opt/nemoclaw-deepagents-code/progressive_tool_disclosure.py /opt/nemoclaw-deepagents-code/nemoclaw_observability.py /opt/nemoclaw-deepagents-code/validate-progressive-tool-disclosure.py /opt/nemoclaw-deepagents-code/validate-observability.py /opt/nemoclaw-deepagents-code/validate-read-only-mcp-call.py /usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh /etc/profile.d/nemoclaw-dcode.sh /usr/local/lib/nemoclaw/nemoclaw_read_only_mcp.py \
&& chmod 755 /usr/local/bin/nemoclaw-start /usr/local/bin/nemoclaw-managed-startup-hold /usr/local/bin/nemoclaw-managed-bootstrap /usr/local/lib/nemoclaw/dcode-wrapper.sh /usr/local/lib/nemoclaw/dcode-launcher.sh /usr/local/lib/nemoclaw/dcode-session-supervisor.py \
&& test "$(stat -c '%u:%g:%a' /usr/local/lib/nemoclaw/dcode-session-supervisor.py)" = "0:0:755" \
&& install -o root -g root -m 0755 /usr/local/lib/nemoclaw/dcode-launcher.sh /usr/local/lib/nemoclaw/dcode-managed-exec \
Expand Down Expand Up @@ -319,7 +319,7 @@
&& env -i /usr/local/bin/dcode.real --version \
&& env -i /usr/local/bin/deepagents-code --version

ENV HOME=/sandbox \

Check warning on line 322 in agents/langchain-deepagents-code/Dockerfile

View workflow job for this annotation

GitHub Actions / PR build and direct managed startup (Deep Agents Code)

Sensitive data should not be used in the ARG or ENV commands

SecretsUsedInArgOrEnv: Do not use ARG or ENV instructions for sensitive data (ENV "DEEPAGENTS_CODE_OPENAI_API_KEY") More info: https://docs.docker.com/go/dockerfile/rule/secrets-used-in-arg-or-env/
VIRTUAL_ENV=/opt/venv \
PATH="/usr/local/bin:/opt/venv/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin" \
NEMOCLAW_MODEL=${NEMOCLAW_MODEL} \
Expand Down Expand Up @@ -362,10 +362,8 @@
USER root
RUN chown root:sandbox /sandbox \
&& chmod 1775 /sandbox \
&& install -o root -g root -m 0444 /usr/local/lib/nemoclaw/dcode-login-profile.sh /sandbox/.bash_profile \
&& test "$(stat -c '%U:%G:%a' /sandbox)" = 'root:sandbox:1775' \
&& test "$(stat -c '%U:%G:%a' /sandbox/.bash_profile)" = 'root:root:444' \
&& cmp -s /usr/local/lib/nemoclaw/dcode-login-profile.sh /sandbox/.bash_profile \
&& chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile \
&& chmod 644 /sandbox/.bashrc /sandbox/.profile \
&& chown root:root /sandbox/.nemoclaw \
&& chmod 1755 /sandbox/.nemoclaw \
&& chown -R root:root /sandbox/.nemoclaw/blueprints \
Expand Down
4 changes: 2 additions & 2 deletions agents/langchain-deepagents-code/Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -305,8 +305,8 @@ RUN printf '%s\n' \
'export HOME=/sandbox' \
'export PATH="/usr/local/bin:/opt/venv/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin"' \
> /sandbox/.profile \
&& chown root:root /sandbox/.bashrc /sandbox/.profile \
&& chmod 444 /sandbox/.bashrc /sandbox/.profile
&& chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile \
&& chmod 644 /sandbox/.bashrc /sandbox/.profile

COPY scripts/lib/sandbox-rlimits.sh /usr/local/lib/nemoclaw/sandbox-rlimits.sh

Expand Down
18 changes: 5 additions & 13 deletions agents/langchain-deepagents-code/dcode-login-profile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,11 @@
# SPDX-License-Identifier: Apache-2.0
# shellcheck shell=bash

# OpenShell starts command-bearing sandbox sessions with `bash -lc` and sets
# HOME to the writable workspace before Bash reads its first login file. Keep
# this first-match profile root-owned so sandbox code cannot run before a
# NemoClaw-managed DCode probe. Ordinary login commands retain the established
# runtime environment; the managed launcher rebuilds that environment from
# image-owned inputs and must not source the sandbox-user-owned convenience
# file first.
unset BASH_ENV ENV
# OpenShell uses a login shell for managed probes. Select an image-owned home
# before Bash reads personal files; the managed launcher restores agent HOME.
case "${BASH_EXECUTION_STRING:-}" in
*"/usr/local/lib/nemoclaw/dcode-managed-exec"*) ;;
*)
[ -f /tmp/nemoclaw-proxy-env.sh ] && . /tmp/nemoclaw-proxy-env.sh
export HOME=/sandbox
export PATH="/usr/local/bin:/opt/venv/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin"
*"/usr/local/lib/nemoclaw/dcode-managed-exec"*)
unset BASH_ENV ENV
export HOME=/usr/local/lib/nemoclaw
;;
esac
45 changes: 1 addition & 44 deletions agents/langchain-deepagents-code/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,6 @@ unset BASH_ENV ENV
export HOME=/sandbox
export PATH="/usr/local/bin:/opt/venv/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin"

readonly NEMOCLAW_DCODE_LOGIN_PROFILE_SOURCE="/usr/local/lib/nemoclaw/dcode-login-profile.sh"

verify_dcode_login_profile() {
[ -d /sandbox ] \
&& [ ! -L /sandbox ] \
&& [ -f "$NEMOCLAW_DCODE_LOGIN_PROFILE_SOURCE" ] \
&& [ ! -L "$NEMOCLAW_DCODE_LOGIN_PROFILE_SOURCE" ] \
&& [ "$(stat -c '%U:%G:%a' "$NEMOCLAW_DCODE_LOGIN_PROFILE_SOURCE" 2>/dev/null || true)" = "root:root:444" ] \
&& [ ! -L /sandbox/.bash_profile ] \
&& [ "$(stat -c '%U:%G:%a' /sandbox 2>/dev/null || true)" = "root:sandbox:1775" ] \
&& [ "$(stat -c '%U:%G:%a' /sandbox/.bash_profile 2>/dev/null || true)" = "root:root:444" ] \
&& cmp -s "$NEMOCLAW_DCODE_LOGIN_PROFILE_SOURCE" /sandbox/.bash_profile
}

protect_dcode_login_profile() {
local source_metadata
source_metadata="$(stat -c '%U:%G:%a' "$NEMOCLAW_DCODE_LOGIN_PROFILE_SOURCE" 2>/dev/null || true)"
if [ ! -f "$NEMOCLAW_DCODE_LOGIN_PROFILE_SOURCE" ] \
|| [ -L "$NEMOCLAW_DCODE_LOGIN_PROFILE_SOURCE" ] \
|| [ "$source_metadata" != "root:root:444" ]; then
printf '%s\n' '[SECURITY] Managed DCode login profile is missing or unsafe.' >&2
exit 1
fi

chown root:sandbox /sandbox
chmod 1775 /sandbox
rm -f -- /sandbox/.bash_profile
install -o root -g root -m 0444 \
"$NEMOCLAW_DCODE_LOGIN_PROFILE_SOURCE" /sandbox/.bash_profile
if ! verify_dcode_login_profile; then
printf '%s\n' '[SECURITY] Could not protect the managed DCode login profile.' >&2
exit 1
fi
}

# managed-entrypoint-env-wrapper begin
_NEMOCLAW_ENTRYPOINT_ENV_WRAPPER="/usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh"
if [ ! -f "$_NEMOCLAW_ENTRYPOINT_ENV_WRAPPER" ]; then
Expand All @@ -69,19 +34,11 @@ unset NEMOCLAW_ENTRYPOINT_NORMALIZED_ARGC NEMOCLAW_ENTRYPOINT_NORMALIZED_ARGV \
unset -f nemoclaw_normalize_entrypoint_env_wrapper
# managed-entrypoint-env-wrapper end

# The published managed image uses uid 0 as its OCI entry user so every start
# can repair the protected login-profile boundary before immediately dropping
# to the legacy sandbox-user path. A sandbox-user image still verifies the
# image-baked boundary before continuing.
# Root entrypoints hand off agent work without reading personal shell files.
if [ "$(id -u)" -eq 0 ]; then
protect_dcode_login_profile
exec /usr/bin/setpriv --reuid=sandbox --regid=sandbox --init-groups -- \
/usr/local/bin/nemoclaw-start "$@"
fi
if ! verify_dcode_login_profile; then
printf '%s\n' '[SECURITY] DCode login profile is not protected; rebuild this sandbox.' >&2
exit 1
fi

while IFS= read -r _nemoclaw_auto_approval_env; do
unset "$_nemoclaw_auto_approval_env"
Expand Down
2 changes: 2 additions & 0 deletions agents/pi/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
strings "$binary" | grep -Fq '/usr/local/lib/nemoclaw/managed-bootstrap-trampoline.sh'

# hadolint ignore=DL3006
FROM ${BASE_IMAGE}

Check warning on line 62 in agents/pi/Dockerfile

View workflow job for this annotation

GitHub Actions / Build and validate the Pi candidate managed image (arm64)

Default value for global ARG results in an empty or invalid base image name

InvalidDefaultArgInFrom: Default value for ARG ${BASE_IMAGE} results in empty or invalid base image name More info: https://docs.docker.com/go/dockerfile/rule/invalid-default-arg-in-from/

Check warning on line 62 in agents/pi/Dockerfile

View workflow job for this annotation

GitHub Actions / Build and validate the Pi candidate managed image (arm64)

Default value for global ARG results in an empty or invalid base image name

InvalidDefaultArgInFrom: Default value for ARG ${BASE_IMAGE} results in empty or invalid base image name More info: https://docs.docker.com/go/dockerfile/rule/invalid-default-arg-in-from/

Check warning on line 62 in agents/pi/Dockerfile

View workflow job for this annotation

GitHub Actions / Build and validate the Pi candidate managed image (amd64)

Default value for global ARG results in an empty or invalid base image name

InvalidDefaultArgInFrom: Default value for ARG ${BASE_IMAGE} results in empty or invalid base image name More info: https://docs.docker.com/go/dockerfile/rule/invalid-default-arg-in-from/

Check warning on line 62 in agents/pi/Dockerfile

View workflow job for this annotation

GitHub Actions / Build and validate the Pi candidate managed image (amd64)

Default value for global ARG results in an empty or invalid base image name

InvalidDefaultArgInFrom: Default value for ARG ${BASE_IMAGE} results in empty or invalid base image name More info: https://docs.docker.com/go/dockerfile/rule/invalid-default-arg-in-from/

# The supplied base may end as a non-root runtime user. Reset the build user
# explicitly before installing the root-owned managed-startup handoff.
Expand Down Expand Up @@ -236,6 +236,8 @@
USER root
RUN chown root:sandbox /sandbox \
&& chmod 1775 /sandbox \
&& chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile \
&& chmod 644 /sandbox/.bashrc /sandbox/.profile \
&& test "$(stat -c '%U:%G:%a' /sandbox)" = 'root:sandbox:1775' \
&& chown root:root /sandbox/.nemoclaw \
&& chmod 1755 /sandbox/.nemoclaw \
Expand Down
4 changes: 2 additions & 2 deletions agents/pi/Dockerfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,8 @@ RUN printf '%s\n' \
'export HOME=/sandbox' \
'export PATH="/usr/local/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin"' \
> /sandbox/.profile \
&& chown root:root /sandbox/.bashrc /sandbox/.profile \
&& chmod 444 /sandbox/.bashrc /sandbox/.profile
&& chown sandbox:sandbox /sandbox/.bashrc /sandbox/.profile \
&& chmod 644 /sandbox/.bashrc /sandbox/.profile

COPY scripts/lib/sandbox-rlimits.sh /usr/local/lib/nemoclaw/sandbox-rlimits.sh

Expand Down
23 changes: 1 addition & 22 deletions agents/pi/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,6 @@ export HOME=/sandbox
export PATH="/usr/local/bin:/usr/local/sbin:/usr/sbin:/usr/bin:/sbin:/bin"

readonly NEMOCLAW_PI_STATE_DIR="/sandbox/.pi/agent"
readonly NEMOCLAW_PI_SHELL_INIT_FILES=(/sandbox/.bashrc /sandbox/.profile)

verify_pi_shell_init() {
local file
[ -d /sandbox ] && [ ! -L /sandbox ] || return 1
for file in "${NEMOCLAW_PI_SHELL_INIT_FILES[@]}"; do
[ -f "$file" ] && [ ! -L "$file" ] || return 1
[ "$(stat -c '%U:%G:%a' "$file" 2>/dev/null || true)" = "root:root:444" ] || return 1
done
}

# managed-entrypoint-env-wrapper begin
_NEMOCLAW_ENTRYPOINT_ENV_WRAPPER="/usr/local/lib/nemoclaw/entrypoint-env-wrapper.sh"
if [ ! -f "$_NEMOCLAW_ENTRYPOINT_ENV_WRAPPER" ]; then
Expand All @@ -50,23 +39,13 @@ unset NEMOCLAW_ENTRYPOINT_NORMALIZED_ARGC NEMOCLAW_ENTRYPOINT_NORMALIZED_ARGV \
unset -f nemoclaw_normalize_entrypoint_env_wrapper
# managed-entrypoint-env-wrapper end

# The published managed image uses uid 0 as its OCI entry user so every start
# can repair the protected workspace boundary and create the protected merged
# CA bundle before dropping to the sandbox user. A sandbox-user image verifies
# the image-baked boundary instead.
# Root startup prepares protected state and trust before dropping privileges.
_NEMOCLAW_PI_DROP_PRIVILEGES=0
if [ "$(id -u)" -eq 0 ]; then
if ! verify_pi_shell_init; then
printf '%s\n' '[SECURITY] Managed Pi shell initialization files are missing or unsafe.' >&2
exit 1
fi
chown root:sandbox /sandbox
chmod 1775 /sandbox
install -d -o sandbox -g sandbox -m 0700 "$NEMOCLAW_PI_STATE_DIR"
_NEMOCLAW_PI_DROP_PRIVILEGES=1
elif ! verify_pi_shell_init; then
printf '%s\n' '[SECURITY] Pi shell initialization files are not protected; rebuild this sandbox.' >&2
exit 1
fi

export PI_OFFLINE=1
Expand Down
1 change: 0 additions & 1 deletion ci/full-e2e-cold-path-calibration.json
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@
"scripts/lib/gateway-supervisor.sh",
"scripts/lib/sandbox-rlimits.sh",
"scripts/lib/openclaw_device_approval_policy.py",
"scripts/lib/clean_runtime_shell_env_shim.py",
"scripts/lib/normalize_mutable_config_perms.py",
"src/lib/extra-agents-validation.ts",
"src/lib/messaging",
Expand Down
8 changes: 4 additions & 4 deletions ci/pi-agent-qualification-v1-linux-amd64.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
"agent": "pi",
"platform": "linux/amd64",
"image": "ghcr.io/nvidia/nemoclaw/pi-sandbox",
"digest": "sha256:bf9d72ffc4dd36e9567aff047ec16c7434a53281310020dd4b12efb1b6ec59e3",
"reference": "ghcr.io/nvidia/nemoclaw/pi-sandbox@sha256:bf9d72ffc4dd36e9567aff047ec16c7434a53281310020dd4b12efb1b6ec59e3",
"digest": "sha256:32416a25b4adf069edf71d8b022ed5a3eb980cdb259e6580a43978414b228b6e",
"reference": "ghcr.io/nvidia/nemoclaw/pi-sandbox@sha256:32416a25b4adf069edf71d8b022ed5a3eb980cdb259e6580a43978414b228b6e",
"source": {
"repository": "NVIDIA/NemoClaw",
"revision": "7d588356f720c0c09e860e83baa359456a63410f",
"revision": "375c76a4646c8f4f9537ac9c18370e519354bbc7",
"release": "v0.1.0",
"cohort": "ghrun-34045063725-1"
"cohort": "ghrun-34305816131-1"
},
"startupProfileContractVersion": 1,
"capabilityContractVersion": 1
Expand Down
8 changes: 4 additions & 4 deletions ci/pi-agent-qualification-v1-linux-arm64.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@
"agent": "pi",
"platform": "linux/arm64",
"image": "ghcr.io/nvidia/nemoclaw/pi-sandbox",
"digest": "sha256:396c89ada5657e8a3247e61d33357d764aba4901b8ac98320398e4e006c0afc6",
"reference": "ghcr.io/nvidia/nemoclaw/pi-sandbox@sha256:396c89ada5657e8a3247e61d33357d764aba4901b8ac98320398e4e006c0afc6",
"digest": "sha256:9bd80aa090f8bc51db8b1eead1a774d01d926ad0dc2d1ed629447db3f4dd7c72",
"reference": "ghcr.io/nvidia/nemoclaw/pi-sandbox@sha256:9bd80aa090f8bc51db8b1eead1a774d01d926ad0dc2d1ed629447db3f4dd7c72",
"source": {
"repository": "NVIDIA/NemoClaw",
"revision": "7d588356f720c0c09e860e83baa359456a63410f",
"revision": "375c76a4646c8f4f9537ac9c18370e519354bbc7",
"release": "v0.1.0",
"cohort": "ghrun-34045063725-1"
"cohort": "ghrun-34305816131-1"
},
"startupProfileContractVersion": 1,
"capabilityContractVersion": 1
Expand Down
Loading
Loading