#21216: feat(models): add apiKeyHelper for dynamic API key resolution
docs
gateway
commands
agents
size: M
Cluster:
Model Authentication Enhancements
## Summary
- Adds `apiKeyHelper` field to model provider config, allowing a shell command to resolve API keys at runtime (secret managers, gateway proxies, dynamic rotation)
- Uses cross-platform shell via `buildNodeShellCommand` (`/bin/sh -lc` on Unix, `cmd.exe` on Windows)
- Async `execFile` with 10s timeout — non-blocking for gateway request flow
- Auth label rendering uses config-only check (no command execution in `/status`)
- Surfaces `apiKeyHelper` providers in `models status --json` and probe targets
- Marks `apiKeyHelper` as sensitive in Zod schema (consistent with `apiKey`)
## Review issue fixes
1. **Cross-platform shell** — replaced hardcoded `/bin/sh` with `buildNodeShellCommand()` from `src/infra/node-shell.ts`
2. **Sensitive marking** — `apiKeyHelper` now uses `.register(sensitive)` in Zod schema
3. **No side effects in label rendering** — auth label checks config presence only, never runs the command
4. **Async execution** — converted from blocking `execFileSync` to async `execFile` callback pattern (Codex P1)
5. **Probe target support** — `apiKeyHelper` providers are now included in auth probe targets (Codex P2)
## Testing
### Automated
- 761 test files, 6121 tests all passing
- New test coverage: `model-auth.test.ts` (7 new tests), `list.probe.test.ts` (4 new tests)
- Tests cover: key resolution, whitespace trimming, timeout handling, empty output, Tailscale Aperture `echo '-'` pattern, probe target selection, env priority over helper
### Manual (end-to-end)
- **Tailscale Aperture**: Configured `apiKeyHelper: "echo '-'"` proxying to Anthropic — verified dummy key flows through gateway, model responds correctly
- **1Password CLI**: Configured `apiKeyHelper: "op read 'op://Personal/ANTHROPIC_API_KEY/license key'"` — verified vault key retrieval, model responds correctly
- **`models status --json`**: Verified `apiKeyHelper` providers appear in status output with correct `effective.kind`
- **Sensitive data audit**: Confirmed no secrets, tokens, or vault paths leak in diff
## Test plan
- [ ] `pnpm build` passes
- [ ] `pnpm format:check` passes
- [ ] `pnpm lint` — 0 warnings, 0 errors
- [ ] `pnpm test` — 6121/6121 tests pass
- [ ] Verify cross-platform shell on Windows (uses `cmd.exe /d /s /c`)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Adds `apiKeyHelper` field to model provider config for dynamic API key resolution via shell commands at runtime. Key changes:
- **Core implementation**: New `resolveApiKeyHelper()` in `src/agents/model-auth.ts:73-92` runs user-configured shell commands with 10s timeout using cross-platform `buildNodeShellCommand()` (Unix: `/bin/sh -lc`, Windows: `cmd.exe /d /s /c`)
- **Auth priority**: Profiles → env vars → `apiKeyHelper` → inline `apiKey` (lines 232-248)
- **Zod schema**: Marks `apiKeyHelper` as sensitive in `src/config/zod-schema.core.ts:62`
- **Status/probe support**: Providers with `apiKeyHelper` correctly surface in `models status --json` and probe targets
- **No side effects in labels**: Auth label rendering checks config presence only (line 77), never executes command
- **Comprehensive tests**: 7 new tests in `model-auth.test.ts` (key resolution, whitespace, timeout, empty output, Tailscale `-` pattern) + 4 probe tests
Documentation updated for English and zh-CN with examples for Tailscale Aperture and 1Password CLI.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk
- The implementation is production-ready: uses async non-blocking `execFile` with timeout protection, properly handles cross-platform shells, maintains correct auth priority order, includes comprehensive test coverage (11 new tests), and follows the project's conventions. The shell command execution is intentional for user-configured commands from their own config file (not untrusted input), with proper error handling and fallback to inline `apiKey` on failure.
- No files require special attention
<sub>Last reviewed commit: b457fa7</sub>
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#15756: [Security]: strip provider apiKey from models.json before prompt se...
by SecBear · 2026-02-13
77.3%
#21884: feat(models): auth improvements — status command, heuristics, multi...
by kckylechen1 · 2026-02-20
76.4%
#19728: feat: add apiKeyFile support for model providers
by paperMoose · 2026-02-18
76.3%
#16663: feat: GCP Secret Manager integration for external secrets management
by amor71 · 2026-02-15
74.2%
#11300: feat(exec): make shell configurable via tools.exec.shell
by imjszhang · 2026-02-07
74.0%
#9163: Fix: Save Anthropic setup token to config file
by vishaltandale00 · 2026-02-04
74.0%
#14836: fix: strip apiKey from models.json to prevent credential exposure
by pahud · 2026-02-12
73.4%
#16766: fix(model): apply provider baseUrl/headers override to registry-fou...
by dzianisv · 2026-02-15
73.3%
#16290: fix: add field-level validation for custom LLM provider config
by superlowburn · 2026-02-14
72.9%
#6730: feat: Make OpenAI Codex CLI models usable - reasoning effort directive
by levineam · 2026-02-02
72.8%