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
27 changes: 23 additions & 4 deletions adapters/claude/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,29 @@ just wheels
python -m pip install --find-links dist "nemo-fabric[claude]"
```

Claude Code authentication can come from an existing cached login or from
`ANTHROPIC_API_KEY`. Package installation is verified by the adapter wheel and
module-entrypoint tests. Authentication is validated when Claude starts the
invocation.
## Authentication

Fabric preserves Claude's native credential resolution. Use an existing Claude
Code login for local development, `ANTHROPIC_AUTH_TOKEN` for a gateway or proxy
bearer credential, `ANTHROPIC_API_KEY` for a static API credential, or Anthropic
Workload Identity Federation (WIF) for production and CI workloads that should
not store a long-lived API key.

The adapter forwards the Anthropic profile and federation environment variables
that Claude Code and the Claude Agent SDK consume. This includes
`ANTHROPIC_CONFIG_DIR`, `ANTHROPIC_PROFILE`, the direct federation identifiers,
and `ANTHROPIC_IDENTITY_TOKEN` or `ANTHROPIC_IDENTITY_TOKEN_FILE`. Fabric reads
selected environment values and forwards them to the Claude runtime, but it
does not persist or log them in configuration or artifacts. Authentication is
validated when Claude starts the invocation.

Unset unused `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` variables before
using WIF. Anthropic credential resolution treats an empty variable as selected,
so an empty API credential prevents fallback to a federation profile.

Refer to the [Claude adapter authentication guide](https://nvidia-nemo-fabric.docs.buildwithfern.com/nemo/fabric/integrations/claude)
for mode selection, required WIF variables, and the Relay boundary. Package
installation is verified by the adapter wheel and module-entrypoint tests.

Relay-enabled runs also require the external `nemo-relay` CLI. Install the CLI
separately:
Expand Down
11 changes: 10 additions & 1 deletion adapters/claude/src/nemo_fabric_adapters/claude/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,15 @@
}
INHERITED_ENV_NAMES = {
"ANTHROPIC_API_KEY",
"ANTHROPIC_AUTH_TOKEN",
"ANTHROPIC_CONFIG_DIR",
"ANTHROPIC_FEDERATION_RULE_ID",
"ANTHROPIC_IDENTITY_TOKEN",
"ANTHROPIC_IDENTITY_TOKEN_FILE",
"ANTHROPIC_ORGANIZATION_ID",
"ANTHROPIC_PROFILE",
"ANTHROPIC_SERVICE_ACCOUNT_ID",
"ANTHROPIC_WORKSPACE_ID",
"APPDATA",
"CLAUDE_CONFIG_DIR",
"COMSPEC",
Expand Down Expand Up @@ -704,7 +713,7 @@ def child_environment(
) -> dict[str, str]:
values = {name: "" for name in os.environ}
values.update(
{name: value for name in INHERITED_ENV_NAMES if (value := os.environ.get(name))}
{name: os.environ[name] for name in INHERITED_ENV_NAMES if name in os.environ}
)
model = _selected_model_config(payload)
api_key_env = model.get("api_key_env")
Expand Down
2 changes: 2 additions & 0 deletions docs/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ navigation:
path: ./sdk/python.mdx
- section: Integrations
contents:
- page: Claude
path: ./integrations/claude.mdx
- page: Harbor
path: ./integrations/harbor.mdx
- section: Reference
Expand Down
90 changes: 90 additions & 0 deletions docs/integrations/claude.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
---
title: "Authenticate the Claude Adapter"
description: "Configure cached login, bearer token, API key, or Workload Identity Federation authentication for the NeMo Fabric Claude adapter."
---
{/* SPDX-FileCopyrightText: Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
SPDX-License-Identifier: Apache-2.0 */}

# Authenticate the Claude Adapter

The `nvidia.fabric.claude` adapter uses the Claude Agent SDK and its bundled
Claude Code runtime. Fabric preserves Claude's native credential precedence and
forwards only supported operating-system, configuration, and authentication
variables plus values explicitly configured in `harness.settings.env`.

## Choose an Authentication Mode

Use the mode that matches the execution environment:

| Mode | Credential Source | Recommended Use |
| --- | --- | --- |
| Claude Code login | Cached Claude.ai or Anthropic Console login | Interactive local development |
| Bearer token | `ANTHROPIC_AUTH_TOKEN` | LLM gateways or proxies that authenticate with an `Authorization: Bearer` header |
| API key | `ANTHROPIC_API_KEY` | Local development, prototypes, and controlled servers |
| Workload Identity Federation (WIF) | Anthropic profile or direct federation environment variables | CI, cloud workloads, and Kubernetes without static API keys |
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Claude Code selects `ANTHROPIC_AUTH_TOKEN` before `ANTHROPIC_API_KEY`. Both
environment credentials take precedence over WIF and cached login credentials.
An empty value still occupies its precedence slot, so unset an unused variable
instead of setting it to an empty string.

Claude Code can use a Claude.ai Pro or Max login, an Anthropic Console login, or
supported enterprise providers. Refer to the
[Claude Code authentication documentation](https://docs.anthropic.com/en/docs/claude-code/getting-started)
for the current subscription and enterprise options.

## Use a Cached Claude Code Login

Authenticate Claude Code outside Fabric. The adapter inherits `HOME` and
`CLAUDE_CONFIG_DIR`, so the bundled runtime can reuse the same cached login.
Fabric does not copy the credential store into its configuration or artifacts.

## Use an API Key

Set the API key in the process that invokes Fabric:

```bash
export ANTHROPIC_API_KEY=sk-ant-api03-example
```

The adapter also forwards the selected model's `api_key_env` when the model
configuration names a different environment variable.

## Use Workload Identity Federation

For a named WIF profile, set the profile and optional nondefault configuration
directory:

```bash
export ANTHROPIC_CONFIG_DIR=/etc/anthropic
export ANTHROPIC_PROFILE=production
```

For direct environment configuration, provide the federation rule,
organization, service account, and one identity-token source:

```bash
export ANTHROPIC_FEDERATION_RULE_ID=fdrl_example
export ANTHROPIC_ORGANIZATION_ID=00000000-0000-0000-0000-000000000000
export ANTHROPIC_SERVICE_ACCOUNT_ID=svac_example
export ANTHROPIC_WORKSPACE_ID=wrkspc_example
export ANTHROPIC_IDENTITY_TOKEN_FILE=/var/run/secrets/anthropic.com/token
```

Use `ANTHROPIC_IDENTITY_TOKEN` instead of
`ANTHROPIC_IDENTITY_TOKEN_FILE` when the platform injects the identity token as
an environment variable. Refer to the
[Anthropic WIF reference](https://platform.claude.com/docs/en/manage-claude/wif-reference)
for credential precedence, profile structure, required claims, and provider
setup.

Unset `ANTHROPIC_API_KEY` and `ANTHROPIC_AUTH_TOKEN` before using WIF. These
variables take precedence over federation even when their value is empty.

## Use Authentication with Relay

NeMo Relay does not authenticate Claude. A Relay-enabled Fabric invocation
starts the gateway as a supervised sidecar and sets `ANTHROPIC_BASE_URL` for the
Claude runtime. Claude still resolves its credential through the selected mode,
and Fabric does not write authentication values to Relay configuration or
artifacts.
62 changes: 59 additions & 3 deletions tests/adapters/test_claude_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@
from nemo_fabric_adapters.claude import adapter

ROOT = Path(__file__).resolve().parents[2]
ANTHROPIC_AUTH_ENV_NAMES = {
"ANTHROPIC_API_KEY",
"ANTHROPIC_AUTH_TOKEN",
"ANTHROPIC_CONFIG_DIR",
"ANTHROPIC_FEDERATION_RULE_ID",
"ANTHROPIC_IDENTITY_TOKEN",
"ANTHROPIC_IDENTITY_TOKEN_FILE",
"ANTHROPIC_ORGANIZATION_ID",
"ANTHROPIC_PROFILE",
"ANTHROPIC_SERVICE_ACCOUNT_ID",
"ANTHROPIC_WORKSPACE_ID",
}


def test_claude_descriptor_is_narrow_and_versioned():
Expand Down Expand Up @@ -748,16 +760,60 @@ def test_run_reports_relay_start_failure_without_raw_diagnostic(
assert not relay.plugin_path.exists()


def test_build_options_forwards_default_anthropic_api_key(claude_payload, monkeypatch):
@pytest.mark.parametrize(
"auth_environment",
[
{
"ANTHROPIC_CONFIG_DIR": "/run/anthropic",
"ANTHROPIC_PROFILE": "production",
},
{
"ANTHROPIC_FEDERATION_RULE_ID": "fdrl_test",
"ANTHROPIC_ORGANIZATION_ID": "organization-test",
"ANTHROPIC_SERVICE_ACCOUNT_ID": "svac_test",
"ANTHROPIC_WORKSPACE_ID": "wrkspc_test",
"ANTHROPIC_IDENTITY_TOKEN_FILE": "/run/secrets/anthropic/token",
},
{
"ANTHROPIC_FEDERATION_RULE_ID": "fdrl_test",
"ANTHROPIC_ORGANIZATION_ID": "organization-test",
"ANTHROPIC_SERVICE_ACCOUNT_ID": "svac_test",
"ANTHROPIC_IDENTITY_TOKEN": "identity-token",
},
{"ANTHROPIC_API_KEY": "default-secret"},
{"ANTHROPIC_AUTH_TOKEN": "bearer-token"},
{
"ANTHROPIC_API_KEY": "",
"ANTHROPIC_PROFILE": "fallback-profile",
},
{
"ANTHROPIC_AUTH_TOKEN": "",
"ANTHROPIC_API_KEY": "fallback-api-key",
"ANTHROPIC_PROFILE": "fallback-profile",
},
],
)
def test_build_options_forwards_anthropic_auth_environment(
claude_payload, auth_environment
):
model = claude_payload["effective_config"]["config"]["models"]["default"]
model.pop("api_key_env")
settings = claude_payload["effective_config"]["config"]["harness"]["settings"]
settings.pop("env")
monkeypatch.setenv("ANTHROPIC_API_KEY", "default-secret")
for name in ANTHROPIC_AUTH_ENV_NAMES:
os.environ.pop(name, None)
os.environ["FABRIC_UNRELATED_SECRET"] = "do-not-forward"
os.environ.update(auth_environment)

options = adapter.build_options(claude_payload, resume=None)

assert options.env["ANTHROPIC_API_KEY"] == "default-secret"
forwarded_auth_environment = {
name: options.env[name]
for name in ANTHROPIC_AUTH_ENV_NAMES
if name in options.env
}
assert forwarded_auth_environment == auth_environment
assert options.env["FABRIC_UNRELATED_SECRET"] == ""


@pytest.mark.parametrize(
Expand Down
Loading