feat: GitHub Copilot authentication in headless containerised deployments
Problem
copilot-bridge relies on an authenticated GitHub Copilot session, which is currently managed by the Copilot CLI on the host machine. When running inside a Docker container, there is no browser, no interactive terminal for device flow, and no persistent host credential store available by default.
This is a blocking issue for the containerised deployment architecture - without a solution, agent containers cannot authenticate to GitHub Copilot and the bridge cannot function.
Motivation
For containerised deployments (and headless servers generally), authentication needs to:
- Work without human interaction on every container start
- Not bake credentials into the container image
- Support rotation without rebuilding images
- Work the same way on Linux and macOS hosts
The current interactive gh auth login flow works on a developer workstation but is not viable for a container that starts automatically on boot or restart.
Background: How Copilot auth works today
The Copilot CLI authenticates via GitHub OAuth and stores a token in the host credential store (e.g. macOS Keychain, Linux libsecret, or ~/.config/gh/hosts.yml). This token is then used by the bridge to make Copilot API calls.
Inside a container, none of these stores are available unless explicitly mounted.
Proposed Solution
Recommended: GitHub PAT via 1Password + op inject
Since copilot-bridge already uses op inject to render config.json.tpl at startup (see issue #142), the GitHub PAT is simply another secret in the same 1Password vault. The entrypoint renders it alongside the bot tokens and writes it to the expected credential location:
config.json.tpl:
{
"github": {
"token": "{{ op://Vault/copilot-bridge/github-pat }}"
}
}
entrypoint.sh:
# op inject renders all secrets including the GitHub PAT
op inject -i /config/config.json.tpl -o /tmp/config.json
# Wire the PAT into the gh CLI credential store
GITHUB_PAT=$(node -e "console.log(require('/tmp/config.json').github.token)")
mkdir -p /home/node/.config/gh
printf "github.com:\n oauth_token: %s\n" "$GITHUB_PAT" \
> /home/node/.config/gh/hosts.yml
No additional secrets mechanism needed - consistent with the established pattern, rotatable in one place, no host coupling.
The PAT must have Copilot scope and be tied to a licensed human GitHub account (see "Considered and rejected" below for why service accounts are not viable).
Considered and Rejected
Option A: Mount host credential file
Mount ~/.config/gh/hosts.yml from the host into the container as a read-only volume:
volumes:
- ~/.config/gh/hosts.yml:/home/node/.config/gh/hosts.yml:ro
Rejected because: couples the container tightly to the host user session, reduces isolation, and breaks if the host token expires or is rotated. Acceptable as a quick-start shortcut for local development only - not suitable for production.
Option C: GitHub App or service account
Register a GitHub App and use short-lived installation tokens instead of a PAT.
Rejected because: GitHub Copilot access is restricted to licensed human accounts. GitHub Apps and service accounts cannot be granted Copilot access. This option is not viable regardless of implementation complexity.
Deliverables
Reported By
Agent (automated) - drafted collaboratively with user raykao
feat: GitHub Copilot authentication in headless containerised deployments
Problem
copilot-bridge relies on an authenticated GitHub Copilot session, which is currently managed by the Copilot CLI on the host machine. When running inside a Docker container, there is no browser, no interactive terminal for device flow, and no persistent host credential store available by default.
This is a blocking issue for the containerised deployment architecture - without a solution, agent containers cannot authenticate to GitHub Copilot and the bridge cannot function.
Motivation
For containerised deployments (and headless servers generally), authentication needs to:
The current interactive
gh auth loginflow works on a developer workstation but is not viable for a container that starts automatically on boot or restart.Background: How Copilot auth works today
The Copilot CLI authenticates via GitHub OAuth and stores a token in the host credential store (e.g. macOS Keychain, Linux
libsecret, or~/.config/gh/hosts.yml). This token is then used by the bridge to make Copilot API calls.Inside a container, none of these stores are available unless explicitly mounted.
Proposed Solution
Recommended: GitHub PAT via 1Password +
op injectSince copilot-bridge already uses
op injectto renderconfig.json.tplat startup (see issue #142), the GitHub PAT is simply another secret in the same 1Password vault. The entrypoint renders it alongside the bot tokens and writes it to the expected credential location:config.json.tpl:{ "github": { "token": "{{ op://Vault/copilot-bridge/github-pat }}" } }entrypoint.sh:No additional secrets mechanism needed - consistent with the established pattern, rotatable in one place, no host coupling.
The PAT must have Copilot scope and be tied to a licensed human GitHub account (see "Considered and rejected" below for why service accounts are not viable).
Considered and Rejected
Option A: Mount host credential file
Mount
~/.config/gh/hosts.ymlfrom the host into the container as a read-only volume:Rejected because: couples the container tightly to the host user session, reduces isolation, and breaks if the host token expires or is rotated. Acceptable as a quick-start shortcut for local development only - not suitable for production.
Option C: GitHub App or service account
Register a GitHub App and use short-lived installation tokens instead of a PAT.
Rejected because: GitHub Copilot access is restricted to licensed human accounts. GitHub Apps and service accounts cannot be granted Copilot access. This option is not viable regardless of implementation complexity.
Deliverables
entrypoint.shto wire the GitHub PAT from the rendered config into the gh CLI credential storeconfig.json.tplto include agithub.tokenfield with anop://referencedocker-compose.ymlexample to show the full flowReported By
Agent (automated) - drafted collaboratively with user raykao