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
49 changes: 48 additions & 1 deletion .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,10 +131,12 @@ reviews:
- `sandbox-survival-e2e` — gateway restart recovery
- `sandbox-operations-e2e` — process recovery after gateway kill
- `cloud-e2e` — full onboard + cloud inference
- `openclaw-slack-pairing-e2e` — gateway/connect-shell OpenClaw
pairing state root parity for Slack

To run selectively:
```
gh workflow run nightly-e2e.yaml --ref <branch> -f jobs=sandbox-survival-e2e,sandbox-operations-e2e,cloud-e2e
gh workflow run nightly-e2e.yaml --ref <branch> -f jobs=sandbox-survival-e2e,sandbox-operations-e2e,cloud-e2e,openclaw-slack-pairing-e2e
```

- path: "scripts/lib/sandbox-init.sh"
Expand Down Expand Up @@ -354,6 +356,51 @@ reviews:
gh workflow run nightly-e2e.yaml --ref <branch> -f jobs=hermes-slack-e2e
```

- path: "test/e2e/test-openclaw-slack-pairing.sh"
instructions: |
This script validates OpenClaw Slack DM pairing across the gateway
and connect-shell runtime contexts with a hermetic fake Slack Socket
Mode event and fake chat.postMessage reply.

**E2E test recommendation:**
- `openclaw-slack-pairing-e2e` - Slack pairing request approval and
allowFrom state root parity

To run selectively:
```
gh workflow run nightly-e2e.yaml --ref <branch> -f jobs=openclaw-slack-pairing-e2e
```

- path: "test/e2e/lib/fake-slack-api.cjs"
instructions: |
This helper backs hermetic Slack REST and Socket Mode tests. Changes
can alter Slack credential rewrite assertions, fake Socket Mode
envelopes, or chat.postMessage capture.

**E2E test recommendation:**
- `messaging-providers-e2e` - Slack REST provider placeholder rewrite
- `openclaw-slack-pairing-e2e` - fake Slack Socket Mode pairing flow

To run selectively:
```
gh workflow run nightly-e2e.yaml --ref <branch> -f jobs=messaging-providers-e2e,openclaw-slack-pairing-e2e
```

- path: "test/e2e/lib/slack-api-proof.sh"
instructions: |
This helper applies hermetic Slack REST and Socket Mode policies for
messaging E2E scripts. Changes can affect REST credential rewrite
probes and native websocket policy coverage.

**E2E test recommendation:**
- `messaging-providers-e2e` - Slack REST provider placeholder rewrite
- `openclaw-slack-pairing-e2e` - fake Slack Socket Mode pairing flow

To run selectively:
```
gh workflow run nightly-e2e.yaml --ref <branch> -f jobs=messaging-providers-e2e,openclaw-slack-pairing-e2e
```

- path: "nemoclaw-blueprint/policies/**"
instructions: |
This directory contains network policy definitions and presets.
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/nightly-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
# messaging-providers-e2e Validates messaging credential provider/placeholder/L7-proxy chain
# for Telegram + Discord + Slack. Uses fake tokens. Slack additionally
# exercises OpenShell provider-shaped alias resolution (#2085 follow-up).
# openclaw-slack-pairing-e2e
# Validates hermetic Slack Socket Mode pairing request approval across
# gateway and connect-shell OpenClaw state roots (#3730/#3737).
# messaging-compatible-endpoint-e2e
# Validates Telegram + OpenAI-compatible endpoint inference routing
# through inference.local with a hermetic local mock (#2766).
Expand Down Expand Up @@ -66,6 +69,7 @@ on:
Comma-separated job names to run (empty = all).
Valid: cloud-e2e, cloud-onboard-e2e, cloud-inference-e2e,
skill-agent-e2e, docs-validation-e2e, messaging-providers-e2e,
openclaw-slack-pairing-e2e,
messaging-compatible-endpoint-e2e,
kimi-inference-compat-e2e,
token-rotation-e2e, sandbox-survival-e2e,
Expand Down Expand Up @@ -337,6 +341,43 @@ jobs:
/tmp/nemoclaw-e2e-whatsapp-*.log
if-no-files-found: ignore

# ── OpenClaw Slack Pairing E2E (#3730/#3737) ──────────────────
# Hermetic Socket Mode inbound event + chat.postMessage reply path, then
# connect-shell `openclaw pairing approve slack <code>` against shared state.
openclaw-slack-pairing-e2e:
if: >-
github.repository == 'NVIDIA/NemoClaw' &&
(github.event_name != 'workflow_dispatch' ||
inputs.jobs == '' ||
contains(format(',{0},', inputs.jobs), ',openclaw-slack-pairing-e2e,'))
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ inputs.target_ref || github.ref }}

- name: Run OpenClaw Slack pairing E2E test
env:
NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }}
NEMOCLAW_NON_INTERACTIVE: "1"
NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1"
NEMOCLAW_POLICY_TIER: "open"
NEMOCLAW_SANDBOX_NAME: "e2e-openclaw-slack-pairing"
GITHUB_TOKEN: ${{ github.token }}
SLACK_BOT_TOKEN: "xoxb-fake-slack-pairing-e2e"
SLACK_APP_TOKEN: "xapp-fake-slack-pairing-e2e"
run: bash test/e2e/test-openclaw-slack-pairing.sh

- name: Upload install log on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: install-log-openclaw-slack-pairing
path: /tmp/nemoclaw-e2e-openclaw-slack-pairing-install.log
if-no-files-found: ignore

# ── Messaging + compatible endpoint regression (#2766) ───────
# Hermetic Telegram + OpenAI-compatible endpoint path. Uses a local mock
# endpoint and fake Telegram token, then asserts sandbox inference.local
Expand Down Expand Up @@ -2031,6 +2072,7 @@ jobs:
skill-agent-e2e,
docs-validation-e2e,
messaging-providers-e2e,
openclaw-slack-pairing-e2e,
messaging-compatible-endpoint-e2e,
channels-stop-start-e2e,
brave-search-e2e,
Expand Down Expand Up @@ -2124,6 +2166,7 @@ jobs:
skill-agent-e2e,
docs-validation-e2e,
messaging-providers-e2e,
openclaw-slack-pairing-e2e,
messaging-compatible-endpoint-e2e,
channels-stop-start-e2e,
brave-search-e2e,
Expand Down Expand Up @@ -2274,6 +2317,7 @@ jobs:
skill-agent-e2e,
docs-validation-e2e,
messaging-providers-e2e,
openclaw-slack-pairing-e2e,
messaging-compatible-endpoint-e2e,
channels-stop-start-e2e,
brave-search-e2e,
Expand Down
28 changes: 25 additions & 3 deletions scripts/nemoclaw-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -199,13 +199,16 @@ if [ -z "$_DASHBOARD_PORT_RAW" ]; then
fi
else
_DASHBOARD_PORT="$(printf '%s' "$_DASHBOARD_PORT_RAW" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')"
_DASHBOARD_PORT_VALID=1
case "$_DASHBOARD_PORT" in
*[!0-9]* | '')
echo "[SECURITY] Invalid NEMOCLAW_DASHBOARD_PORT='${NEMOCLAW_DASHBOARD_PORT}' — must be an integer between 1024 and 65535" >&2
exit 1
_DASHBOARD_PORT_VALID=0
;;
esac
if ! [ "$_DASHBOARD_PORT" -ge 1024 ] || ! [ "$_DASHBOARD_PORT" -le 65535 ]; then
if [ "$_DASHBOARD_PORT_VALID" -eq 1 ] && { [ "$_DASHBOARD_PORT" -lt 1024 ] || [ "$_DASHBOARD_PORT" -gt 65535 ]; }; then
_DASHBOARD_PORT_VALID=0
fi
if [ "$_DASHBOARD_PORT_VALID" -ne 1 ]; then
echo "[SECURITY] Invalid NEMOCLAW_DASHBOARD_PORT='${NEMOCLAW_DASHBOARD_PORT}' — must be an integer between 1024 and 65535" >&2
exit 1
fi
Expand All @@ -225,6 +228,18 @@ export OPENCLAW_GATEWAY_PORT="$_DASHBOARD_PORT"
export OPENCLAW_GATEWAY_URL="ws://127.0.0.1:${_DASHBOARD_PORT}"
OPENCLAW="$(command -v openclaw)" # Resolve once, use absolute path everywhere
_SANDBOX_HOME="/sandbox" # Home dir for the sandbox user (useradd -d /sandbox in Dockerfile.base)
_OPENCLAW_STATE_DIR="${_SANDBOX_HOME}/.openclaw"
_OPENCLAW_CREDENTIALS_DIR="${_OPENCLAW_STATE_DIR}/credentials"

# OpenClaw 2026.4.x stores channel pairing requests under
# resolveOAuthDir(resolveStateDir(...))/<channel>-pairing.json. The gateway
# runs as the gateway user while connect-shell commands run as sandbox, so
# relying on HOME/os.homedir() can split pending requests across users. Force
# every OpenClaw process in the sandbox to the persistent shared state root.
export OPENCLAW_HOME="${_SANDBOX_HOME}"
export OPENCLAW_STATE_DIR="${_OPENCLAW_STATE_DIR}"
export OPENCLAW_CONFIG_PATH="${_OPENCLAW_STATE_DIR}/openclaw.json"
export OPENCLAW_OAUTH_DIR="${_OPENCLAW_CREDENTIALS_DIR}"

# ── Config integrity check (delegates to shared library) ────────
# verify_config_integrity_if_locked is provided by sandbox-init.sh. OpenClaw
Expand Down Expand Up @@ -1580,6 +1595,13 @@ export http_proxy="$_PROXY_URL"
export https_proxy="$_PROXY_URL"
export no_proxy="$_NO_PROXY_VAL"
PROXYEOF
local _openclaw_env_name _openclaw_env_value _escaped_openclaw_env_value
for _openclaw_env_name in OPENCLAW_HOME OPENCLAW_STATE_DIR OPENCLAW_CONFIG_PATH OPENCLAW_OAUTH_DIR; do
_openclaw_env_value="${!_openclaw_env_name:-}"
[ -n "$_openclaw_env_value" ] || continue
_escaped_openclaw_env_value="$(printf '%s' "$_openclaw_env_value" | sed "s/'/'\\\\''/g")"
printf "export %s='%s'\n" "$_openclaw_env_name" "$_escaped_openclaw_env_value"
done
if [ -n "${OPENCLAW_GATEWAY_PORT:-}" ]; then
_escaped_gateway_port="$(printf '%s' "$OPENCLAW_GATEWAY_PORT" | sed "s/'/'\\\\''/g")"
printf "export OPENCLAW_GATEWAY_PORT='%s'\n" "$_escaped_gateway_port"
Expand Down
Loading
Loading