From 88b5448e815f03574ae20b3dbe844cb88c1a5828 Mon Sep 17 00:00:00 2001 From: sauravdev Date: Sat, 4 Jul 2026 09:20:56 +0530 Subject: [PATCH 1/3] docs(security): add credential rotation guide Step-by-step guide for rotating inference API keys, messaging tokens, and other provider credentials without a full sandbox rebuild. Covers emergency rotation, CI/CD workflows, and verification steps. Adds the page to the docs/index.yml navigation for both openclaw and hermes agent variants. Co-Authored-By: Claude Sonnet 4.6 --- docs/index.yml | 6 + docs/security/credential-rotation.mdx | 174 ++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 docs/security/credential-rotation.mdx 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/security/credential-rotation.mdx b/docs/security/credential-rotation.mdx new file mode 100644 index 00000000000..0dff9e489b7 --- /dev/null +++ b/docs/security/credential-rotation.mdx @@ -0,0 +1,174 @@ +--- +# 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: "How to update or rotate API keys and provider credentials in a running NemoClaw sandbox without triggering a full re-onboard." +description-agent: "Step-by-step guide for rotating inference API keys, messaging tokens, and other provider credentials in NemoClaw. Use when a key expires, is compromised, or needs to be changed without rebuilding the sandbox." +keywords: ["nemoclaw credential rotation", "rotate api key", "update inference key", "rotate without reonboard", "nemoclaw credentials reset"] +content: + type: "how_to" +--- +import { AgentOnly } from "../_components/AgentGuide"; + +Rotate a credential in NemoClaw without tearing down or rebuilding your sandbox. +The update takes effect as soon as the new value is registered with the OpenShell gateway. +No sandbox restart is required for most providers. + +## Before You Start + +List the provider names currently registered in the gateway so you know the exact name to target: + +```bash +$$nemoclaw credentials list +``` + +Example output: + +```text +alpha-nvidia-inference +alpha-slack +alpha-brave-search +``` + +The name pattern is `-`. +Use the exact name when running `credentials reset` or updating a provider directly through `openshell`. + +## Rotate an Inference API Key + +Export the new key and re-run onboarding. +NemoClaw calls `openshell provider update` internally, replacing the stored value without rebuilding the sandbox: + +```bash +NVIDIA_INFERENCE_API_KEY=nvapi-new-value $$nemoclaw onboard +``` + +NemoClaw detects that a sandbox is already registered and skips Docker and OpenShell setup steps, going straight to credential re-registration. + +To skip the interactive prompts entirely: + +```bash +NVIDIA_INFERENCE_API_KEY=nvapi-new-value $$nemoclaw onboard --non-interactive --yes +``` + + +If the onboarding session was started while the old key was valid, running inference commands before the gateway propagates the new credential may yield 401 errors for a few seconds. +Retry the failing request once the `$$nemoclaw onboard` command completes. + + +## Rotate a Messaging Token + +### Slack + +```bash +SLACK_BOT_TOKEN=xoxb-new-value \ +SLACK_APP_TOKEN=xapp-new-value \ +$$nemoclaw onboard --non-interactive --yes +``` + +### Telegram + +```bash +TELEGRAM_BOT_TOKEN=new-bot-token $$nemoclaw onboard --non-interactive --yes +``` + +### Discord + +```bash +DISCORD_BOT_TOKEN=new-token $$nemoclaw onboard --non-interactive --yes +``` + + +For Hermes-managed messaging channels, rotate the channel token through the same onboard path. +NemoClaw re-registers the new value with the OpenShell provider and restarts the in-sandbox bridge without rebuilding the full sandbox image. + + +## Rotate a Web Search Key + +```bash +BRAVE_API_KEY=new-key $$nemoclaw onboard --non-interactive --yes +# or +TAVILY_API_KEY=new-key $$nemoclaw onboard --non-interactive --yes +``` + +## Remove and Re-register a Credential + +Use this two-step approach when you want the next `$$nemoclaw onboard` to prompt interactively for a fresh value, or when you need to clear a compromised credential immediately before the replacement is available. + +Step 1: Remove the stored credential. + +```bash +$$nemoclaw credentials reset alpha-nvidia-inference +``` + +This removes the OpenShell provider entry. +The sandbox continues running, but inference requests that reach the gateway will fail until a valid credential is registered again. + +Step 2: Re-register the new value. + +```bash +NVIDIA_INFERENCE_API_KEY=nvapi-new-value $$nemoclaw onboard --non-interactive --yes +``` + +Or run `$$nemoclaw onboard` without the environment variable to be prompted interactively. + +## Emergency Rotation After Key Compromise + +If a key was exposed, act in this order: + +1. Revoke the key at the provider dashboard (NVIDIA API Catalog, Slack, Telegram, etc.) before doing anything else. +2. Remove it from the gateway immediately so the sandbox cannot use the compromised value: + + ```bash + $$nemoclaw credentials reset - + ``` + +3. Issue a replacement key at the provider dashboard. +4. Register the new key: + + ```bash + PROVIDER_KEY=new-value $$nemoclaw onboard --non-interactive --yes + ``` + +5. Audit sandbox logs for unintended use of the old key: + + ```bash + $$nemoclaw logs --since 24h | grep -i "auth\|401\|403\|forbidden" + ``` + +## Rotate Credentials in CI/CD + +In automated pipelines, supply credentials through environment variables at invocation time. +The gateway stores the new value and subsequent sandbox runs pick it up automatically: + +```bash +# Example GitHub Actions step +- name: Rotate inference key + env: + NVIDIA_INFERENCE_API_KEY: ${{ secrets.NVIDIA_INFERENCE_API_KEY }} + run: $$nemoclaw onboard --non-interactive --yes +``` + +Do not commit credential values or `.nemoclaw/` state to the repository. +Use your CI provider's secret store and inject credentials through environment variables only. + +## Verify the New Credential Is Active + +After rotating, confirm the gateway accepted the new credential: + +```bash +# Check overall status, including inference health +$$nemoclaw status + +# Or check inference specifically +$$nemoclaw inference get +``` + +A healthy status response confirms the gateway is accepting requests with the new credential. +If status reports an error, re-run the rotation step and check that the environment variable name matches exactly (names are case-sensitive). + +## Related Pages + +- [Credential Storage](credential-storage): how NemoClaw stores and protects credentials. +- [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 a key. From 4f2ef44597c1850c1e1f6a91e75fc7384a5dcb3f Mon Sep 17 00:00:00 2001 From: Carlos Villela Date: Thu, 9 Jul 2026 01:02:15 -0700 Subject: [PATCH 2/3] docs(security): require fresh web search rotation Signed-off-by: Carlos Villela --- docs/security/credential-rotation.mdx | 4 ++-- test/credential-rotation-docs.test.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/security/credential-rotation.mdx b/docs/security/credential-rotation.mdx index 4bc49f7c615..b8ed0b02250 100644 --- a/docs/security/credential-rotation.mdx +++ b/docs/security/credential-rotation.mdx @@ -100,7 +100,7 @@ OpenClaw supports Brave or Tavily through NemoClaw onboarding. ```bash NEMOCLAW_WEB_SEARCH_PROVIDER=brave \ BRAVE_API_KEY=new-key \ - $$nemoclaw onboard --name --recreate-sandbox \ + $$nemoclaw onboard --fresh --name --recreate-sandbox \ --non-interactive --yes --yes-i-accept-third-party-software ``` @@ -115,7 +115,7 @@ Brave is not a supported Hermes web search backend. ```bash NEMOCLAW_WEB_SEARCH_PROVIDER=tavily \ TAVILY_API_KEY=new-key \ - $$nemoclaw onboard --name --recreate-sandbox \ + $$nemoclaw onboard --fresh --name --recreate-sandbox \ --non-interactive --yes --yes-i-accept-third-party-software ``` diff --git a/test/credential-rotation-docs.test.ts b/test/credential-rotation-docs.test.ts index a1fa03a69ea..9616ac0daed 100644 --- a/test/credential-rotation-docs.test.ts +++ b/test/credential-rotation-docs.test.ts @@ -48,6 +48,7 @@ describe("credential rotation documentation", () => { 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"); } }); From 1473591c6c58059e70cd8410f8d04f2b3674b502 Mon Sep 17 00:00:00 2001 From: Carlos Villela Date: Thu, 9 Jul 2026 09:03:59 -0700 Subject: [PATCH 3/3] docs(security): keep rotated secrets out of shell history Signed-off-by: Carlos Villela --- docs/security/credential-rotation.mdx | 55 +++++++++++++++++++-------- test/credential-rotation-docs.test.ts | 19 +++++++++ 2 files changed, 59 insertions(+), 15 deletions(-) diff --git a/docs/security/credential-rotation.mdx b/docs/security/credential-rotation.mdx index b8ed0b02250..4fdd7bae56d 100644 --- a/docs/security/credential-rotation.mdx +++ b/docs/security/credential-rotation.mdx @@ -39,11 +39,17 @@ Use `channels add`, `channels remove`, or `channels stop` for messaging integrat ## 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 -NVIDIA_INFERENCE_API_KEY=nvapi-new-value \ - $$nemoclaw onboard --name \ +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. @@ -61,28 +67,39 @@ The replacement takes effect only after a rebuild because messaging providers ar Slack requires both replacement tokens. ```bash -NEMOCLAW_NON_INTERACTIVE=1 \ -SLACK_BOT_TOKEN=xoxb-new-value \ -SLACK_APP_TOKEN=xapp-new-value \ - $$nemoclaw channels add slack +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 -NEMOCLAW_NON_INTERACTIVE=1 \ -TELEGRAM_BOT_TOKEN=new-bot-token \ - $$nemoclaw channels add telegram +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 -NEMOCLAW_NON_INTERACTIVE=1 \ -DISCORD_BOT_TOKEN=new-token \ - $$nemoclaw channels add discord +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 ``` @@ -98,13 +115,17 @@ Select the provider again and recreate the sandbox so the replacement becomes ac 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 \ -BRAVE_API_KEY=new-key \ $$nemoclaw onboard --fresh --name --recreate-sandbox \ --non-interactive --yes --yes-i-accept-third-party-software +unset BRAVE_API_KEY ``` -Use `NEMOCLAW_WEB_SEARCH_PROVIDER=tavily` with `TAVILY_API_KEY` to rotate a Tavily key instead. +To rotate a Tavily key instead, read and export `TAVILY_API_KEY` silently and select `NEMOCLAW_WEB_SEARCH_PROVIDER=tavily`. @@ -113,10 +134,14 @@ 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 \ -TAVILY_API_KEY=new-key \ $$nemoclaw onboard --fresh --name --recreate-sandbox \ --non-interactive --yes --yes-i-accept-third-party-software +unset TAVILY_API_KEY ``` diff --git a/test/credential-rotation-docs.test.ts b/test/credential-rotation-docs.test.ts index 9616ac0daed..da3d1fef17e 100644 --- a/test/credential-rotation-docs.test.ts +++ b/test/credential-rotation-docs.test.ts @@ -35,6 +35,25 @@ describe("credential rotation documentation", () => { 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");