diff --git a/docs/index.yml b/docs/index.yml
index ce7f6b6babd..3a79e8e4d5b 100644
--- a/docs/index.yml
+++ b/docs/index.yml
@@ -140,6 +140,9 @@ navigation:
- page: "Credential Storage"
path: _build/agent-variants/security/credential-storage.openclaw.generated.mdx
slug: credential-storage
+ - page: "Credential Rotation"
+ path: _build/agent-variants/security/credential-rotation.openclaw.generated.mdx
+ slug: credential-rotation
- page: "Trusted Computing Base"
path: _build/agent-variants/security/tcb-boundary.openclaw.generated.mdx
slug: trusted-computing-base
@@ -427,6 +430,9 @@ navigation:
- page: "Credential Storage"
path: _build/agent-variants/security/credential-storage.hermes.generated.mdx
slug: credential-storage
+ - page: "Credential Rotation"
+ path: _build/agent-variants/security/credential-rotation.hermes.generated.mdx
+ slug: credential-rotation
- page: "Trusted Computing Base"
path: _build/agent-variants/security/tcb-boundary.hermes.generated.mdx
slug: trusted-computing-base
diff --git a/docs/reference/commands.mdx b/docs/reference/commands.mdx
index dfd0a166d64..b25905e06f7 100644
--- a/docs/reference/commands.mdx
+++ b/docs/reference/commands.mdx
@@ -1454,10 +1454,19 @@ Do not log it, share it, or commit it to version control.
-`gateway-token` is not applicable to Hermes sandboxes.
-Hermes API access uses bearer-token authentication configured through the Hermes runtime, not the OpenClaw gateway token.
-For browser access to the dashboard, use `nemohermes my-assistant dashboard-url`; Hermes dashboard auth is read from the in-sandbox config (`/sandbox/.hermes/config.yaml`), not a gateway token.
-If you need the endpoint for an OpenAI-compatible client, use `nemohermes my-assistant status` and the API URL it reports.
+Print the Hermes API bearer token for a running sandbox to stdout.
+NemoClaw retrieves the sandbox's `API_SERVER_KEY`, which authenticates OpenAI-compatible clients on the forwarded API port.
+Capture the token and pass it in the `Authorization` header:
+
+```bash
+TOKEN=$(nemohermes my-assistant gateway-token --quiet)
+curl -fsS -H "Authorization: Bearer $TOKEN" \
+ http://127.0.0.1:8642/v1/models
+```
+
+Treat the token like a password.
+Do not log it, share it, or commit it to version control.
+For browser access to the dashboard, use `nemohermes my-assistant dashboard-url`.
diff --git a/docs/security/credential-rotation.mdx b/docs/security/credential-rotation.mdx
new file mode 100644
index 00000000000..4fdd7bae56d
--- /dev/null
+++ b/docs/security/credential-rotation.mdx
@@ -0,0 +1,239 @@
+---
+# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
+title: "Credential Rotation"
+sidebar-title: "Credential Rotation"
+description: "Rotate inference, messaging, and web search credentials through the supported NemoClaw workflows."
+description-agent: "Step-by-step guide for rotating inference API keys, messaging tokens, and web search credentials in NemoClaw. Use when a key expires, is compromised, or must be replaced."
+keywords: ["nemoclaw credential rotation", "rotate api key", "update inference key", "messaging token", "nemoclaw credentials reset"]
+content:
+ type: "how_to"
+---
+import { AgentOnly } from "../_components/AgentGuide";
+
+NemoClaw uses different rotation paths for inference, messaging, and web search credentials.
+Inference credentials can normally be updated while reusing the existing sandbox.
+Messaging tokens and web search settings require a rebuild or recreation because their configuration is applied when the sandbox image starts.
+
+## Before You Start
+
+List the provider names registered with the OpenShell gateway.
+
+```bash
+$$nemoclaw credentials list
+```
+
+The output is authoritative for commands that accept an OpenShell provider name.
+An inference provider is commonly named `nvidia-prod`, while an onboarded web search provider is commonly named `-brave-search` or `-tavily-search`.
+Provider names can differ with your selected inference route and sandbox configuration.
+
+Per-sandbox messaging bridge names are not resettable credentials.
+Use `channels add`, `channels remove`, or `channels stop` for messaging integrations instead of passing a bridge name to `credentials reset`.
+
+| Credential | Supported rotation path | Sandbox impact |
+|---|---|---|
+| Inference API key | Rerun onboarding with the replacement value | The existing sandbox can normally be reused unless onboarding detects unrelated configuration drift |
+| Slack, Telegram, or Discord token | Re-add the channel, then rebuild | Rebuild required |
+| Brave or Tavily web search key | Rerun onboarding with the selected web search provider | Sandbox recreation required |
+
+## Rotate an Inference API Key
+
+Supply the replacement key and rerun onboarding for the existing sandbox.
+Read replacement credentials silently on a trusted host so their values do not enter shell history or terminal scrollback.
+Unset each variable after the command finishes.
+
+```bash
+printf 'New NVIDIA inference API key: ' >&2
+IFS= read -r -s NVIDIA_INFERENCE_API_KEY
+printf '\n' >&2
+export NVIDIA_INFERENCE_API_KEY
+$$nemoclaw onboard --name \
+ --non-interactive --yes --yes-i-accept-third-party-software
+unset NVIDIA_INFERENCE_API_KEY
+```
+
+Onboarding updates the selected OpenShell inference provider and reuses the sandbox when its recorded configuration is still compatible.
+If onboarding detects other configuration drift, review the requested rebuild or recreation before continuing.
+
+For an interactive rotation, export the replacement key and run `$$nemoclaw onboard --name ` without the non-interactive flags.
+
+## Rotate a Messaging Token
+
+Re-adding an existing channel overwrites its stored credentials.
+The replacement takes effect only after a rebuild because messaging providers are resolved when the sandbox starts.
+
+### Slack
+
+Slack requires both replacement tokens.
+
+```bash
+printf 'New Slack bot token: ' >&2
+IFS= read -r -s SLACK_BOT_TOKEN
+printf '\n' >&2
+printf 'New Slack app token: ' >&2
+IFS= read -r -s SLACK_APP_TOKEN
+printf '\n' >&2
+export SLACK_BOT_TOKEN SLACK_APP_TOKEN
+NEMOCLAW_NON_INTERACTIVE=1 $$nemoclaw channels add slack
+unset SLACK_BOT_TOKEN SLACK_APP_TOKEN
+$$nemoclaw rebuild --yes
+```
+
+### Telegram
+
+```bash
+printf 'New Telegram bot token: ' >&2
+IFS= read -r -s TELEGRAM_BOT_TOKEN
+printf '\n' >&2
+export TELEGRAM_BOT_TOKEN
+NEMOCLAW_NON_INTERACTIVE=1 $$nemoclaw channels add telegram
+unset TELEGRAM_BOT_TOKEN
+$$nemoclaw rebuild --yes
+```
+
+### Discord
+
+```bash
+printf 'New Discord bot token: ' >&2
+IFS= read -r -s DISCORD_BOT_TOKEN
+printf '\n' >&2
+export DISCORD_BOT_TOKEN
+NEMOCLAW_NON_INTERACTIVE=1 $$nemoclaw channels add discord
+unset DISCORD_BOT_TOKEN
+$$nemoclaw rebuild --yes
+```
+
+Omit `NEMOCLAW_NON_INTERACTIVE=1` and the token variables if you want `channels add` to prompt for replacement values and offer the rebuild interactively.
+
+## Rotate a Web Search Key
+
+Web search provider configuration and credential attachment are baked into the sandbox image.
+Select the provider again and recreate the sandbox so the replacement becomes active.
+
+
+
+OpenClaw supports Brave or Tavily through NemoClaw onboarding.
+
+```bash
+printf 'New Brave API key: ' >&2
+IFS= read -r -s BRAVE_API_KEY
+printf '\n' >&2
+export BRAVE_API_KEY
+NEMOCLAW_WEB_SEARCH_PROVIDER=brave \
+ $$nemoclaw onboard --fresh --name --recreate-sandbox \
+ --non-interactive --yes --yes-i-accept-third-party-software
+unset BRAVE_API_KEY
+```
+
+To rotate a Tavily key instead, read and export `TAVILY_API_KEY` silently and select `NEMOCLAW_WEB_SEARCH_PROVIDER=tavily`.
+
+
+
+
+Hermes uses Tavily for NemoClaw-managed web search.
+Brave is not a supported Hermes web search backend.
+
+```bash
+printf 'New Tavily API key: ' >&2
+IFS= read -r -s TAVILY_API_KEY
+printf '\n' >&2
+export TAVILY_API_KEY
+NEMOCLAW_WEB_SEARCH_PROVIDER=tavily \
+ $$nemoclaw onboard --fresh --name --recreate-sandbox \
+ --non-interactive --yes --yes-i-accept-third-party-software
+unset TAVILY_API_KEY
+```
+
+
+
+## Remove and Re-register a Provider Credential
+
+Use `credentials reset` only when you need to remove an inference or web search provider before its replacement is available.
+Run `$$nemoclaw credentials list` first, then pass the exact provider name from that output.
+
+```bash
+$$nemoclaw credentials reset nvidia-prod --yes
+```
+
+Removing a provider interrupts requests that depend on it.
+Re-register an inference provider with the inference rotation command above.
+For web search, repeat the matching web search onboarding flow and recreate the sandbox.
+
+Do not pass a messaging bridge name to `credentials reset`.
+Use the messaging rotation flow above to replace a token, or `$$nemoclaw channels remove ` to retire the integration.
+
+## Emergency Rotation After Key Compromise
+
+If a credential was exposed, act in this order:
+
+1. Revoke the exposed credential at the upstream provider before relying on any local cleanup.
+2. For inference or web search, remove the exact provider shown by `$$nemoclaw credentials list` if a replacement is not immediately available.
+3. For messaging, use `channels remove` to retire the integration or re-add the channel with a replacement token and rebuild.
+4. Issue a replacement credential at the upstream provider.
+5. Follow the matching rotation procedure on this page.
+6. Complete a real request through the affected inference, search, or messaging integration.
+7. Review recent sandbox logs for unexpected authentication failures or use of the retired credential:
+
+ ```bash
+ $$nemoclaw logs --since 24h | grep -i "auth\|401\|403\|forbidden"
+ ```
+
+## Rotate an Inference Key in CI/CD
+
+Supply credentials through the CI system's secret store and inject them as environment variables.
+Non-interactive onboarding must include the third-party software acceptance flag.
+
+```yaml
+- name: Rotate inference key
+ env:
+ NVIDIA_INFERENCE_API_KEY: ${{ secrets.NVIDIA_INFERENCE_API_KEY }}
+ run: >-
+ $$nemoclaw onboard --name
+ --non-interactive --yes --yes-i-accept-third-party-software
+```
+
+Do not commit credential values or host-side NemoClaw state to the repository.
+Plan for rebuild downtime when automating messaging rotation and recreation downtime when automating web search rotation.
+
+## Verify the Replacement
+
+`$$nemoclaw credentials list` confirms that a provider exists, but it does not reveal or validate the stored value.
+`$$nemoclaw status` performs reachability checks without sending cloud API keys, so even an HTTP `401` or `403` can count as reachable.
+`$$nemoclaw inference get` reports the active route and does not authenticate a model request.
+
+Complete a real request through the rotated integration before declaring the rotation successful.
+
+
+
+Verify an inference key by running an isolated OpenClaw turn and confirming that it returns the requested content.
+
+```bash
+$$nemoclaw agent --session-id credential-check \
+ -m "Reply with only: credential-check-ok"
+```
+
+
+
+
+Verify an inference key by forwarding the Hermes API and making a chat-completions request with the onboarded model.
+
+```bash
+TOKEN=$($$nemoclaw gateway-token --quiet)
+openshell forward start --background 8642
+curl -sN http://127.0.0.1:8642/v1/chat/completions \
+ -H 'Content-Type: application/json' \
+ -H "Authorization: Bearer $TOKEN" \
+ -d '{"model":"","messages":[{"role":"user","content":"Reply with credential-check-ok"}],"stream":false}'
+```
+
+
+
+To verify messaging, send a test message from an allowed account and confirm the sandbox receives it and responds.
+To verify web search, ask the agent to perform a search and confirm that the tool returns current results without an authentication error.
+
+## Related Pages
+
+- [Credential Storage](credential-storage): how NemoClaw stores and protects credentials.
+- [Runtime Controls](../manage-sandboxes/runtime-controls): which changes take effect at runtime and which require recreation.
+- [Security Best Practices](best-practices): recommended key scopes and operational hygiene.
+- [Switch Inference Providers](../inference/switch-inference-providers): change the inference provider rather than rotate its key.
diff --git a/test/credential-rotation-docs.test.ts b/test/credential-rotation-docs.test.ts
new file mode 100644
index 00000000000..da3d1fef17e
--- /dev/null
+++ b/test/credential-rotation-docs.test.ts
@@ -0,0 +1,95 @@
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+import { readFileSync } from "node:fs";
+import path from "node:path";
+
+import { describe, expect, it } from "vitest";
+
+const DOC_PATH = "docs/security/credential-rotation.mdx";
+
+function readGuide(): string {
+ return readFileSync(path.join(process.cwd(), DOC_PATH), "utf8");
+}
+
+function fencedBlocks(text: string, language: string): string[] {
+ const pattern = new RegExp("```" + language + "\\n([\\s\\S]*?)```", "g");
+ return [...text.matchAll(pattern)].map((match) => match[1] ?? "");
+}
+
+describe("credential rotation documentation", () => {
+ it("keeps every non-interactive onboard example executable", () => {
+ const examples = [
+ ...fencedBlocks(readGuide(), "bash"),
+ ...fencedBlocks(readGuide(), "yaml"),
+ ].filter((block) => block.includes("onboard") && block.includes("--non-interactive"));
+
+ expect(examples.length).toBeGreaterThan(0);
+ for (const example of examples) {
+ expect(example).toContain("--name ");
+ expect(example).toContain("--yes-i-accept-third-party-software");
+ }
+ });
+
+ it("uses normal onboarding instead of interrupted-session resume", () => {
+ expect(readGuide()).not.toContain("--resume");
+ });
+
+ it("keeps replacement credentials out of command text (#6266)", () => {
+ const guide = readGuide();
+ const credentialVariables = [
+ "NVIDIA_INFERENCE_API_KEY",
+ "SLACK_BOT_TOKEN",
+ "SLACK_APP_TOKEN",
+ "TELEGRAM_BOT_TOKEN",
+ "DISCORD_BOT_TOKEN",
+ "BRAVE_API_KEY",
+ "TAVILY_API_KEY",
+ ];
+
+ for (const variable of credentialVariables) {
+ expect(guide).toMatch(new RegExp(`IFS= read -r -s ${variable}`));
+ expect(guide).toMatch(new RegExp(`unset [^\\n]*\\b${variable}\\b`));
+ expect(guide).not.toMatch(new RegExp(`${variable}=[^\\s$]`));
+ }
+ });
+
+ it("documents messaging rebuilds and web search recreation", () => {
+ const guide = readGuide();
+ const bash = fencedBlocks(guide, "bash");
+
+ for (const channel of ["slack", "telegram", "discord"]) {
+ const example = bash.find((block) => block.includes(`channels add ${channel}`));
+ expect(example, channel).toBeDefined();
+ expect(example, channel).toContain("rebuild --yes");
+ }
+
+ const searchExamples = bash.filter((block) => block.includes("NEMOCLAW_WEB_SEARCH_PROVIDER"));
+ expect(searchExamples.length).toBeGreaterThan(0);
+ for (const example of searchExamples) {
+ expect(example).toContain("--fresh");
+ expect(example).toContain("--recreate-sandbox");
+ }
+ });
+
+ it("uses real provider names and separates configuration checks from live proof", () => {
+ const guide = readGuide();
+
+ expect(guide).toContain("credentials reset nvidia-prod --yes");
+ expect(guide).toContain("Per-sandbox messaging bridge names are not resettable credentials");
+ expect(guide).toContain("Complete a real request through the rotated integration");
+ expect(guide).not.toContain("alpha-nvidia-inference");
+ expect(guide).not.toContain("alpha-slack");
+ expect(guide).not.toContain("PROVIDER_KEY=new-value");
+ });
+
+ it("authenticates the Hermes verification request", () => {
+ const example = fencedBlocks(readGuide(), "bash").find((block) =>
+ block.includes("/v1/chat/completions"),
+ );
+
+ expect(example).toBeDefined();
+ expect(example).toContain("gateway-token --quiet");
+ expect(example).toContain("Authorization: Bearer $TOKEN");
+ });
+});