Skip to content

fix(whatsapp): reconcile post-pair gateway restart without operator.admin#6496

Merged
cv merged 6 commits into
NVIDIA:mainfrom
harjothkhara:fix/6413-whatsapp-postpair-reconcile
Jul 9, 2026
Merged

fix(whatsapp): reconcile post-pair gateway restart without operator.admin#6496
cv merged 6 commits into
NVIDIA:mainfrom
harjothkhara:fix/6413-whatsapp-postpair-reconcile

Conversation

@harjothkhara

@harjothkhara harjothkhara commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #6413 by restarting the running WhatsApp channel after a successful QR login without granting operator.admin to the pairing device.
The bounded restart uses gateway-token authentication only when the login targets the private gateway URL baked into NemoClaw's generated runtime environment.

The original implementation is by @harjothkhara.
The maintainer security follow-up preserves that credit with Co-authored-by: harjoth <harjoth.khara@gmail.com>.

Changes

  • Reissues the bounded channels.start RPC after a successful token-less WhatsApp login.
  • Bakes a separate readonly gateway URL trust anchor for token-bearing helpers instead of trusting caller-mutable OPENCLAW_GATEWAY_URL or NEMOCLAW_OPENCLAW_GATEWAY_URL values.
  • Strips the gateway token from a login to any caller-selected URL and skips the follow-up reconcile.
  • Adds the exact regression where a caller spoofs both mutable URL variables and verifies that no token reaches the login or reconcile.
  • Uses command-scoped environment assignments so the generated runtime file preserves the fix(openclaw): restore local CLI pairing path #6291 no-ambient-export invariant.
  • Preserves login exit status, forwards allowlisted --account values, and downgrades reconcile failures to recovery guidance.
  • Documents the OpenClaw-only post-pair restart, trust boundary, custom-URL behavior, and status recovery flow.
  • Synchronizes current main without rewriting the contributor's verified commit history.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — the existing QR, runtime-env, gateway-host, permission, and scope-replacement suites remain green.
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification:
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — the maintainer follow-up closes the mutable trust-anchor token-exfiltration path and adds an adversarial regression.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Verification

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run check:diff passed when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set — 73 passed and 3 skipped across the focused runtime, WhatsApp, gateway-host, permission, scope, and token-export coverage.
  • Applicable broad gate passed — normal exact-head CI is pending after this push.
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only) — passed with 0 errors and 2 existing repository warnings.
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Validation performed

  • Focused Vitest suites: 73 passed, 3 skipped
  • npm run docs:sync-agent-variants
  • npm run docs (0 errors; 2 existing warnings)
  • npm run test-size:check
  • npm run test:titles:check
  • npx prek run shfmt --files scripts/nemoclaw-start.sh
  • npx prek run shellcheck --files scripts/nemoclaw-start.sh
  • npm run check:diff
  • Commit and push hooks

AI-assisted: authored and reviewed with Claude Code; adversarially cross-reviewed with Codex.


Signed-off-by: harjoth harjoth.khara@gmail.com

Summary by CodeRabbit

  • Bug Fixes
    • Improved WhatsApp experimental login to automatically reconcile/restart the WhatsApp channel after successful QR pairing when safe.
    • Added --account / --account=... forwarding to the gateway restart request, with account ID validation and clear guidance on invalid values.
    • Strengthened gateway-token protection: token is only used for the trusted private gateway URL (and stripped for untrusted targets); enhanced recovery guidance when restart/reconciliation can’t run or fails.
  • Tests
    • Added end-to-end regression coverage for WhatsApp post-pair restart, parameter forwarding, and recovery/security scenarios.
    • Added a trusted gateway URL read-only alias test.
  • Documentation
    • Expanded WhatsApp sandbox channel documentation with verification steps and expected restart/status behavior.

@copy-pr-bot

copy-pr-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a trusted WhatsApp post-pair restart path to the login guard, with account forwarding, URL-based token gating, host guidance, updated docs, and regression tests covering restart, safety, and failure cases.

Changes

WhatsApp Post-Pair Restart

Layer / File(s) Summary
Trusted gateway URL anchor
scripts/nemoclaw-start.sh
Writes a readonly trusted private gateway URL value into the runtime shell environment.
Gateway token reader and post-pair reconcile function
scripts/nemoclaw-start.sh
Reads gateway.auth.token from config and calls channels.start after WhatsApp pairing with accountId validation and fallback guidance on missing token, invalid account, or RPC failure.
Account argument parsing in login wrapper
scripts/nemoclaw-start.sh
Initializes login account parsing state and captures --account and --account=* values during channels login.
Trusted gateway URL classification and token gating
scripts/nemoclaw-start.sh
Compares the selected gateway URL against the trusted anchor, strips OPENCLAW_GATEWAY_TOKEN for untrusted targets, and runs WhatsApp login with temporary errexit suppression and conditional env injection.
Post-login success branching and guidance
scripts/nemoclaw-start.sh, docs/manage-sandboxes/messaging-channels.mdx
Branches after successful WhatsApp login to print no-auto-restart guidance for untrusted targets, skip duplicate reconciliation when token auth already happened, otherwise call the post-pair restart helper, restore set -e, and update the WhatsApp pairing documentation with the new host-side guidance.
Guard extraction and simulated login harness
test/repro-6413-whatsapp-postpair-start.test.ts
Adds the reproduction test scaffolding, typed wrapper options/results, a temp bash harness with fake openclaw behavior, and a helper for isolating gateway call log lines.
Regression cases for restart and token safety
test/repro-6413-whatsapp-postpair-start.test.ts, test/nemoclaw-start-gateway-ws-host.test.ts
Exercises post-pair restart success, account forwarding and validation, untrusted URL token protection, ambient token stripping, spoofed alias handling, trusted-URL behavior, set -e exit handling, missing-token guidance, restart-failure recovery, and the exported trusted URL in the generated env script.

Estimated code review effort: 4 (Complex) | ~60 minutes

Suggested labels: bug-fix, area: sandbox

Suggested reviewers: brandonpelfrey, ericksoa

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main fix: WhatsApp post-pair gateway reconciliation without operator.admin.
Linked Issues check ✅ Passed The PR implements a bounded post-pair restart path with trusted-token gating and recovery guidance, matching #6413 and the security boundary.
Out of Scope Changes check ✅ Passed The added tests and documentation directly support the WhatsApp restart fix and do not introduce unrelated scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@harjothkhara harjothkhara force-pushed the fix/6413-whatsapp-postpair-reconcile branch from 362c233 to 0e4cba1 Compare July 8, 2026 20:42
…dmin (NVIDIA#6413)

OpenClaw's `channels login --channel whatsapp` saves credentials locally and
then asks the running gateway to restart the channel via the `channels.start`
RPC, which is gated behind `operator.admin`. Since NVIDIA#6291 stopped ordinary
in-sandbox argv from inheriting the gateway token, the login runs on device
auth, the device approval policy deliberately never grants `operator.admin`,
and the post-pair restart is denied — credentials are saved but the running
channel keeps its old session ("missing scope: operator.admin").

After a successful token-less WhatsApp login, re-issue that same bounded
`channels.start` as a NemoClaw-owned one-shot authenticated with the gateway
token (read from the mutable config), scoped to a single fixed-argv child and
sent only to NemoClaw's trusted private gateway URL. This mirrors the existing
sessions.reset/delete admin RPC path; it does not auto-approve operator.admin
or reintroduce ambient token auth, preserving the NVIDIA#6291 boundary. A failed or
ambient-token login is left untouched, and reconcile failures downgrade to
host-side recovery guidance without changing the login's exit code.

The WhatsApp login subprocess now also strips an ambient gateway token when the
login targets a caller-supplied (non-trusted) URL, closing a token
exfiltration path, and runs under saved/restored errexit so its exit status is
always captured.

Adds test/repro-6413-whatsapp-postpair-start.test.ts covering the reconcile,
trusted-URL gating, token non-exfiltration (login and reconcile), account-id
handling, and the errexit path.

Signed-off-by: harjoth <harjoth.khara@gmail.com>
@harjothkhara harjothkhara force-pushed the fix/6413-whatsapp-postpair-reconcile branch from 0e4cba1 to c5423e8 Compare July 8, 2026 20:45
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
@apurvvkumaria apurvvkumaria added the v0.0.79 Release target label Jul 8, 2026
Co-authored-by: harjoth <harjoth.khara@gmail.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
@apurvvkumaria

Copy link
Copy Markdown
Collaborator

I pushed signed commits f207d4a20 and 9a83e4929 after synchronizing current main. The security follow-up preserves @harjothkhara's original implementation and credit through Co-authored-by: harjoth <harjoth.khara@gmail.com>.

The key correction is that token-bearing decisions no longer trust either caller-mutable URL variable. The generated runtime environment now bakes a separate readonly URL anchor, and the regression spoofs both OPENCLAW_GATEWAY_URL and NEMOCLAW_OPENCLAW_GATEWAY_URL while proving the token reaches neither login nor reconcile. I also replaced ambient exports with command-scoped assignments, clearing the branch-specific #6291 guard failure.

Validation completed:

  • focused runtime/WhatsApp/security tests: 73 passed, 3 skipped
  • shfmt, ShellCheck, Biome, test-size and title checks: passed
  • docs variant sync and build: passed (0 errors; 2 existing warnings)
  • full scoped pre-commit/pre-push verification: passed
  • both maintainer commits are GitHub Verified

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
cd /tmp
gh pr view 6496 --repo NVIDIA/NemoClaw --json title,body,commits,files -q '.' | head -c 3000
echo "---DIFF---"
gh pr diff 6496 --repo NVIDIA/NemoClaw > pr.diff
wc -l pr.diff
sed -n '1,400p' pr.diff

Length of output: 25622


🏁 Script executed:

#!/bin/bash
cd /tmp
grep -n "nemoclaw-configure-guard" pr.diff scripts/nemoclaw-start.sh 2>/dev/null | head -50
echo "---"
find . -maxdepth 2 -iname "nemoclaw-start.sh"

Length of output: 540


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rm -rf /tmp/repo
git clone --quiet https://github.com/NVIDIA/NemoClaw.git /tmp/repo
cd /tmp/repo
gh pr checkout 6496 --repo NVIDIA/NemoClaw
grep -n "nemoclaw-configure-guard" scripts/nemoclaw-start.sh | head -20

Length of output: 699


🏁 Script executed:

#!/bin/bash
cd /tmp/repo
sed -n '3240,3300p' scripts/nemoclaw-start.sh

Length of output: 3683


🏁 Script executed:

#!/bin/bash
cd /tmp/repo
sed -n '1,400p' test/repro-6413-whatsapp-postpair-start.test.ts | sed -n '100,400p'

Length of output: 12008


This is a solid, well-tested security fix. The core correction addresses a real gap in the previous version: the earlier iteration used the caller-mutable NEMOCLAW_OPENCLAW_GATEWAY_URL alias to decide token trust, which meant a caller could spoof that variable and redirect the token-bearing reconcile to an attacker-controlled URL. This version fixes that by baking a separate _NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL as readonly at generation time, so it can't be overridden after the runtime file is sourced.

A few things I verified by pulling the branch and diff:

  • The readonly trust anchor is installed inside the generated runtime file (_NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL), guarded by a conditional assignment + builtin readonly, making repeated sourcing idempotent while failing closed if a conflicting value is already readonly.
  • The openclaw() wrapper now branches on _NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL (not the mutable alias) to decide whether the gateway token may be used for login or the post-pair reconcile, and strips OPENCLAW_GATEWAY_TOKEN for non-trusted targets.
  • Login now runs with errexit saved/restored, so exit status and post-login handling are reliable even under a caller's set -e.
  • --account / --account=<id> is parsed and allowlisted ([A-Za-z0-9._:-] under LC_ALL=C) before being embedded in the RPC JSON params, avoiding injection.
  • The reconcile never fails the login itself — token-missing, invalid-account, and RPC-failure paths all downgrade to stderr guidance and return 0.
  • The new test suite (test/repro-6413-whatsapp-postpair-start.test.ts) explicitly covers the adversarial case of spoofing both OPENCLAW_GATEWAY_URL and NEMOCLAW_OPENCLAW_GATEWAY_URL post-sourcing and asserts the token reaches neither the login subprocess nor the reconcile call, plus double-reconcile avoidance, account-id validation, and set -e preservation.

This looks like a meaningful hardening over the prior implementation while preserving the original author's design and intent. I'll trigger a full review pass to double check the diff end-to-end, including the docs and gateway-ws-host test changes.


✏️ Learnings added
Learnt from: apurvvkumaria
URL: https://github.com/NVIDIA/NemoClaw/pull/6496

Timestamp: 2026-07-08T21:52:22.408Z
Learning: In scripts/nemoclaw-start.sh, security-relevant trust decisions (e.g., whether the ambient OPENCLAW_GATEWAY_TOKEN may be sent to a gateway URL) must be based on `_NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL`, a `readonly` variable baked into the generated runtime file at sandbox-creation time. The caller-mutable compatibility alias `NEMOCLAW_OPENCLAW_GATEWAY_URL` (and the raw `OPENCLAW_GATEWAY_URL`) must never be used as the trust anchor for token-bearing decisions, since callers can override them after the runtime file is sourced.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
test/nemoclaw-start-gateway-ws-host.test.ts (1)

186-187: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer behavioral verification over literal source-text matching.

These assertions check the literal generated shell text rather than the actual runtime effect (that the variable is truly read-only and resolves to the expected value once sourced). Consider sourcing envFile and asserting the resulting env value plus that a reassignment attempt fails, consistent with behavioral testing over implementation-lock-in checks.

♻️ Example behavioral check
-      expect(envFile).toContain("_NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL='ws://10.200.0.2:18790'");
-      expect(envFile).toContain("builtin readonly _NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL");
+      const check = spawnSync("bash", ["-c",
+        `source ${JSON.stringify(envFile)}; printf '%s\\n' "$_NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL"; ` +
+        `if _NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL=x 2>/dev/null; then exit 1; fi`],
+        { encoding: "utf-8" });
+      expect(check.stdout.trim()).toBe("ws://10.200.0.2:18790");
+      expect(check.status).toBe(0);

Note this mirrors the file's existing assertion style for sibling variables, so this is a minor, deferable improvement rather than a blocking concern.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/nemoclaw-start-gateway-ws-host.test.ts` around lines 186 - 187, The
current assertions in the test rely on matching the generated shell text instead
of verifying behavior, so update the test around the envFile checks to source
the file and assert the resulting _NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL value
plus that reassignment is rejected. Reuse the existing sibling-variable testing
style in nemoclaw-start-gateway-ws-host.test.ts, and focus the change near the
envFile expectations for _NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@test/nemoclaw-start-gateway-ws-host.test.ts`:
- Around line 186-187: The current assertions in the test rely on matching the
generated shell text instead of verifying behavior, so update the test around
the envFile checks to source the file and assert the resulting
_NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL value plus that reassignment is rejected.
Reuse the existing sibling-variable testing style in
nemoclaw-start-gateway-ws-host.test.ts, and focus the change near the envFile
expectations for _NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 3fed4b2d-6a95-4f1c-afbf-b741567031fc

📥 Commits

Reviewing files that changed from the base of the PR and between c5423e8 and 9a83e49.

📒 Files selected for processing (4)
  • docs/manage-sandboxes/messaging-channels.mdx
  • scripts/nemoclaw-start.sh
  • test/nemoclaw-start-gateway-ws-host.test.ts
  • test/repro-6413-whatsapp-postpair-start.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • scripts/nemoclaw-start.sh
  • test/repro-6413-whatsapp-postpair-start.test.ts

@apurvvkumaria apurvvkumaria self-assigned this Jul 8, 2026
Co-authored-by: harjoth <harjoth.khara@gmail.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
@apurvvkumaria

Copy link
Copy Markdown
Collaborator

Addressed CodeRabbit’s remaining behavioral-test suggestion in signed commit 8df9f738f: the test now sources the generated runtime environment, verifies _NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL resolves to the trusted private gateway, and proves reassignment is rejected. The focused gateway-host suite passes 9/9; commit and pre-push hooks passed. Original author @harjothkhara remains credited as co-author.

@harjothkhara

Copy link
Copy Markdown
Contributor Author

Thanks @apurvvkumaria. My version trusted NEMOCLAW_OPENCLAW_GATEWAY_URL, but a caller can just set that themselves, so switching to the readonly _NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL is the right fix. LGTM.

@cv cv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact-head security review at 8df9f738f found a fail-closed trust-anchor blocker in scripts/nemoclaw-start.sh:3273-3275.

The generated runtime file says a conflicting preexisting readonly _NEMOCLAW_TRUSTED_OPENCLAW_GATEWAY_URL fails closed, but Bash does the opposite here: the assignment prints readonly variable, sourcing continues, the final source status is 0, and the attacker-controlled value remains. The WhatsApp wrapper later treats that value as trusted at lines 3611-3615, so a matching caller-selected gateway URL can retain OPENCLAW_GATEWAY_TOKEN for the login and token-bearing reconcile.

Please make the generated runtime file explicitly verify that the trust anchor equals the baked URL and abort or disable every token-bearing helper when it does not. Add an adversarial regression that predefines a conflicting readonly anchor before sourcing and proves neither the login nor channels.start receives the token. The current tests only verify the normal generated readonly value and do not exercise this failure mode.

Secondary hardening: bound the allowlisted WhatsApp account ID length and test the boundary so an arbitrarily large caller value cannot inflate the JSON/RPC argv.

cv and others added 2 commits July 8, 2026 22:44
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Fail closed on conflicting readonly gateway anchors.

Bound account identifiers before constructing the channel-start RPC.

Co-authored-by: harjoth <harjoth.khara@gmail.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>

@cv cv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact-head follow-up at b8c47c2ff: both requested security fixes are addressed. Conflicting readonly trust anchors now fail closed, clear the ambient token, and return before token-bearing helpers are installed; the adversarial regression proves no token reaches either OpenClaw call. Account IDs are character-validated and bounded to 128 before token read/JSON/RPC, with 128/129 boundary coverage. The focused security suite passes 26/26 and all 31 exact-head checks are green.

@cv cv merged commit 1ff8855 into NVIDIA:main Jul 9, 2026
31 checks passed
@cv

cv commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Post-merge regression found while validating #6444: the trust-anchor code added here is emitted into /tmp/nemoclaw-proxy-env.sh, which sandbox commands source from /bin/sh -lc, but it invokes the Bash-only builtin readonly and builtin unset commands.

On the supported Ubuntu shell, sh -lc 'builtin readonly NEMOCLAW_TEST_ANCHOR=value' exits 127 with builtin: not found. In exact E2E run 29003632720, both onboard-resume and snapshot-commands then hit the fail-closed path and abort authenticated sandbox exec with:

NemoClaw rejected a conflicting gateway trust anchor, and the ambient gateway token could not be cleared.

Four other requested lifecycle jobs passed. The failures occur before #6444-specific lifecycle assertions, and scripts/nemoclaw-start.sh is not in that PR's diff.

This needs a POSIX-shell-compatible trust-anchor implementation that preserves the fail-closed anti-spoofing property, plus a regression that sources the generated runtime file through /bin/sh. Until then, fresh lifecycle evidence for #6444 is blocked.

@cv

cv commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Follow-up #6560 now contains the narrow fix for the post-merge POSIX-shell regression reported above. It keeps the fail-closed trust-anchor/token behavior while making the generated runtime environment sourceable by both Bash and POSIX sh, with focused regression coverage.

jyaunches added a commit that referenced this pull request Jul 9, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Fixes a regression from #6496 where the generated
`/tmp/nemoclaw-proxy-env.sh` used Bash-only `builtin` commands, causing
POSIX login shells to reject the gateway trust anchor and fail lifecycle
operations. The generated file now preserves fail-closed trust behavior
in Bash and POSIX `sh`, including when ordinary Bash functions are
imported from the caller environment.

## Changes

- Invoke the `readonly` and `unset` shell builtins through portable
`command` dispatch in the generated runtime environment.
- Validate source-time anchor values with unshadowable shell `case`
syntax and structurally withhold token re-export unless the exact baked
anchor is readonly.
- Bake the trusted gateway URL directly into the root-owned WhatsApp
helper, independently of caller-preseedable variables.
- Route WhatsApp login and trust decisions with shell syntax that
imported functions cannot intercept.
- Launch caller-selected gateway logins through `/usr/bin/env -u
OPENCLAW_GATEWAY_TOKEN`, so readonly parent variables and imported
`command`, `unset`, or `exit` functions cannot preserve the token in the
child.
- Cover repeated Bash/POSIX-sh sourcing, conflicting and mutable
anchors, readonly tokens, and before/after-source function-shadowing
regressions.

## Type of Change

- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates

<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [ ] Docs updated for user-facing behavior changes
- [x] Docs not applicable — justification: this restores the documented
shell-hook behavior without changing commands, configuration, security
boundaries, or operator workflows.
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification: independent audit
exercised source-time and invocation-time function-shadowing,
readonly-token, poisoned-anchor, mutable-anchor, and caller-selected
gateway paths; the final audit found no remaining bypass in the imported
ordinary-function boundary.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification

<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: `npx
vitest run --project integration
test/nemoclaw-start-gateway-ws-host.test.ts
test/whatsapp-qr-compact.test.ts
test/repro-6413-whatsapp-postpair-start.test.ts` (56/56 passed)
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Carlos Villela <cvillela@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved trusted-gateway and read-only enforcement in POSIX shells,
adding confirmation checks and fail-closed behavior when enforcement
can’t be verified.
* Strengthened WhatsApp gateway login flow to ensure the gateway token
is stripped in non-trusted paths, with clearer detection of
token-clearing failures.
* **Tests**
* Expanded WhatsApp pairing/login and gateway websocket-host probes to
run under both `bash` and POSIX `sh`, with per-shell assertions.
* Updated guard and token-stripping tests to inject per-scenario trusted
gateway URLs and validate outcomes via the `openclaw` call log.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: J. Yaunches <jyaunches@nvidia.com>
@jyaunches jyaunches mentioned this pull request Jul 9, 2026
21 tasks
cv pushed a commit that referenced this pull request Jul 9, 2026
<!-- markdownlint-disable MD041 -->
## Summary
Adds the pre-tag v0.0.79 release notes entry to
`docs/about/release-notes.mdx` so the release plan can be generated
after docs merge.
The entry summarizes the merged v0.0.79 release train across inference,
diagnostics, runtime hardening, policies, onboarding recovery, and
release validation.

## Changes
- Added the v0.0.79 release notes section with linked follow-up
documentation for OpenRouter onboarding, managed vLLM changes,
completion and logging, Deep Agents runtime limits, policy updates,
onboarding recovery, and release validation.
- Source summary:
- #6461 -> `docs/about/release-notes.mdx`: Documents OpenRouter
onboarding support and links to inference/provider references.
- #6271 and #6272 -> `docs/about/release-notes.mdx`: Documents shell
completion and structured logging highlights.
- #6465, #6539, #6570, and #6528 -> `docs/about/release-notes.mdx`:
Documents status route-drift, orphaned sandbox, gateway cleanup, and DGX
Spark express-install diagnostics.
- #6523, #6551, #6484, #6488, #6324, and #6542 ->
`docs/about/release-notes.mdx`: Documents managed vLLM, Qwen3.6 tool
parser, compaction, and timeout/readiness improvements.
- #6559, #6538, #6560, #6568, #6552, #6567, and #6587 ->
`docs/about/release-notes.mdx`: Documents runtime, credential, proxy,
PID namespace, TOML, and provider-state hardening.
- #6541, #5415, #6246, #6496, and #6573 ->
`docs/about/release-notes.mdx`: Documents GitHub policy, Gmail policy,
MCP allowlist, WhatsApp, and messaging-variant updates.
- #6253, #6572, #6444, #6536, and #5860 ->
`docs/about/release-notes.mdx`: Documents onboarding resume and
create-step recovery improvements.
- #6508, #6527, #5506, #6588, #6446, #6447, #6582, #6296, #6367, #6397,
and #6505 -> `docs/about/release-notes.mdx`: Documents docs,
release-risk, and E2E validation updates.

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [x] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates
<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [ ] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [x] Tests not applicable — justification: Release-note prose only.
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [ ] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: Tests
not applicable, release-note prose only.
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

Docs validation note: `npm run docs:check-agent-variants && npm run
docs:check-routes && git diff --check` passed. Full `npm run docs` is
currently blocked before Fern validation because the pinned
`fern-api@5.65.2` package is unavailable from npm (`ETARGET No matching
version found`).

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added release notes for v0.0.79 with a new summary of recent
improvements, including onboarding and inference options, operator/CLI
diagnostics, sandbox recovery hardening, runtime limits, network policy
behavior, and release validation updates.
  * Added updated references and links for the latest release.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
cv added a commit that referenced this pull request Jul 11, 2026
…plugin provenance (#6645)

<!-- markdownlint-disable MD041 -->
## Summary
Fixes two stacked failures that made WhatsApp unusable on a stock
install — pairing succeeded but the channel never ran:

- **Login (#6413):** stop re-injecting the stashed private veth IP URL
into `openclaw channels login`. A private-IP origin makes the gateway's
locality check strip operator scopes (token or not), so the post-pair
`channels.start` was denied with `missing scope: operator.admin`. With
no URL in the env, OpenClaw resolves the loopback gateway from its own
config — same pattern as the `devices approve` wrapper (#4462) — and the
restart succeeds natively. Replaces the #6496 reconcile, which no-ops on
exactly this token-authed connect-shell path (`0:1:1` dispatch arm);
fresh-install validation with #6496 merged still reproduced the error.
- **Plugin trust:** install channel plugins through the `npm-pack:` spec
so OpenClaw records npm provenance for the integrity-verified tarball.
Archive-path installs record archive provenance, which fails the
trusted-official-install check gating `openKeyedStore` on OpenClaw >=
2026.6.10 — the channel crash-looped right after pairing
(`openKeyedStore is only available for trusted plugins in this
release`).
- **Removed with #6496's reconcile:** its config token reader,
`--account` forwarding, and the readonly gateway trust anchor (source of
the DGX Spark/Station regression, #6560). No gateway token moves
anywhere under this design.

**Blast radius:** the npm-pack change flows through the shared
installer, so all externally-installed channels (whatsapp, discord,
slack, msteams, weixin) plus diagnostics-otel/brave gain npm provenance;
telegram is bundled and unaffected. Audited: trust flip is benefit-only
(unblocks keyed stores that today crash or silently degrade), and the
new install layout breaks no path assumption (details below).

**Validated:** WhatsApp end-to-end live on a fresh install — QR pair, no
missing-scope, trusted record, channel running, real message round-trip.
Control run on unfixed main on the same host still fails.

<details>
<summary>Blast-radius audit: why the npm-pack change is safe for the
other channels</summary>

**What actually changes for a channel plugin?** Only two things: (1) the
install ledger now says the plugin came from npm (name, exact version,
integrity) instead of from an anonymous archive file, and (2) the
plugin's files land under
`.openclaw/npm/projects/<hash>/node_modules/<pkg>` instead of
`extensions/<id>`. The tarball itself, and the integrity verification
before installing it, are byte-for-byte the same as before.

**Could the new ledger entry break anything?** No — it only unlocks. The
trusted-official-install check is the single consumer of this
provenance, and today it fails for all five external channels. That is
why WhatsApp crash-loops at startup (it opens `openKeyedStore`
immediately), why Slack silently loses its duplicate-message protection
across restarts, and why Discord's interactive buttons/modals lose their
persistent state. With npm provenance the check passes and those
features work as designed. Nothing consumes "archive" provenance as a
positive signal, so nothing regresses.

**Could the new file location break anything?** We audited every place
that could care about the old path:
- *Runtime patches/preloads:* none match on `extensions/<id>`. The
WhatsApp compact-QR preload recognizes the qrcode module by its shape,
not its path.
- *Sandbox rebuild (backup/restore):* the backup never includes `npm/`,
so after a rebuild the plugin files and ledger always come from the
freshly built image. The old extensions-restore logic simply sees "no
extensions dir to preserve" and moves on — a state it already handles.
- *WeChat seed files:* they are written to the state dir
(`~/.openclaw/openclaw-weixin/`), not into the plugin's install dir, so
the move doesn't touch them. WeChat's hand-written `plugins.installs`
config entry keeps the old path, but the trust check never reads config
— only the ledger — so the stale entry is inert.

**Who is not affected at all?** Telegram — it ships bundled inside the
openclaw package, never goes through this installer, and was already
trusted.

**What was tested?** WhatsApp end-to-end on a fresh install (the only
channel that hard-fails today). The `openclaw-lifecycle-policy` contract
test verifies every reviewed package spec installs through the new
`npm-pack:` form.

</details>

## Related Issue
Fixes #6413

## Changes
<!-- Bullet list of key changes. -->

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [x] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates
<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — targeted replacement of the #6496 login machinery: removes
gateway-token movement entirely (the reconcile helper and its config
token reader are deleted), removes the readonly trust anchor that
regressed DGX Spark/Station, and restores the pre-#6496 runtime-env
emission; the ws:// scheme check on an explicitly exported
`OPENCLAW_GATEWAY_URL` is retained.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [ ] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [ ] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [ ] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification:
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [ ] Quality Gates section completed with required justifications or
waivers
- [ ] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Hung Le <hple@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Improvements**
- Updated OpenClaw plugin installation to use `npm-pack:` based
installs, aligning provenance/version traces (replacing direct archive
installs and `--pin`).
- Adjusted Docker-based plugin installation to use the verified tarball
install path that preserves npm provenance.
- Refreshed WhatsApp pairing/login: defaults to in-sandbox loopback,
accepts gateway URL overrides only for loopback `ws://`/`wss://`, and
exports gateway tokens whenever available.
- **Documentation**
- Shortened WhatsApp sandbox guidance and added troubleshooting for
gateway close code **1008**.
- **Tests**
- Updated messaging/OpenClaw trace expectations for `npm-pack:` formats
and revised WhatsApp guard coverage/fixtures to match the new
pairing/token/exit-code behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Hung Le <hple@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
…dmin (NVIDIA#6496)

<!-- markdownlint-disable MD041 -->
## Summary

Fixes NVIDIA#6413 by restarting the running WhatsApp channel after a
successful QR login without granting `operator.admin` to the pairing
device.
The bounded restart uses gateway-token authentication only when the
login targets the private gateway URL baked into NemoClaw's generated
runtime environment.

The original implementation is by @harjothkhara.
The maintainer security follow-up preserves that credit with
`Co-authored-by: harjoth <harjoth.khara@gmail.com>`.

## Changes

- Reissues the bounded `channels.start` RPC after a successful
token-less WhatsApp login.
- Bakes a separate readonly gateway URL trust anchor for token-bearing
helpers instead of trusting caller-mutable `OPENCLAW_GATEWAY_URL` or
`NEMOCLAW_OPENCLAW_GATEWAY_URL` values.
- Strips the gateway token from a login to any caller-selected URL and
skips the follow-up reconcile.
- Adds the exact regression where a caller spoofs both mutable URL
variables and verifies that no token reaches the login or reconcile.
- Uses command-scoped environment assignments so the generated runtime
file preserves the NVIDIA#6291 no-ambient-export invariant.
- Preserves login exit status, forwards allowlisted `--account` values,
and downgrades reconcile failures to recovery guidance.
- Documents the OpenClaw-only post-pair restart, trust boundary,
custom-URL behavior, and status recovery flow.
- Synchronizes current `main` without rewriting the contributor's
verified commit history.

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [x] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates

- [x] Tests added or updated for changed behavior
- [x] Existing tests cover changed behavior — the existing QR,
runtime-env, gateway-host, permission, and scope-replacement suites
remain green.
- [ ] Tests not applicable — justification:
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — the maintainer follow-up closes the mutable trust-anchor
token-exfiltration path and adds an adversarial regression.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification

- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set — 73
passed and 3 skipped across the focused runtime, WhatsApp, gateway-host,
permission, scope, and token-export coverage.
- [ ] Applicable broad gate passed — normal exact-head CI is pending
after this push.
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only) — passed
with 0 errors and 2 existing repository warnings.
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

### Validation performed

- Focused Vitest suites: 73 passed, 3 skipped
- `npm run docs:sync-agent-variants`
- `npm run docs` (0 errors; 2 existing warnings)
- `npm run test-size:check`
- `npm run test:titles:check`
- `npx prek run shfmt --files scripts/nemoclaw-start.sh`
- `npx prek run shellcheck --files scripts/nemoclaw-start.sh`
- `npm run check:diff`
- Commit and push hooks

> AI-assisted: authored and reviewed with Claude Code; adversarially
cross-reviewed with Codex.

---
Signed-off-by: harjoth <harjoth.khara@gmail.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved WhatsApp experimental login to automatically
reconcile/restart the WhatsApp channel after successful QR pairing when
safe.
* Added `--account` / `--account=...` forwarding to the gateway restart
request, with account ID validation and clear guidance on invalid
values.
* Strengthened gateway-token protection: token is only used for the
trusted private gateway URL (and stripped for untrusted targets);
enhanced recovery guidance when restart/reconciliation can’t run or
fails.
* **Tests**
* Added end-to-end regression coverage for WhatsApp post-pair restart,
parameter forwarding, and recovery/security scenarios.
  * Added a trusted gateway URL read-only alias test.
* **Documentation**
* Expanded WhatsApp sandbox channel documentation with verification
steps and expected restart/status behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: harjoth <harjoth.khara@gmail.com>
Signed-off-by: Apurv Kumaria <akumaria@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Apurv Kumaria <akumaria@nvidia.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
<!-- markdownlint-disable MD041 -->
## Summary

Fixes a regression from NVIDIA#6496 where the generated
`/tmp/nemoclaw-proxy-env.sh` used Bash-only `builtin` commands, causing
POSIX login shells to reject the gateway trust anchor and fail lifecycle
operations. The generated file now preserves fail-closed trust behavior
in Bash and POSIX `sh`, including when ordinary Bash functions are
imported from the caller environment.

## Changes

- Invoke the `readonly` and `unset` shell builtins through portable
`command` dispatch in the generated runtime environment.
- Validate source-time anchor values with unshadowable shell `case`
syntax and structurally withhold token re-export unless the exact baked
anchor is readonly.
- Bake the trusted gateway URL directly into the root-owned WhatsApp
helper, independently of caller-preseedable variables.
- Route WhatsApp login and trust decisions with shell syntax that
imported functions cannot intercept.
- Launch caller-selected gateway logins through `/usr/bin/env -u
OPENCLAW_GATEWAY_TOKEN`, so readonly parent variables and imported
`command`, `unset`, or `exit` functions cannot preserve the token in the
child.
- Cover repeated Bash/POSIX-sh sourcing, conflicting and mutable
anchors, readonly tokens, and before/after-source function-shadowing
regressions.

## Type of Change

- [x] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates

<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [ ] Docs updated for user-facing behavior changes
- [x] Docs not applicable — justification: this restores the documented
shell-hook behavior without changing commands, configuration, security
boundaries, or operator workflows.
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification: independent audit
exercised source-time and invocation-time function-shadowing,
readonly-token, poisoned-anchor, mutable-anchor, and caller-selected
gateway paths; the final audit found no remaining bypass in the imported
ordinary-function boundary.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification

<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: `npx
vitest run --project integration
test/nemoclaw-start-gateway-ws-host.test.ts
test/whatsapp-qr-compact.test.ts
test/repro-6413-whatsapp-postpair-start.test.ts` (56/56 passed)
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Carlos Villela <cvillela@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Improved trusted-gateway and read-only enforcement in POSIX shells,
adding confirmation checks and fail-closed behavior when enforcement
can’t be verified.
* Strengthened WhatsApp gateway login flow to ensure the gateway token
is stripped in non-trusted paths, with clearer detection of
token-clearing failures.
* **Tests**
* Expanded WhatsApp pairing/login and gateway websocket-host probes to
run under both `bash` and POSIX `sh`, with per-shell assertions.
* Updated guard and token-stripping tests to inject per-scenario trusted
gateway URLs and validate outcomes via the `openclaw` call log.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: J. Yaunches <jyaunches@nvidia.com>
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
<!-- markdownlint-disable MD041 -->
## Summary
Adds the pre-tag v0.0.79 release notes entry to
`docs/about/release-notes.mdx` so the release plan can be generated
after docs merge.
The entry summarizes the merged v0.0.79 release train across inference,
diagnostics, runtime hardening, policies, onboarding recovery, and
release validation.

## Changes
- Added the v0.0.79 release notes section with linked follow-up
documentation for OpenRouter onboarding, managed vLLM changes,
completion and logging, Deep Agents runtime limits, policy updates,
onboarding recovery, and release validation.
- Source summary:
- NVIDIA#6461 -> `docs/about/release-notes.mdx`: Documents OpenRouter
onboarding support and links to inference/provider references.
- NVIDIA#6271 and NVIDIA#6272 -> `docs/about/release-notes.mdx`: Documents shell
completion and structured logging highlights.
- NVIDIA#6465, NVIDIA#6539, NVIDIA#6570, and NVIDIA#6528 -> `docs/about/release-notes.mdx`:
Documents status route-drift, orphaned sandbox, gateway cleanup, and DGX
Spark express-install diagnostics.
- NVIDIA#6523, NVIDIA#6551, NVIDIA#6484, NVIDIA#6488, NVIDIA#6324, and NVIDIA#6542 ->
`docs/about/release-notes.mdx`: Documents managed vLLM, Qwen3.6 tool
parser, compaction, and timeout/readiness improvements.
- NVIDIA#6559, NVIDIA#6538, NVIDIA#6560, NVIDIA#6568, NVIDIA#6552, NVIDIA#6567, and NVIDIA#6587 ->
`docs/about/release-notes.mdx`: Documents runtime, credential, proxy,
PID namespace, TOML, and provider-state hardening.
- NVIDIA#6541, NVIDIA#5415, NVIDIA#6246, NVIDIA#6496, and NVIDIA#6573 ->
`docs/about/release-notes.mdx`: Documents GitHub policy, Gmail policy,
MCP allowlist, WhatsApp, and messaging-variant updates.
- NVIDIA#6253, NVIDIA#6572, NVIDIA#6444, NVIDIA#6536, and NVIDIA#5860 ->
`docs/about/release-notes.mdx`: Documents onboarding resume and
create-step recovery improvements.
- NVIDIA#6508, NVIDIA#6527, NVIDIA#5506, NVIDIA#6588, NVIDIA#6446, NVIDIA#6447, NVIDIA#6582, NVIDIA#6296, NVIDIA#6367, NVIDIA#6397,
and NVIDIA#6505 -> `docs/about/release-notes.mdx`: Documents docs,
release-risk, and E2E validation updates.

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [ ] Code change with doc updates
- [x] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates
<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [ ] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [x] Tests not applicable — justification: Release-note prose only.
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [ ] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [ ] Sensitive-path review completed or maintainer-approved waiver
recorded — reviewer/approval link/justification:
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [x] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [x] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [x] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification: Tests
not applicable, release-note prose only.
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [x] Quality Gates section completed with required justifications or
waivers
- [x] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [x] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

Docs validation note: `npm run docs:check-agent-variants && npm run
docs:check-routes && git diff --check` passed. Full `npm run docs` is
currently blocked before Fern validation because the pinned
`fern-api@5.65.2` package is unavailable from npm (`ETARGET No matching
version found`).

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Julie Yaunches <jyaunches@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added release notes for v0.0.79 with a new summary of recent
improvements, including onboarding and inference options, operator/CLI
diagnostics, sandbox recovery hardening, runtime limits, network policy
behavior, and release validation updates.
  * Added updated references and links for the latest release.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Hadar301 pushed a commit to Hadar301/NemoClaw-OpenShift that referenced this pull request Jul 12, 2026
…plugin provenance (NVIDIA#6645)

<!-- markdownlint-disable MD041 -->
## Summary
Fixes two stacked failures that made WhatsApp unusable on a stock
install — pairing succeeded but the channel never ran:

- **Login (NVIDIA#6413):** stop re-injecting the stashed private veth IP URL
into `openclaw channels login`. A private-IP origin makes the gateway's
locality check strip operator scopes (token or not), so the post-pair
`channels.start` was denied with `missing scope: operator.admin`. With
no URL in the env, OpenClaw resolves the loopback gateway from its own
config — same pattern as the `devices approve` wrapper (NVIDIA#4462) — and the
restart succeeds natively. Replaces the NVIDIA#6496 reconcile, which no-ops on
exactly this token-authed connect-shell path (`0:1:1` dispatch arm);
fresh-install validation with NVIDIA#6496 merged still reproduced the error.
- **Plugin trust:** install channel plugins through the `npm-pack:` spec
so OpenClaw records npm provenance for the integrity-verified tarball.
Archive-path installs record archive provenance, which fails the
trusted-official-install check gating `openKeyedStore` on OpenClaw >=
2026.6.10 — the channel crash-looped right after pairing
(`openKeyedStore is only available for trusted plugins in this
release`).
- **Removed with NVIDIA#6496's reconcile:** its config token reader,
`--account` forwarding, and the readonly gateway trust anchor (source of
the DGX Spark/Station regression, NVIDIA#6560). No gateway token moves
anywhere under this design.

**Blast radius:** the npm-pack change flows through the shared
installer, so all externally-installed channels (whatsapp, discord,
slack, msteams, weixin) plus diagnostics-otel/brave gain npm provenance;
telegram is bundled and unaffected. Audited: trust flip is benefit-only
(unblocks keyed stores that today crash or silently degrade), and the
new install layout breaks no path assumption (details below).

**Validated:** WhatsApp end-to-end live on a fresh install — QR pair, no
missing-scope, trusted record, channel running, real message round-trip.
Control run on unfixed main on the same host still fails.

<details>
<summary>Blast-radius audit: why the npm-pack change is safe for the
other channels</summary>

**What actually changes for a channel plugin?** Only two things: (1) the
install ledger now says the plugin came from npm (name, exact version,
integrity) instead of from an anonymous archive file, and (2) the
plugin's files land under
`.openclaw/npm/projects/<hash>/node_modules/<pkg>` instead of
`extensions/<id>`. The tarball itself, and the integrity verification
before installing it, are byte-for-byte the same as before.

**Could the new ledger entry break anything?** No — it only unlocks. The
trusted-official-install check is the single consumer of this
provenance, and today it fails for all five external channels. That is
why WhatsApp crash-loops at startup (it opens `openKeyedStore`
immediately), why Slack silently loses its duplicate-message protection
across restarts, and why Discord's interactive buttons/modals lose their
persistent state. With npm provenance the check passes and those
features work as designed. Nothing consumes "archive" provenance as a
positive signal, so nothing regresses.

**Could the new file location break anything?** We audited every place
that could care about the old path:
- *Runtime patches/preloads:* none match on `extensions/<id>`. The
WhatsApp compact-QR preload recognizes the qrcode module by its shape,
not its path.
- *Sandbox rebuild (backup/restore):* the backup never includes `npm/`,
so after a rebuild the plugin files and ledger always come from the
freshly built image. The old extensions-restore logic simply sees "no
extensions dir to preserve" and moves on — a state it already handles.
- *WeChat seed files:* they are written to the state dir
(`~/.openclaw/openclaw-weixin/`), not into the plugin's install dir, so
the move doesn't touch them. WeChat's hand-written `plugins.installs`
config entry keeps the old path, but the trust check never reads config
— only the ledger — so the stale entry is inert.

**Who is not affected at all?** Telegram — it ships bundled inside the
openclaw package, never goes through this installer, and was already
trusted.

**What was tested?** WhatsApp end-to-end on a fresh install (the only
channel that hard-fails today). The `openclaw-lifecycle-policy` contract
test verifies every reviewed package spec installs through the new
`npm-pack:` form.

</details>

## Related Issue
Fixes NVIDIA#6413

## Changes
<!-- Bullet list of key changes. -->

## Type of Change

- [ ] Code change (feature, bug fix, or refactor)
- [x] Code change with doc updates
- [ ] Doc only (prose changes, no code sample modifications)
- [ ] Doc only (includes code sample changes)

## Quality Gates
<!-- Check exactly one tests line and one docs line. Check other lines
when applicable. Add every requested justification or approval
reference. -->
- [x] Tests added or updated for changed behavior
- [ ] Existing tests cover changed behavior — justification:
- [ ] Tests not applicable — justification:
- [x] Docs updated for user-facing behavior changes
- [ ] Docs not applicable — justification:
- [x] Sensitive paths changed (security, policy, credentials, preflight,
onboarding, inference, runner, sandbox, or messaging)
- [x] Sensitive-path review completed or maintainer-approved waiver
recorded — targeted replacement of the NVIDIA#6496 login machinery: removes
gateway-token movement entirely (the reconcile helper and its config
token reader are deleted), removes the readonly trust anchor that
regressed DGX Spark/Station, and restores the pre-NVIDIA#6496 runtime-env
emission; the ws:// scheme check on an explicitly exported
`OPENCLAW_GATEWAY_URL` is retained.
- [ ] Non-success, skipped, or missing CI check accepted by maintainer —
check name, approval link, and follow-up issue:

## Verification
<!-- Check each applicable item only when supported by the requested
evidence. Run targeted tests once per relevant change set and rerun
after later edits or hook autofixes that can affect the tested behavior.
Do not rerun hook-covered checks. -->
- [ ] PR description includes the DCO sign-off declaration and every
commit appears as `Verified` in GitHub
- [ ] Normal `pre-commit`, `commit-msg`, and `pre-push` hooks passed, or
`npm run check:diff` passed when hooks were skipped or unavailable
- [ ] Targeted behavior tests pass for the current change set, or tests
are marked not applicable above — command/result or justification:
- [ ] Applicable broad gate passed — `npm test` for broad
runtime/test-harness changes; `npm run check` for repo-wide
validation/coverage changes — command/result:
- [ ] Quality Gates section completed with required justifications or
waivers
- [ ] No secrets, API keys, or credentials committed
- [ ] `npm run docs` builds without warnings (doc changes only)
- [ ] Doc pages follow the [style
guide](https://github.com/NVIDIA/NemoClaw/blob/main/docs/CONTRIBUTING.md)
(doc changes only)
- [ ] New doc pages include SPDX header and frontmatter (new pages only)

---
<!-- DCO sign-off is required in this PR description, and every commit
must appear as Verified in GitHub. Run: git config user.name && git
config user.email -->
Signed-off-by: Hung Le <hple@nvidia.com>


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Improvements**
- Updated OpenClaw plugin installation to use `npm-pack:` based
installs, aligning provenance/version traces (replacing direct archive
installs and `--pin`).
- Adjusted Docker-based plugin installation to use the verified tarball
install path that preserves npm provenance.
- Refreshed WhatsApp pairing/login: defaults to in-sandbox loopback,
accepts gateway URL overrides only for loopback `ws://`/`wss://`, and
exports gateway tokens whenever available.
- **Documentation**
- Shortened WhatsApp sandbox guidance and added troubleshooting for
gateway close code **1008**.
- **Tests**
- Updated messaging/OpenClaw trace expectations for `npm-pack:` formats
and revised WhatsApp guard coverage/fixtures to match the new
pairing/token/exit-code behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Hung Le <hple@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Co-authored-by: Carlos Villela <cvillela@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v0.0.79 Release target

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(whatsapp): post-pair gateway restart fails without operator.admin

4 participants