feat: add Copilot BYOK support via COPILOT_API_KEY#1918
Conversation
There was a problem hiding this comment.
Pull request overview
Adds Bring Your Own Key (BYOK) support for GitHub Copilot via COPILOT_API_KEY, integrating it into the existing API-proxy credential-isolation flow so the real key can be kept in the sidecar (and only passed to the agent when the proxy is disabled).
Changes:
- Extend config + CLI env parsing to support
COPILOT_API_KEYalongsideCOPILOT_GITHUB_TOKEN. - Update docker-compose generation to protect/forward
COPILOT_API_KEYcorrectly (one-shot-token + placeholder isolation + sidecar injection). - Update api-proxy sidecar to accept either Copilot credential, with precedence for
COPILOT_GITHUB_TOKEN, plus docs/examples/tests adjustments.
Show a summary per file
| File | Description |
|---|---|
src/types.ts |
Adds copilotApiKey?: string and documents behavior when API proxy is enabled. |
src/cli.ts |
Reads COPILOT_API_KEY, updates API-proxy validation + config redaction + status logging. |
src/docker-manager.ts |
Adds COPILOT_API_KEY to one-shot-token list; sets placeholders; forwards real key to sidecar and conditionally to agent. |
src/docker-manager.test.ts |
Adds unit tests for forwarding/isolation behavior of COPILOT_API_KEY. |
src/cli.test.ts |
Updates validation warning expectations to include COPILOT_API_KEY. |
containers/api-proxy/server.js |
Introduces COPILOT_AUTH_TOKEN (`COPILOT_GITHUB_TOKEN |
containers/agent/api-proxy-health-check.sh |
Ensures COPILOT_API_KEY is a placeholder in the agent when API-proxy is active. |
examples/github-copilot.sh |
Updates example commentary to focus on COPILOT_API_KEY usage with the sidecar. |
docs/api-proxy-sidecar.md |
Documents COPILOT_API_KEY as a sidecar-held secret and placeholder behavior in the agent. |
docs-site/src/content/docs/reference/cli-reference.md |
Adds COPILOT_API_KEY to the CLI environment variable reference. |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 10/10 changed files
- Comments generated: 3
| // it gets a placeholder value set earlier (line ~362) for credential isolation | ||
| if (process.env.COPILOT_GITHUB_TOKEN && !config.enableApiProxy) environment.COPILOT_GITHUB_TOKEN = process.env.COPILOT_GITHUB_TOKEN; | ||
| // COPILOT_API_KEY (BYOK) — forward when api-proxy is NOT enabled; when api-proxy IS enabled, | ||
| // it is excluded from agent env (held securely in api-proxy sidecar) |
There was a problem hiding this comment.
The comment says COPILOT_API_KEY is “excluded from agent env” when api-proxy is enabled, but the implementation sets a placeholder value in the agent environment for credential isolation. Please reword to clarify that the real key is not forwarded and a placeholder may still be present.
| // it is excluded from agent env (held securely in api-proxy sidecar) | |
| // the real key is not forwarded to the agent env and a placeholder may still be present | |
| // for credential isolation while the real key is held securely in the api-proxy sidecar |
| # Run Copilot CLI with API proxy enabled | ||
| # Use sudo -E to preserve environment variables (COPILOT_GITHUB_TOKEN, GITHUB_TOKEN, AWF_ONE_SHOT_TOKEN_DEBUG) | ||
| # Use sudo -E to preserve environment variables (COPILOT_API_KEY, GITHUB_TOKEN, AWF_ONE_SHOT_TOKEN_DEBUG) | ||
| # The api-proxy sidecar holds the real COPILOT_API_KEY and injects it into requests. |
There was a problem hiding this comment.
This example script currently hard-requires COPILOT_API_KEY; however, the PR description/documentation states COPILOT_GITHUB_TOKEN and COPILOT_API_KEY are interchangeable. Consider updating the example to accept either credential (and mention/preserve both in the sudo -E comment) so it matches the supported behavior.
| const OPENAI_API_KEY = (process.env.OPENAI_API_KEY || '').trim() || undefined; | ||
| const ANTHROPIC_API_KEY = (process.env.ANTHROPIC_API_KEY || '').trim() || undefined; | ||
| const COPILOT_GITHUB_TOKEN = (process.env.COPILOT_GITHUB_TOKEN || '').trim() || undefined; | ||
| const COPILOT_API_KEY = (process.env.COPILOT_API_KEY || '').trim() || undefined; | ||
| // BYOK: use COPILOT_GITHUB_TOKEN (GitHub OAuth) or COPILOT_API_KEY (direct key), GitHub token takes precedence | ||
| const COPILOT_AUTH_TOKEN = COPILOT_GITHUB_TOKEN || COPILOT_API_KEY; |
There was a problem hiding this comment.
COPILOT_API_KEY / COPILOT_AUTH_TOKEN support was added here, but there are no unit tests covering the new env var path or the precedence rule (COPILOT_GITHUB_TOKEN should win when both are set). Since containers/api-proxy/server.js already has a test suite, please add/extend tests to validate these cases.
| const OPENAI_API_KEY = (process.env.OPENAI_API_KEY || '').trim() || undefined; | |
| const ANTHROPIC_API_KEY = (process.env.ANTHROPIC_API_KEY || '').trim() || undefined; | |
| const COPILOT_GITHUB_TOKEN = (process.env.COPILOT_GITHUB_TOKEN || '').trim() || undefined; | |
| const COPILOT_API_KEY = (process.env.COPILOT_API_KEY || '').trim() || undefined; | |
| // BYOK: use COPILOT_GITHUB_TOKEN (GitHub OAuth) or COPILOT_API_KEY (direct key), GitHub token takes precedence | |
| const COPILOT_AUTH_TOKEN = COPILOT_GITHUB_TOKEN || COPILOT_API_KEY; | |
| function resolveCopilotAuthToken(env = process.env) { | |
| const githubToken = (env.COPILOT_GITHUB_TOKEN || '').trim() || undefined; | |
| const apiKey = (env.COPILOT_API_KEY || '').trim() || undefined; | |
| // BYOK: use COPILOT_GITHUB_TOKEN (GitHub OAuth) or COPILOT_API_KEY (direct key), | |
| // with GitHub token taking precedence when both are set. | |
| return githubToken || apiKey; | |
| } | |
| const OPENAI_API_KEY = (process.env.OPENAI_API_KEY || '').trim() || undefined; | |
| const ANTHROPIC_API_KEY = (process.env.ANTHROPIC_API_KEY || '').trim() || undefined; | |
| const COPILOT_GITHUB_TOKEN = (process.env.COPILOT_GITHUB_TOKEN || '').trim() || undefined; | |
| const COPILOT_API_KEY = (process.env.COPILOT_API_KEY || '').trim() || undefined; | |
| const COPILOT_AUTH_TOKEN = resolveCopilotAuthToken(process.env); |
- Reword docker-manager comment to clarify placeholder behavior when api-proxy is enabled - Update github-copilot example to accept either COPILOT_GITHUB_TOKEN or COPILOT_API_KEY instead of hard-requiring COPILOT_API_KEY - Extract resolveCopilotAuthToken() as a testable function with JSDoc and export it from server.js - Add 8 unit tests covering precedence, fallback, empty/whitespace handling, and trimming behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Smoke test results: ✅ GitHub MCP: Overall: PASS
|
🔥 Smoke Test Results — PASS
PR: feat: add Copilot BYOK support via COPILOT_API_KEY Overall: ✅ PASS
|
|
Smoke test results:
|
🔍 Chroot Version Comparison Results
Overall: ❌ Not all tests passed — Python and Node.js versions differ between host and chroot environment.
|
Smoke Test: GitHub Actions Services Connectivity ✅
All checks passed.
|
🏗️ Build Test Suite Results
Overall: 8/8 ecosystems passed — ✅ PASS
|
Adds support for
COPILOT_API_KEYas a direct Copilot API key (BYOK) alongside the existingCOPILOT_GITHUB_TOKENOAuth flow. When api-proxy is enabled, the key is held exclusively in the sidecar — never exposed to the agent.Changes
src/types.ts— newcopilotApiKey?: stringfield onWrapperConfigsrc/cli.ts— readsCOPILOT_API_KEYfrom env; updatesvalidateApiProxyConfigto treat either token as satisfying the Copilot key requirement; excludes from redacted config logsrc/docker-manager.ts:COPILOT_API_KEYtoAWF_ONE_SHOT_TOKENSplaceholder-token-for-credential-isolation) when api-proxy is enabled to block--env-allleakageCOPILOT_API_URL+COPILOT_TOKENplaceholder now also set whencopilotApiKeyis providedcontainers/api-proxy/server.js— introducesCOPILOT_AUTH_TOKEN = COPILOT_GITHUB_TOKEN || COPILOT_API_KEY(GitHub token takes precedence); Copilot proxy starts with either key presentcontainers/agent/api-proxy-health-check.sh— validatesCOPILOT_API_KEYis placeholder when api-proxy is activeUsage
COPILOT_GITHUB_TOKENandCOPILOT_API_KEYare interchangeable; if both are set,COPILOT_GITHUB_TOKENtakes precedence in the proxy.