-
Notifications
You must be signed in to change notification settings - Fork 6
feat: GitHub Copilot authentication in headless containerised deployments #144
Description
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.ymlNo 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:roRejected 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
- Document all three options in a "Containerised Auth" section of the docs
- Update
entrypoint.shto wire the GitHub PAT from the rendered config into the gh CLI credential store - Update
config.json.tplto include agithub.tokenfield with anop://reference - Update
docker-compose.ymlexample to show the full flow
Reported By
Agent (automated) - drafted collaboratively with user raykao