Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ Customize the Network Policy <network-policy/customize-network-policy>
:hidden:

Security Best Practices <security/best-practices>
Credential Storage <security/credential-storage>
```

```{toctree}
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ NemoClaw keeps its operator-facing state on the host rather than inside the sand

| Path | Purpose |
|---|---|
| `~/.nemoclaw/credentials.json` | Provider credentials saved during onboarding. |
| `~/.nemoclaw/credentials.json` | Provider credentials saved during onboarding. Stored as plaintext JSON protected by local filesystem permissions; see [Credential Storage](../security/credential-storage.md). |
| `~/.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
2 changes: 1 addition & 1 deletion docs/reference/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ Avoid `openshell self-update`, `npm update -g openshell`, `openshell gateway sta

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`.
Credentials are stored in `~/.nemoclaw/credentials.json`. For file permissions, plaintext storage behavior, and hardening guidance, see [Credential Storage](../security/credential-storage.md).
The legacy `nemoclaw setup` command is deprecated; use `nemoclaw onboard` instead.

If you enable Brave Search during onboarding, NemoClaw currently stores the Brave API key in the sandbox's OpenClaw configuration.
Expand Down
133 changes: 133 additions & 0 deletions docs/security/credential-storage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
title:
page: "NemoClaw Credential Storage"
nav: "Credential Storage"
description:
main: "Learn where NemoClaw stores credentials, what filesystem protections it applies, and how to secure or rotate stored secrets."
agent: "Explains where NemoClaw stores provider credentials, the file permissions it applies, and the operational security trade-offs of plaintext local storage. Use when reviewing credential handling or advising users how to secure stored API keys."
keywords: ["nemoclaw credential storage", "credentials.json", "api key security"]
topics: ["generative_ai", "ai_agents"]
tags: ["security", "credentials", "filesystem", "secrets"]
content:
type: reference
difficulty: technical_beginner
audience: ["developer", "engineer"]
status: published
---

<!--
SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0
-->

# Credential Storage

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.

⚠️ Potential issue | 🟡 Minor

Align H1 with frontmatter title.page.

The H1 is Credential Storage, but title.page is NemoClaw Credential Storage. These should match exactly.

As per coding guidelines, new pages must have an H1 heading that matches the title.page frontmatter value.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/security/credential-storage.md` at line 23, Update the top-level H1 to
exactly match the frontmatter key title.page: replace the current H1 "Credential
Storage" with "NemoClaw Credential Storage" so the page heading and title.page
value are identical; verify the H1 line (the single leading "# ..." heading) is
changed and no other headings are altered.


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.

## Location and Permissions

By default, NemoClaw stores credentials in:

```text
~/.nemoclaw/credentials.json
```

When NemoClaw creates this state directory, it uses owner-only permissions:

- `~/.nemoclaw/` is created with mode `0700`
- `~/.nemoclaw/credentials.json` is written with mode `0600`

That means only the local account that owns the files should be able to read or modify them.

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.

## Plaintext Storage Warning

The credential file is plaintext JSON.
NemoClaw does **not** currently encrypt the file or integrate with the host operating system keychain.

A typical file looks like this:

```json
{
"NVIDIA_API_KEY": "nvapi-...",
"GITHUB_TOKEN": "ghp_...",
"OPENAI_API_KEY": "sk-..."
}
```

Treat this file like any other local secret material.
Anyone who can read it can reuse those credentials with the upstream provider.

## Precedence and Scope

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.

This behavior is useful for:

- CI or automation where you do not want to persist secrets to disk
- temporary overrides during testing
- short-lived or rotated credentials

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

## Security Recommendations

Use the following practices to reduce the risk of credential exposure.

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.

## Inspect and Repair Permissions

To inspect the current permissions:

```console
$ ls -ld ~/.nemoclaw ~/.nemoclaw/credentials.json
```

Expected output should show a private directory and file, for example:

```text
drwx------ ... ~/.nemoclaw
-rw------- ... ~/.nemoclaw/credentials.json
```

If the permissions are broader than expected, tighten them:

```console
$ chmod 700 ~/.nemoclaw
$ chmod 600 ~/.nemoclaw/credentials.json
```

## 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:

```console
$ nemoclaw onboard
```

To remove the stored file entirely:

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

On the next run, NemoClaw prompts again unless the credential is supplied through the environment.

## 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](./best-practices.md) and [Architecture](../reference/architecture.md).
Comment on lines +128 to +133

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.

⚠️ Potential issue | 🟡 Minor

Add a Next Steps section at the bottom.

This ends with Related Files; for new pages, the final section should be Next Steps with related doc links.

As per coding guidelines, new docs pages must include a “Next Steps” section at the bottom linking to related pages.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/security/credential-storage.md` around lines 128 - 133, Add a new "Next
Steps" section after the existing "Related Files" section: create a final header
"Next Steps" that lists links to the related docs already referenced (e.g.,
./best-practices.md and ../reference/architecture.md) and any other next-reading
resources relevant to credential-storage; ensure the section follows the same
link style as the rest of the file and appears as the final section of the page.

5 changes: 5 additions & 0 deletions test/credentials.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ describe("credential prompts", () => {
fs.readFileSync(path.join(home, ".nemoclaw", "credentials.json"), "utf-8"),
);
expect(saved).toEqual({ TEST_API_KEY: "nvapi-saved-key" });

const dirMode = fs.statSync(path.join(home, ".nemoclaw")).mode & 0o777;
const fileMode = fs.statSync(path.join(home, ".nemoclaw", "credentials.json")).mode & 0o777;
expect(dirMode).toBe(0o700);
expect(fileMode).toBe(0o600);
});

it("prefers environment credentials and ignores malformed credential files", async () => {
Expand Down
Loading