From 974b8b0e88ea0d1f57bd3c05993d55a30861da94 Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:21:46 +0000 Subject: [PATCH 1/2] Initial plan From 96f7411d5c75509065b183d3f818b6d7728a6c0e Mon Sep 17 00:00:00 2001 From: "anthropic-code-agent[bot]" <242468646+Claude@users.noreply.github.com> Date: Sat, 14 Feb 2026 17:25:39 +0000 Subject: [PATCH 2/2] fix: set CLAUDE_CODE_API_KEY_HELPER env var for credential isolation When api-proxy is enabled with an Anthropic key, set the CLAUDE_CODE_API_KEY_HELPER environment variable to point to the get-claude-key.sh script. This ensures Claude Code CLI properly uses the API key helper for credential isolation. Previously, only ANTHROPIC_BASE_URL was set, but Claude Code requires either a config file with apiKeyHelper or the environment variable to actually use the helper script. Without this, Claude Code would not read the config and authentication would fail. This fix: - Sets CLAUDE_CODE_API_KEY_HELPER=/usr/local/bin/get-claude-key.sh when api-proxy is enabled with Anthropic key - Adds comprehensive tests for the new environment variable - Updates type documentation to reflect the new env var Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- src/docker-manager.test.ts | 19 +++++++++++++++++++ src/docker-manager.ts | 5 +++++ src/types.ts | 1 + 3 files changed, 25 insertions(+) diff --git a/src/docker-manager.test.ts b/src/docker-manager.test.ts index a9f6b2b72..9f2be636d 100644 --- a/src/docker-manager.test.ts +++ b/src/docker-manager.test.ts @@ -1568,6 +1568,7 @@ describe('docker-manager', () => { const agent = result.services.agent; const env = agent.environment as Record; expect(env.ANTHROPIC_BASE_URL).toBe('http://172.30.0.30:10001'); + expect(env.CLAUDE_CODE_API_KEY_HELPER).toBe('/usr/local/bin/get-claude-key.sh'); }); it('should set both BASE_URL variables when both keys are provided', () => { @@ -1577,6 +1578,7 @@ describe('docker-manager', () => { const env = agent.environment as Record; expect(env.OPENAI_BASE_URL).toBe('http://172.30.0.30:10000'); expect(env.ANTHROPIC_BASE_URL).toBe('http://172.30.0.30:10001'); + expect(env.CLAUDE_CODE_API_KEY_HELPER).toBe('/usr/local/bin/get-claude-key.sh'); }); it('should not set OPENAI_BASE_URL in agent when only Anthropic key is provided', () => { @@ -1586,6 +1588,7 @@ describe('docker-manager', () => { const env = agent.environment as Record; expect(env.OPENAI_BASE_URL).toBeUndefined(); expect(env.ANTHROPIC_BASE_URL).toBe('http://172.30.0.30:10001'); + expect(env.CLAUDE_CODE_API_KEY_HELPER).toBe('/usr/local/bin/get-claude-key.sh'); }); it('should not set ANTHROPIC_BASE_URL in agent when only OpenAI key is provided', () => { @@ -1614,6 +1617,22 @@ describe('docker-manager', () => { expect(env.no_proxy).toContain('172.30.0.30'); }); + it('should set CLAUDE_CODE_API_KEY_HELPER when Anthropic key is provided', () => { + const configWithProxy = { ...mockConfig, enableApiProxy: true, anthropicApiKey: 'sk-ant-test-key' }; + const result = generateDockerCompose(configWithProxy, mockNetworkConfigWithProxy); + const agent = result.services.agent; + const env = agent.environment as Record; + expect(env.CLAUDE_CODE_API_KEY_HELPER).toBe('/usr/local/bin/get-claude-key.sh'); + }); + + it('should not set CLAUDE_CODE_API_KEY_HELPER when only OpenAI key is provided', () => { + const configWithProxy = { ...mockConfig, enableApiProxy: true, openaiApiKey: 'sk-test-key' }; + const result = generateDockerCompose(configWithProxy, mockNetworkConfigWithProxy); + const agent = result.services.agent; + const env = agent.environment as Record; + expect(env.CLAUDE_CODE_API_KEY_HELPER).toBeUndefined(); + }); + it('should not leak ANTHROPIC_API_KEY to agent when api-proxy is enabled', () => { // Simulate the key being in process.env (as it would be in real usage) const origKey = process.env.ANTHROPIC_API_KEY; diff --git a/src/docker-manager.ts b/src/docker-manager.ts index 24c164fe0..6494a8177 100644 --- a/src/docker-manager.ts +++ b/src/docker-manager.ts @@ -997,6 +997,11 @@ export function generateDockerCompose( if (config.anthropicApiKey) { environment.ANTHROPIC_BASE_URL = `http://${networkConfig.proxyIp}:10001`; logger.debug(`Anthropic API will be proxied through sidecar at http://${networkConfig.proxyIp}:10001`); + + // Set API key helper for Claude Code CLI to use credential isolation + // The helper script returns a placeholder key; real authentication happens via ANTHROPIC_BASE_URL + environment.CLAUDE_CODE_API_KEY_HELPER = '/usr/local/bin/get-claude-key.sh'; + logger.debug('Claude Code API key helper configured: /usr/local/bin/get-claude-key.sh'); } logger.info('API proxy sidecar enabled - API keys will be held securely in sidecar container'); diff --git a/src/types.ts b/src/types.ts index bf73cbbc9..1b9782c46 100644 --- a/src/types.ts +++ b/src/types.ts @@ -398,6 +398,7 @@ export interface WrapperConfig { * variables are set in the agent container: * - OPENAI_BASE_URL=http://api-proxy:10000 (set when OPENAI_API_KEY is provided) * - ANTHROPIC_BASE_URL=http://api-proxy:10001 (set when ANTHROPIC_API_KEY is provided) + * - CLAUDE_CODE_API_KEY_HELPER=/usr/local/bin/get-claude-key.sh (set when ANTHROPIC_API_KEY is provided) * * API keys are passed via environment variables: * - OPENAI_API_KEY - Optional OpenAI API key for Codex