Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
1729b5f
fix(credentials): make the OpenShell gateway the only credential store
ericksoa Apr 27, 2026
9638230
docs(security): describe gateway-backed credential storage
ericksoa Apr 27, 2026
88f5d8c
fix(credentials): address review feedback on legacy migration
ericksoa Apr 27, 2026
879a5cc
docs(security): tighten credential-storage prose per review
ericksoa Apr 27, 2026
3665de2
docs(credentials): add JSDoc for exported helpers and migration entry…
ericksoa Apr 27, 2026
7894fc2
test(e2e): add credential migration nightly job
ericksoa Apr 27, 2026
d207d6d
docs(quickstart): repair credentials-reset cross-reference
ericksoa Apr 27, 2026
34a3b20
fix(test): unbreak credentials.test.ts typecheck
ericksoa Apr 27, 2026
bb3d581
Merge remote-tracking branch 'origin/main' into fix/credentials-gatew…
ericksoa Apr 28, 2026
6a8fac5
test(e2e): satisfy shellcheck on credential-migration script
ericksoa Apr 28, 2026
9091113
Merge branch 'main' into fix/credentials-gateway-only-signed
ericksoa Apr 28, 2026
c396244
fix(credentials): tighten legacy migration semantics per CodeRabbit r…
ericksoa Apr 28, 2026
53fcfb5
Merge remote-tracking branch 'origin/fix/credentials-gateway-only-sig…
ericksoa Apr 28, 2026
0230a0b
docs(credentials): describe gh CLI fallback when no keychain is reach…
ericksoa Apr 28, 2026
65a3419
fix(credentials): adversarial-review hardening pass
ericksoa Apr 28, 2026
d9ade91
chore: pick up formatter changes the pre-push hook is gating on
ericksoa Apr 28, 2026
6b5ae16
Merge branch 'main' into fix/credentials-gateway-only-signed
ericksoa Apr 28, 2026
eec1502
fix(credentials): close TOCTOU window and tighten legacy-file cleanup…
ericksoa Apr 28, 2026
a6a68f8
Merge remote-tracking branch 'origin/fix/credentials-gateway-only-sig…
ericksoa Apr 28, 2026
9b72a10
fix(credentials): track migrated value, not just key, and tighten guards
ericksoa Apr 28, 2026
5225738
fix(credentials): hide and refuse per-sandbox messaging bridge providers
ericksoa Apr 28, 2026
9681ca5
fix(channels): apply add/remove to gateway and registry eagerly
ericksoa Apr 28, 2026
d6cd6a8
fix(credentials): withdraw migration mark when a later upsert replace…
ericksoa Apr 28, 2026
5953625
fix(credentials): close ancestor-symlink + resume + slack-app + delet…
ericksoa Apr 28, 2026
69df1e3
Merge branch 'main' into fix/credentials-gateway-only-signed
ericksoa Apr 28, 2026
37dfb58
fix(credentials): persist value hash and gate inheritance on resume +…
ericksoa Apr 28, 2026
16ba285
Merge remote-tracking branch 'origin/fix/credentials-gateway-only-sig…
ericksoa Apr 28, 2026
dac9a33
Merge branch 'main' into fix/credentials-gateway-only-signed
ericksoa Apr 28, 2026
dc020ba
Merge branch 'main' into fix/credentials-gateway-only-signed
ericksoa Apr 28, 2026
7a50f47
test(onboard): align stale legacy-migration comment with non-destruct…
ericksoa Apr 28, 2026
7f629bd
Merge remote-tracking branch 'origin/fix/credentials-gateway-only-sig…
ericksoa Apr 28, 2026
ab95447
Merge branch 'main' into fix/credentials-gateway-only-signed
ericksoa Apr 28, 2026
5bc89d6
test(credentials): use allowlisted env-key in canonical normalization…
ericksoa Apr 28, 2026
a11901e
fix(onboard): satisfy no-direct-credential-env on upsertProvider valu…
ericksoa Apr 28, 2026
de95d0e
Merge remote-tracking branch 'origin/main' into fix/credentials-gatew…
ericksoa Apr 28, 2026
324c4b6
Merge remote-tracking branch 'origin/main' into fix/credentials-gatew…
ericksoa Apr 28, 2026
f1f8b9b
Merge remote-tracking branch 'origin/main' into fix/credentials-gatew…
ericksoa Apr 28, 2026
347e21e
test(credentials): relax WSL legacy hydration timeout
ericksoa Apr 28, 2026
f937e6c
test(wsl): extend credential lint subprocess timeout
ericksoa Apr 28, 2026
c642070
Merge remote-tracking branch 'origin/main' into fix/credentials-gatew…
ericksoa Apr 28, 2026
d23e145
Merge remote-tracking branch 'origin/main' into fix/credentials-gatew…
ericksoa Apr 28, 2026
b26a893
Merge remote-tracking branch 'origin/main' into fix/credentials-gatew…
ericksoa Apr 28, 2026
d1072bb
docs(credentials): clarify verified legacy cleanup gate
ericksoa Apr 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .agents/skills/nemoclaw-user-configure-security/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: "nemoclaw-user-configure-security"
description: "Presents a risk framework for every configurable security control in NemoClaw. Use when evaluating security posture, reviewing sandbox security defaults, or assessing control trade-offs. Trigger keywords - nemoclaw security best practices, sandbox security controls risk framework, nemoclaw credential storage, credentials.json, api key security, openclaw security controls, nemoclaw security boundary, prompt injection, tool access control."
description: "Presents a risk framework for every configurable security control in NemoClaw. Use when evaluating security posture, reviewing sandbox security defaults, or assessing control trade-offs. Trigger keywords - nemoclaw security best practices, sandbox security controls risk framework, nemoclaw credential storage, openshell provider, api key security, openclaw security controls, nemoclaw security boundary, prompt injection, tool access control."
---

<!-- SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -->
Expand All @@ -12,4 +12,4 @@ description: "Presents a risk framework for every configurable security control

- **Load [references/best-practices.md](references/best-practices.md)** when evaluating security posture, reviewing sandbox security defaults, or assessing control trade-offs. Presents a risk framework for every configurable security control in NemoClaw.
- **Load [references/openclaw-controls.md](references/openclaw-controls.md)** when reviewing the security boundary between NemoClaw and OpenClaw or assessing what NemoClaw does not cover. Lists OpenClaw security controls that operate independently of NemoClaw, including prompt injection detection, tool access control, rate limiting, environment variable policy, audit framework, supply chain scanning, messaging access policy, context visibility, and safe regex.
- **Load [references/credential-storage.md](references/credential-storage.md)** when reviewing how credentials are handled, locating a specific credential file, or assessing the risk of the unencrypted-at-rest default. Covers where NemoClaw stores provider credentials, the file permissions applied, and the trade-offs of plaintext local storage.
- **Load [references/credential-storage.md](references/credential-storage.md)** when reviewing how credentials are handled, locating a stored credential, or assessing the storage threat model. Covers where NemoClaw stores provider credentials, why nothing is persisted to host disk, and how the OpenShell gateway acts as the single system of record.
Original file line number Diff line number Diff line change
Expand Up @@ -2,112 +2,107 @@
<!-- SPDX-License-Identifier: Apache-2.0 -->
# Credential Storage

NemoClaw stores operator-provided host-side credentials under `~/.nemoclaw/`.
These credentials are used during onboarding and host-side lifecycle operations.
They are not encrypted at rest by NemoClaw.
Instead, NemoClaw relies on local filesystem ownership and Unix permissions to limit access.
NemoClaw does not persist provider credentials to host disk.
The OpenShell gateway is the only system of record for stored credentials.

## Location and Permissions
When you provide a provider credential — interactively during `nemoclaw onboard` or via an environment variable — NemoClaw holds the value in memory only long enough to register it with the OpenShell gateway through `openshell provider create` or `openshell provider update`.
The gateway stores the credential and the OpenShell L7 proxy substitutes it into outbound requests at egress, so sandboxed agents see placeholders instead of the raw secret.

By default, NemoClaw stores credentials in:
`nemoclaw config rotate-token` is a separate flow that rotates a sandbox-side OpenClaw auth token; it is not a provider-credential upsert and is documented under Commands (use the `nemoclaw-user-reference` skill).

```text
~/.nemoclaw/credentials.json
```
## Where Credentials Live

When NemoClaw creates this state directory, it uses owner-only permissions:
Provider credentials live in the OpenShell gateway store.
List what is registered with:

- `~/.nemoclaw/` is created with mode `0700`
- `~/.nemoclaw/credentials.json` is written with mode `0600`
```console
$ openshell provider list
```

That means only the local account that owns the files should be able to read or modify them.
Or, equivalently, through NemoClaw:

NemoClaw also refuses to use obviously unsafe `HOME` paths such as `/tmp`, `/var/tmp`, `/dev/shm`, or `/` for credential storage.
If `HOME` points to one of those locations, onboarding exits with an error instead of writing secrets there.
```console
$ nemoclaw credentials list
```

## Plaintext Storage Warning
Both surface the provider names that the gateway holds credentials for. The values themselves cannot be read back from the CLI; this is a deliberate property of OpenShell.

The credential file is plaintext JSON.
NemoClaw does **not** currently encrypt the file or integrate with the host operating system keychain.
NemoClaw still keeps non-secret operational state under `~/.nemoclaw/` (such as the sandbox registry).
That directory is created with mode `0700` and contains no credential material.

A typical file looks like this:
## Environment Variables Take Precedence

```json
{
"NVIDIA_API_KEY": "nvapi-...",
"GITHUB_TOKEN": "ghp_...",
"OPENAI_API_KEY": "sk-..."
}
```
When a NemoClaw command needs a credential value during a single run (for example to forward it to an `openshell provider` registration), it reads from `process.env` first.
This means you can:

Treat this file like any other local secret material.
Anyone who can read it can reuse those credentials with the upstream provider.
- Prefix any command with the credential to override the gateway-stored value: `NVIDIA_API_KEY=nvapi-... nemoclaw onboard`
- Use short-lived or rotated credentials in CI by exporting them once per pipeline run
- Avoid registering credentials in the gateway entirely if your environment supplies them

## Precedence and Scope
## Deploy Reads from Environment Only

When NemoClaw looks up a credential, it checks environment variables first.
If the corresponding environment variable is set, NemoClaw uses that value instead of the stored file.
`nemoclaw deploy` (which provisions a remote Brev box) cannot read secrets back from the gateway, so it requires every credential to be present in the host environment at invocation time.
A typical deploy invocation looks like:

This behavior is useful for:
```console
$ NVIDIA_API_KEY=nvapi-... \
TELEGRAM_BOT_TOKEN=... \
nemoclaw deploy my-instance
```

- CI or automation where you do not want to persist secrets to disk
- temporary overrides during testing
- short-lived or rotated credentials
If a required credential is missing the deploy aborts before any remote work begins.

For interactive local use, `nemoclaw onboard` can save credentials into `~/.nemoclaw/credentials.json` so future runs do not prompt again.
## GitHub Tokens

## Security Recommendations
NemoClaw never persists `GITHUB_TOKEN` itself.
When a private repo requires authentication NemoClaw runs `gh auth token`, which returns whatever the GitHub CLI has stored — without caring about the storage backend.

Use the following practices to reduce the risk of credential exposure.
The GitHub CLI prefers an OS keychain when one is reachable: macOS Keychain on macOS, Windows Credential Manager on Windows, and Linux Secret Service (libsecret + a running D-Bus session) on Linux.
On hosts where no keychain is reachable (CI runners, headless launches, WSL without a session bus, macOS contexts where Keychain access is blocked, etc.) `gh auth login` falls back to a `gh`-managed file under `~/.config/gh/` with mode `0600`.
NemoClaw treats both backends identically: `gh auth token` returns the value, and NemoClaw stages it in `process.env` for the current run only.

1. Keep your home directory private and owned by your user account.
2. Exclude `~/.nemoclaw/` from cloud-sync folders, shared folders, and broad backup exports unless those systems are already approved for secret storage.
3. Prefer short-lived or low-scope provider credentials where the upstream service supports them.
4. Rotate keys after suspected exposure, machine transfer, or account changes.
5. Prefer environment variables for ephemeral automation instead of persisting long-lived secrets locally.
6. Do not copy `credentials.json` into container images, Git repositories, bug reports, or support bundles.
If `gh` is not installed or not logged in, NemoClaw prompts for a personal access token for that single run; the prompted value is held in process memory and is not written to host disk.
Run `gh auth login` if you want a persistent backing store (whichever one applies on your host) so future runs do not prompt.

## Inspect and Repair Permissions
## Migration From Earlier Releases

To inspect the current permissions:
Earlier NemoClaw releases stored credentials as plaintext JSON in `~/.nemoclaw/credentials.json` with mode `0600`.
On first `nemoclaw onboard` after upgrading, NemoClaw automatically:

```console
$ ls -ld ~/.nemoclaw ~/.nemoclaw/credentials.json
```
1. Reads the legacy file.
2. Stages allowlisted credential values into `process.env` for the rest of the run.
3. Re-registers each value with the OpenShell gateway through the normal onboarding path.
4. Securely overwrites and deletes `~/.nemoclaw/credentials.json` only after every staged value has been verified as migrated to the gateway.

Expected output should show a private directory and file, for example:
You will see a one-line stderr notice the first time this happens.
Credential lookup paths such as rebuild also stage allowlisted legacy values so interrupted upgrades can keep working, but those staging-only paths do not delete the plaintext file because they cannot prove every legacy value was registered with the gateway.
If `~/.nemoclaw/credentials.json` remains after a rebuild or other credential lookup, run `nemoclaw onboard` to complete the verified gateway migration and cleanup.

```text
drwx------ ... ~/.nemoclaw
-rw------- ... ~/.nemoclaw/credentials.json
```
## Rotate or Remove a Stored Credential

If the permissions are broader than expected, tighten them:
The simplest way to replace a stored value is to rerun onboarding with the new value in your environment:

```console
$ chmod 700 ~/.nemoclaw
$ chmod 600 ~/.nemoclaw/credentials.json
$ NVIDIA_API_KEY=nvapi-new-value nemoclaw onboard
```

## Rotate or Remove Stored Credentials

The simplest way to replace a stored provider key is to rerun onboarding and provide the new value when prompted:
To remove a credential from the gateway entirely:

```console
$ nemoclaw onboard
$ nemoclaw credentials reset <PROVIDER_NAME>
```

To remove the stored file entirely:
`<PROVIDER_NAME>` is the OpenShell provider name (run `nemoclaw credentials list` first if you are not sure).
On the next run NemoClaw prompts again unless the credential is supplied through the environment.

```console
$ rm -f ~/.nemoclaw/credentials.json
```
## Security Recommendations

On the next run, NemoClaw prompts again unless the credential is supplied through the environment.
1. Prefer short-lived or low-scope provider credentials where the upstream service supports them.
2. Rotate keys after suspected exposure, machine transfer, or account changes.
3. Prefer environment variables for ephemeral automation rather than registering long-lived secrets in the gateway.
4. Do not copy any host-side NemoClaw state into container images, Git repositories, bug reports, or support bundles. Even though credentials no longer live on disk, the surrounding configuration may reveal which providers you have registered.
5. Keep your home directory private and owned by your user account.

## Related Files

Other NemoClaw host-side state also lives under `~/.nemoclaw/`, such as sandbox registry metadata.
These files are operational state, not provider secrets, but they should still remain in a user-owned home directory.

For the broader sandbox security model and operational trade-offs, see Security Best Practices (use the `nemoclaw-user-configure-security` skill) and Architecture (use the `nemoclaw-user-reference` skill).
12 changes: 6 additions & 6 deletions .agents/skills/nemoclaw-user-get-started/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ After you enter the sandbox name, the wizard prints a review summary and asks fo
──────────────────────────────────────────────────
Provider: nvidia-api
Model: nvidia/nemotron-3-super-120b-a12b
API key: NVIDIA_API_KEY (stored in ~/.nemoclaw/credentials.json)
API key: NVIDIA_API_KEY (registered with the OpenShell gateway)
Web search: disabled
Messaging: none
Sandbox name: my-assistant
Expand Down Expand Up @@ -307,15 +307,15 @@ Refer to Switch inference providers (use the `nemoclaw-user-configure-inference`

### Reset a Stored Credential

If an API key was entered incorrectly during onboarding, clear the stored value and re-enter it on the next onboard run:
If a provider credential was entered incorrectly during onboarding, clear the gateway-registered value and re-enter it on the next onboard run:

```console
$ nemoclaw credentials list # see which keys are stored
$ nemoclaw credentials reset <KEY> # clear a single key, for example NVIDIA_API_KEY
$ nemoclaw onboard # re-run to re-enter the cleared key
$ nemoclaw credentials list # see which providers are registered
$ nemoclaw credentials reset <PROVIDER> # clear a single provider, for example nvidia-prod
$ nemoclaw onboard # re-run to re-enter the cleared provider
```

The credentials command is documented in full at `nemoclaw credentials reset <KEY>` (use the `nemoclaw-user-reference` skill).
The credentials command is documented in full at `nemoclaw credentials reset <PROVIDER>` (use the `nemoclaw-user-reference` skill).

### Rebuild a Sandbox While Preserving Workspace State

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,18 @@ Agent (sandbox) ──▶ OpenShell gateway ──▶ NVIDIA Endpoint (build

Refer to Inference Options (use the `nemoclaw-user-configure-inference` skill) for provider configuration details.

## Provider Credential Storage

Provider credentials live in the OpenShell gateway store, not on the host filesystem.
NemoClaw never writes them to host disk; the OpenShell L7 proxy injects values at egress.
See Credential Storage (use the `nemoclaw-user-configure-security` skill) for the inspection, rotation, and migration flow.

## Host-Side State and Config

NemoClaw keeps its operator-facing state on the host rather than inside the sandbox.
NemoClaw keeps non-secret operator-facing state on the host rather than inside the sandbox.

| Path | Purpose |
|---|---|
| `~/.nemoclaw/credentials.json` | Provider credentials saved during onboarding. Stored as plaintext JSON protected by local filesystem permissions; see Credential Storage (use the `nemoclaw-user-configure-security` skill). |
| `~/.nemoclaw/sandboxes.json` | Registered sandbox metadata, including the default sandbox selection. |
| `~/.openclaw/openclaw.json` | Host OpenClaw configuration that NemoClaw snapshots or restores during migration flows. |

Expand Down
19 changes: 11 additions & 8 deletions .agents/skills/nemoclaw-user-reference/references/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ $ NEMOCLAW_SINGLE_SESSION=1 curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash

The wizard prompts for a provider first, then collects the provider credential if needed.
Supported non-experimental choices include NVIDIA Endpoints, OpenAI, Anthropic, Google Gemini, and compatible OpenAI or Anthropic endpoints.
Credentials are stored in `~/.nemoclaw/credentials.json`. For file permissions, plaintext storage behavior, and hardening guidance, see Credential Storage (use the `nemoclaw-user-configure-security` skill).
Credentials are registered with the OpenShell gateway and never persisted to host disk. See Credential Storage (use the `nemoclaw-user-configure-security` skill) for details on inspection, rotation, and migration from earlier releases.
The legacy `nemoclaw setup` command is deprecated; use `nemoclaw onboard` instead.

After provider selection, the wizard prompts for a **policy tier** that controls the default set of network policy presets applied to the sandbox.
Expand Down Expand Up @@ -391,7 +391,7 @@ $ nemoclaw my-assistant channels list
### `nemoclaw <name> channels add <channel>`

Store credentials for a messaging channel (`telegram`, `discord`, or `slack`) and rebuild the sandbox so the image picks up the new channel.
The command prompts for any missing token, persists it under `~/.nemoclaw/credentials.json`, then asks whether to rebuild immediately.
The command prompts for any missing token, registers it with the OpenShell gateway, then asks whether to rebuild immediately.
Running `add` for an already-configured channel simply overwrites the stored tokens — the operation is idempotent.

```console
Expand Down Expand Up @@ -424,7 +424,9 @@ Host-side removal is the supported path because `/sandbox/.openclaw/openclaw.jso

### `nemoclaw <name> channels stop <channel>`

Pause a single messaging bridge (`telegram`, `discord`, or `slack`) without clearing its credentials. The channel is marked disabled in the per-sandbox registry, and the sandbox is rebuilt so the onboard step skips registering the bridge with the gateway. Credentials stay in `~/.nemoclaw/credentials.json`, so a later `channels start` brings the bridge back without re-entering tokens.
Pause a single messaging bridge (`telegram`, `discord`, or `slack`) without clearing its credentials.
The channel is marked disabled in the per-sandbox registry, and the sandbox is rebuilt so the onboard step skips registering the bridge with the gateway.
The provider stays registered with the OpenShell gateway, so a later `channels start` brings the bridge back without re-entering tokens.

```console
$ nemoclaw my-assistant channels stop telegram
Expand Down Expand Up @@ -673,20 +675,21 @@ If `--output` is set and the tarball cannot be written (for example, the destina

### `nemoclaw credentials list`

List the names of all credentials stored in `~/.nemoclaw/credentials.json`.
List the provider credentials registered with the OpenShell gateway.
Values are not printed.

```console
$ nemoclaw credentials list
```

### `nemoclaw credentials reset <KEY>`
### `nemoclaw credentials reset <PROVIDER>`

Remove a stored credential by name.
After removal, re-running `nemoclaw onboard` re-prompts for that key.
Remove a provider credential from the OpenShell gateway by provider name.
After removal, re-running `nemoclaw onboard` re-prompts for that provider's credential.
Run `nemoclaw credentials list` first if you are not sure of the provider name.

```console
$ nemoclaw credentials reset NVIDIA_API_KEY
$ nemoclaw credentials reset nvidia-prod
```

| Flag | Description |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ $ nemoclaw <sandbox> channels add <telegram|discord|slack>
$ nemoclaw <sandbox> channels remove <telegram|discord|slack>
```

`channels add` stores credentials under `~/.nemoclaw/credentials.json` and `channels remove` clears them; both offer to rebuild the sandbox so the image reflects the new channel set.
`channels add` registers credentials with the OpenShell gateway and `channels remove` clears them; both offer to rebuild the sandbox so the image reflects the new channel set.
In non-interactive mode (`NEMOCLAW_NON_INTERACTIVE=1`), the commands stage the change and leave the rebuild to a follow-up `nemoclaw <sandbox> rebuild`.

### `nemoclaw <sandbox> config set` refuses a key that does not currently exist
Expand Down
Loading
Loading