GitHub Copilot CLI
The web shell environment ships the
GitHub Copilot CLI — the copilot
command gives you AI-assisted suggestions, explanations, and chat right in
the browser terminal.
Build and deploy with built-in PaaS guidance
In the default shell app (https://shell.54.195.181.75.sslip.io/), the
CLI starts with platform instructions in ~/.copilot/copilot-instructions.md
and can load the bundled paas-deploy skill from
~/.copilot/skills/paas-deploy/SKILL.md. The skill covers building a
self-contained app, connecting over the container's existing Dokku SSH
identity, pushing to the master deploy branch, and checking the actual URL,
logs and HTTP response. This works whether Copilot uses GitHub-hosted models
or the Bedrock wrapper; no live documentation scrape or new service is needed.
After signing in (if required), try:
copilot
> Make me a small expense tracker and deploy it to a new app named expenses-demo.
> Add a health endpoint to my existing app and help me redeploy it.
The CLI should clarify only app-specific choices, confirm the target app,
inspect it before updates, and explain what it is doing. You can view
available skills with /skills. Inside this container the Dokku host is
dokku: the CLI uses ssh dokku@dokku ... and pushes to
dokku@dokku:<app> (for example git push dokku HEAD:master). Do not use
host-only sudo dokku commands in this terminal.
Important limits:
- The mounted deployer key is shared and can administer all Dokku apps;
browser
?app=scoping does not restrict its Dokku permissions. Only use the app you actually intend to deploy; never expose private keys or another app's configuration in chat. Copilot asks before destructive or disruptive operations. This is not a secure multi-tenant deploy identity. - Only the preinstalled
shellimage has this CLI and skill. Opening a browser shell with?app=another-appuses that app's own image, which may not include Copilot at all. - Files created in the
shellcontainer, including project source and saved login state, disappear after a restart or redeploy. Back up your app to a separate, durable Git remote; the Dokku deployment remote is not a substitute for a source backup. A shell app redeploy also interrupts open terminal sessions. - Newly created apps serve HTTP until a platform operator enables
Let's Encrypt on the host. Ask for HTTPS when ready; do not assume it is
available just because a URL resolves. Datastores
demo-dbanddemo-cacheare shared across linked apps, so request access only when your app needs them and plan data separation.
For the platform contract and a manual walkthrough, see Application requirements and Deploy your first app.
Requirements: a GitHub account with an active Copilot plan (Free, Pro, Business, or Enterprise). If your GitHub account is managed by an organization, that organization must also enable its Copilot CLI policy. Each prompt consumes your plan's premium-request quota.
Signing in (one time per app)
There is no browser inside the container, so the CLI's default desktop web flow cannot complete here. Sign-in uses GitHub's device code flow instead — you finish it in a second browser tab:
Open your web shell (see Web shell).
Run the setup helper:
~/copilot-setup(equivalently:
copilot login --device-code)The terminal prints a one-time code and a URL. Open
https://github.com/login/devicein a new browser tab, sign in to GitHub if asked, enter the code, and authorize.Back in the shell the CLI confirms the login. From now on just run:
copilot
~/copilot-setup is safe to re-run any time: it detects an existing login
(a stored token, not just the CLI's config file) or an operator-provided
token and tells you when no sign-in is needed. Launching copilot first
does not confuse it — quit the CLI and re-run the helper to sign in.
Staying signed in
There is no system keychain inside the container, so the CLI stores its
OAuth token in plaintext in ~/.copilot/config.json (alongside the CLI's
own metadata, which appears there on first launch). The file lives in the
app container, so:
- Closing and re-opening the browser shell does not force a re-login.
- A container restart or redeploy wipes it — re-run
~/copilot-setup. - The login is shared by everyone who has shell access to the same
app. The shell runs one container per app and every session shares it.
On the shared default
shellapp, sign in only if you accept that; for a private Copilot login, ask the operator for a shell scoped to your own app — saved logins are per app, never platform-wide.
Operator: token-based auth instead of per-user login
An operator can give an app's sessions a GitHub token directly, so no interactive login is needed at all:
Create a fine-grained personal access token owned by a personal GitHub account, with the Copilot Requests account permission. (Classic
ghp_tokens are not supported by the Copilot CLI.)Set it on the app:
dokku config:set <app> COPILOT_GITHUB_TOKEN=github_pat_...Let web-shell sessions inherit it: on the Dokku host, allowlist the variable for the shell backend and restart it —
printf '[Service]\nEnvironment="SHELL_ENV_ALLOWLIST=COPILOT_GITHUB_TOKEN"\n' \ | sudo tee /etc/systemd/system/paas-shell-backend.service.d/copilot.conf sudo systemctl daemon-reload sudo systemctl restart paas-shell-backend
New sessions then start with COPILOT_GITHUB_TOKEN set and the CLI
authenticates with it (credential precedence: COPILOT_GITHUB_TOKEN >
GH_TOKEN > GITHUB_TOKEN > stored OAuth login). Note this token is
app-wide and visible to the operator — it is shared by every session
of that app, exactly like a saved interactive login. dokku config:set
restarts the app, which drops any active shell sessions of that app.
Operator: use Amazon Bedrock instead of GitHub models
When AWS credentials are present in a web-shell session, copilot runs
through Bedrock's OpenAI-compatible endpoint
(https://bedrock-runtime.<region>.amazonaws.com/openai/v1) with
global.moonshotai.kimi-k3 as the default model, and GitHub sign-in is not
needed. A short-lived bearer token is derived from the access key at every
credential request by bin/bedrock-token (pure sh + openssl); it is never
written to disk or logged.
Keyless (recommended on EC2): if no AWS keys are in the session, bin/bedrock-token
reads the instance role's credentials from IMDSv2 (hop limit 2 lets the
container reach it). iam.tf grants the role dokku-bedrock-invoke
(bedrock:InvokeModel*, CallWithBearerToken), and the metadata service is open to
containers (block_container_imds = false, the default). Both are applied with
terraform apply (no instance replacement); see app-container-hardening.md item 7
for the security trade-off. No env vars needed.
Alternative with static keys: pass the credentials through to sessions: set them on the app
and allowlist them for the shell backend (same mechanism as
COPILOT_GITHUB_TOKEN above):
dokku config:set shell AWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... AWS_REGION=eu-west-1
# backend drop-in, as in the token section:
# Environment="SHELL_ENV_ALLOWLIST=AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN,AWS_REGION"
The IAM identity needs bedrock:CallWithBearerToken and model-invoke
permissions. Region defaults to eu-west-1. Users can override the model
with BEDROCK_MODEL=<id> copilot or copilot --model <id>, and the token
lifetime with BEDROCK_TOKEN_TTL (seconds, default 3600). Use
COPILOT_USE_GITHUB=1 copilot (or command copilot) for the GitHub-hosted
CLI. Missing keys or a failed token derivation print a clear message and
exit 1 before the CLI starts.
Troubleshooting
~/copilot-setupsays the CLI is not installed: the app environment predates the Copilot install — ask the operator to update theshellapp.- The CLI reports you have no Copilot subscription: your GitHub account needs an active plan — see https://github.com/features/copilot/plans.
- The device page says the code expired: codes are short-lived; run
~/copilot-setupagain for a fresh one. - API calls time out: the platform needs egress to
github.comandapi.individual.githubcopilot.com(open on the default build).