Add GitHub Codespaces as a Crabbox Linux provider
Summary
Add a first-party github-codespaces provider for Crabbox so users can acquire
short-lived Linux leases backed by GitHub Codespaces, run normal Crabbox
repo-sync and command workflows, then stop or delete the codespace safely.
The best Phase 1 shape is an SSH-lease provider that relies on GitHub's
supported Codespaces access path:
- use the GitHub Codespaces REST API or
gh codespace JSON commands for
create/list/view/start/stop/delete;
- use
gh codespace ssh --config or an equivalent gh-mediated OpenSSH config
to expose the codespace as a normal OpenSSH target;
- let Crabbox core own sync, command execution,
ssh, status rendering, local
claims, and release once a usable SSH target is available.
This is not a brokered coordinator provider in the first implementation.
Codespaces are owned by the authenticated GitHub user or billed organization,
and the direct CLI should reuse the operator's existing gh authentication or a
GitHub token with Codespaces access.
Motivation
GitHub Codespaces is a strong missing Crabbox target:
- It is already tied to a GitHub repository, branch, devcontainer, machine type,
idle timeout, and retention period, which matches Crabbox's short-lived
repo-workflow use case.
- GitHub exposes a Codespaces REST surface for create/list/get/update/delete,
start/stop, repository defaults, permission checks, and machine type discovery.
- GitHub CLI exposes the operational pieces Crabbox needs locally:
gh codespace create, list --json, view --json, ssh --config, cp,
ports, stop, and delete.
- If a codespace has an SSH server installed,
gh codespace ssh --config can
generate OpenSSH configuration, letting Crabbox reuse its existing SSH/sync/run
machinery instead of inventing a new remote execution protocol.
The main risk is that Codespaces is not a raw VM API. The public REST object does
not hand back a normal host, port, username, and private key. Crabbox should
treat the GitHub CLI/OpenSSH integration as the supported connection layer and
fail clearly when the selected devcontainer image does not include an SSH server.
Recommended phase 1 scope
Implement provider: github-codespaces as a Linux-only, direct-only SSH lease
provider.
Provider spec:
Name: github-codespaces
Aliases: codespaces, gh-codespaces
Family: github-codespaces
Kind: ssh-lease
Targets: linux
Features: ssh, crabbox-sync, cleanup
Coordinator: never
Phase 1 should support:
crabbox warmup --provider github-codespaces
crabbox run --provider github-codespaces -- <command>
crabbox ssh --provider github-codespaces --id <lease-or-slug>
crabbox list --provider github-codespaces --json
crabbox status --provider github-codespaces --id <lease-or-slug> --json
crabbox stop --provider github-codespaces <lease-or-slug>
crabbox cleanup --provider github-codespaces --dry-run
crabbox doctor --provider github-codespaces
Phase 1 should not attempt Windows, macOS, VNC/desktop/browser/code-server,
coordinator brokerage, GitHub organization admin listing, Codespaces secrets
management, prebuild management, port visibility management, or unpublished
codespace export/publish flows.
GitHub API and CLI fit
The local GitHub docs and live official docs show the lifecycle operations
Crabbox needs:
POST /repos/{owner}/{repo}/codespaces creates a repository codespace.
POST /repos/{owner}/{repo}/pulls/{pull_number}/codespaces creates a
codespace from a pull request.
GET /repos/{owner}/{repo}/codespaces lists repository codespaces for the
authenticated user.
GET /user/codespaces lists the authenticated user's codespaces.
GET /user/codespaces/{codespace_name} gets one codespace.
PATCH /user/codespaces/{codespace_name} updates machine/display metadata.
POST /user/codespaces/{codespace_name}/start starts a codespace.
POST /user/codespaces/{codespace_name}/stop stops a codespace.
DELETE /user/codespaces/{codespace_name} deletes a codespace.
GET /repos/{owner}/{repo}/codespaces/new gets default attributes.
GET /repos/{owner}/{repo}/codespaces/permissions_check checks whether
devcontainer permissions have been accepted.
GET /repos/{owner}/{repo}/codespaces/machines lists repository machine
types.
GET /user/codespaces/{codespace_name}/machines lists alternate machine
types for an existing codespace.
Useful create request fields include:
ref
geo or legacy location
machine
devcontainer_path
multi_repo_permissions_opt_out
working_directory
idle_timeout_minutes
display_name
retention_period_minutes
Useful codespace response fields include:
name
display_name
environment_id
state
machine
repository
location
idle_timeout_minutes
web_url
machines_url
start_url
stop_url
retention_period_minutes
retention_expires_at
- pending operation fields
GitHub CLI help adds the most important SSH details:
gh codespace ssh --config writes OpenSSH config for codespaces.
- Once included in
~/.ssh/config, ordinary ssh, scp, rsync, and related
OpenSSH tools can target codespaces.
gh codespace ssh automatically manages a local SSH key if needed.
- The codespace image must have an SSH server installed. The CLI docs recommend
adding the ghcr.io/devcontainers/features/sshd:1 devcontainer feature when
the image does not include one.
That makes gh a practical first implementation dependency even if direct REST
is used for some control-plane calls.
Recommended implementation strategy
Use a hybrid gh plus REST design, with all subprocess calls routed through
Crabbox Runtime.Exec and all HTTP calls routed through Runtime.HTTP.
The strongest Phase 1 path:
- Require
gh on PATH, unless explicit REST-only mode is later designed.
- Use
gh auth status or a lightweight authenticated gh api call in
doctor to prove the user has GitHub API access without printing tokens.
- Resolve the target repository from
githubCodespaces.repo, --repo, or the
current git remote using Crabbox's existing GitHub repo parsing helpers.
- Before creation, call repository defaults and permissions-check endpoints
where possible so missing Codespaces entitlement, missing repo access,
unaccepted devcontainer permissions, or unavailable machine types fail before
Crabbox creates a lease claim.
- Create the codespace with
POST /repos/{owner}/{repo}/codespaces or gh codespace create, passing ref, machine, devcontainer path, working directory,
idle timeout, retention, and display name.
- Poll
gh codespace view --json ... or GET /user/codespaces/{name} until
the state is usable.
- Generate an OpenSSH config for that codespace with
gh codespace ssh --config -c <name>.
- Store the generated config in a private per-lease routing file, mode
0600,
similar to providers that need provider-specific SSH/proxy state.
- Return an
SSHTarget that uses OpenSSH with that config. The exact mechanics
should follow existing proxy-based providers such as Tenki, KubeVirt,
Sprites, or External rather than hardcoding a public host assumption.
- Set the remote work root to the repository's codespace workspace path by
default, for example /workspaces/<repo-name>/crabbox, unless the user
configured githubCodespaces.workRoot.
- Let core Crabbox perform sync and command execution over SSH.
- On release, choose stop or delete based on config:
- default: delete a Crabbox-created, non-kept codespace;
- optional: stop kept codespaces so state can be reused;
- never delete a codespace that Crabbox did not create or cannot identify as
owned by the local claim.
Do not make the first implementation depend on an undocumented internal VS Code
or Codespaces protocol. If gh codespace ssh --config cannot provide a stable
OpenSSH target for Crabbox, downgrade the implementation plan to a delegated-run
provider that shells through gh codespace ssh -c <name> -- <command> and
documents the reduced sync/artifact surface.
Configuration
Suggested config:
provider: github-codespaces
target: linux
class: standard
githubCodespaces:
repo: ""
ref: ""
machine: ""
devcontainerPath: ""
workingDirectory: ""
geo: ""
idleTimeout: 30m
retentionPeriod: 1h
deleteOnRelease: true
ghPath: gh
workRoot: ""
Suggested flags:
--github-codespaces-repo
--github-codespaces-ref
--github-codespaces-machine
--github-codespaces-devcontainer-path
--github-codespaces-working-directory
--github-codespaces-geo
--github-codespaces-idle-timeout
--github-codespaces-retention-period
--github-codespaces-delete-on-release
--github-codespaces-gh
--github-codespaces-work-root
Suggested environment overrides:
GH_TOKEN / GITHUB_TOKEN GitHub API token used by gh/api
CRABBOX_GITHUB_CODESPACES_REPO owner/repo override
CRABBOX_GITHUB_CODESPACES_REF branch or ref override
CRABBOX_GITHUB_CODESPACES_MACHINE machine type, e.g. standardLinux32gb
CRABBOX_GITHUB_CODESPACES_DEVCONTAINER_PATH devcontainer path override
CRABBOX_GITHUB_CODESPACES_WORKING_DIRECTORY codespace working directory
CRABBOX_GITHUB_CODESPACES_GEO requested geo
CRABBOX_GITHUB_CODESPACES_IDLE_TIMEOUT idle timeout as Go duration
CRABBOX_GITHUB_CODESPACES_RETENTION_PERIOD retention as Go duration
CRABBOX_GITHUB_CODESPACES_DELETE_ON_RELEASE true/false
CRABBOX_GITHUB_CODESPACES_GH gh executable path
CRABBOX_GITHUB_CODESPACES_WORK_ROOT Crabbox sync work root
Do not accept GitHub tokens as command-line arguments. Keep tokens in gh auth,
environment variables, or the user's credential store.
--class can map to a small set of GitHub machine types only after the provider
can query available machine types for the selected repo. Until then, explicit
githubCodespaces.machine / --github-codespaces-machine is safer than guessing
global names. If --type is already the generic exact machine-type flag in
Crabbox, map it to Codespaces machine.
Lifecycle
Phase 1 lifecycle:
- Resolve GitHub repo and ref.
- Validate
target=linux; Codespaces is a Linux devcontainer surface for this
provider.
- Validate
gh availability and authenticated access.
- Check repository Codespaces defaults and devcontainer permission acceptance
when the endpoints are available.
- Resolve or validate machine type from repository machine types.
- Allocate a Crabbox lease id and slug.
- Create a codespace with a display name that embeds the Crabbox slug/lease in
a human-readable way.
- Record a local claim containing:
- provider =
github-codespaces
- lease id
- slug
- repo owner/name
- ref
- codespace name
- environment id
- machine
- devcontainer path
- working directory
- authenticated GitHub login when available
- delete-on-release policy
- Poll until the codespace is available.
- Generate and store per-lease OpenSSH config through
gh codespace ssh --config -c <codespace>.
- Return an SSH target using that config and a readiness check that proves
git, rsync, tar, and the configured work root are usable. Do not
assume python3, Node.js, or npm are present in a minimal Codespaces base
image; install or validate them only when Crabbox core or a requested
workflow actually needs them.
- Let core Crabbox run sync/command/ssh flows.
- Touch/status should refresh state from GitHub and update local labels only;
there is no provider-side Crabbox tag equivalent.
- Stop should delete or stop the codespace according to ownership and
deleteOnRelease.
- Cleanup should only act on codespaces with complete local Crabbox claim
state and matching GitHub identity/repo. Without local claim proof, it may
report candidate names but should not delete them.
Ownership and cleanup safety
Codespaces does not expose cloud-style arbitrary resource tags equivalent to
DigitalOcean, Linode, or Hetzner. Use local claims as the primary ownership
source, plus display-name naming as a human hint only.
Suggested display name:
crabbox <slug> <short-lease-id>
Suggested local claim identity fields:
provider=github-codespaces
lease=<cbx_...>
slug=<slug>
repo=<owner>/<repo>
ref=<ref>
codespace=<codespace_name>
environment_id=<environment_id>
github_login=<login>
target=linux
delete_on_release=<true|false>
expires_at=<unix-seconds>
Stop/delete should refuse when:
- the local claim is absent and
--reclaim was not supplied;
- the claim provider is not
github-codespaces;
- the claim's repo or GitHub login does not match the live codespace;
- the live codespace name/environment id differs from the claim;
- the codespace has unsaved changes and the requested operation would require a
forced delete, unless the user explicitly passes a force flag that Crabbox
wires through intentionally.
Cleanup should be conservative. Because Codespaces are personal development
environments, accidental deletion is more damaging than leaving a stale stopped
codespace. The first PR should prefer cleanup --dry-run and claim-backed
delete only.
Devcontainer and SSH-server contract
Crabbox should document this provider's strongest prerequisite plainly:
- A Codespaces devcontainer image must expose SSH for
gh codespace ssh.
- If the image does not include SSH, users can add the devcontainer SSHD feature:
{
"features": {
"ghcr.io/devcontainers/features/sshd:1": {
"version": "latest"
}
}
}
The provider should detect SSH config/readiness failures and return a clear
error that tells the user to add SSHD to the devcontainer rather than presenting
it as a generic Crabbox sync failure.
Implementation outline
Likely files to add or modify:
| File |
Change |
internal/providers/githubcodespaces/provider.go |
Register provider name/aliases, spec, flags, Configure, ConfigureDoctor, and server-type mapping. |
internal/providers/githubcodespaces/backend.go |
Implement acquire, resolve, list, release, touch, cleanup, and doctor. |
internal/providers/githubcodespaces/client.go |
Wrap gh api or direct REST calls for create/list/view/start/stop/delete/machines/defaults/permissions. |
internal/providers/githubcodespaces/gh.go |
Wrap gh codespace subprocess calls through Runtime.Exec, including ssh --config. |
internal/providers/githubcodespaces/ssh_config.go |
Parse/store generated OpenSSH config and construct the Crabbox SSHTarget. |
internal/providers/githubcodespaces/provider_test.go |
Provider spec, aliases, target/features, flag behavior. |
internal/providers/githubcodespaces/client_test.go |
Fake HTTP/API fixtures, status parsing, redaction, retry/error handling. |
internal/providers/githubcodespaces/gh_test.go |
Recording command runner tests for every gh invocation and argv redaction. |
internal/providers/githubcodespaces/backend_test.go |
Fake lifecycle tests for acquire/resolve/list/release/cleanup and SSH target construction. |
internal/providers/all/all.go |
Blank-import the provider package. |
internal/providers/all/all_test.go |
Add registration/discovery coverage if the test enumerates built-ins. |
internal/cli/config.go |
Add typed GitHubCodespacesConfig, file config, defaults, env overrides, and config show support. |
internal/cli/config_test.go |
Config file/env/default/redaction coverage. |
docs/providers/github-codespaces.md |
Provider runbook. |
docs/providers/provider-metadata.json |
Add generated matrix metadata. |
docs/providers/README.md |
Regenerate with node scripts/generate-provider-matrix.mjs. |
docs/features/providers.md |
Add direct-provider note if needed. |
docs/source-map.md |
Add the new provider package and docs paths. |
scripts/live-github-codespaces-smoke.sh |
Optional guarded live smoke. |
scripts/live-github-codespaces-smoke.test.js |
Test live-smoke classification and redaction without live GitHub calls. |
Do not touch run.go, repo.go, coordinator.go, or shared provider
interfaces unless implementation proves a provider-neutral extension is required.
The provider authoring guide explicitly treats such changes as a checkpoint.
Tests and docs
Implementation should include:
- Provider registration tests so
github-codespaces, codespaces, and
gh-codespaces resolve as expected.
- Provider spec tests proving Linux-only, direct-only, SSH/sync/cleanup
behavior.
- Config tests for YAML, environment overrides, defaults, and redaction.
- Command-runner tests proving:
gh is invoked through Runtime.Exec;
- no token appears in argv;
- repo/ref/machine/devcontainer/timeout/retention flags are passed correctly;
gh codespace ssh --config -c <name> is called for SSH target generation;
- delete/stop commands include noninteractive force only where explicitly
intended.
- API/client tests proving:
- create request includes repo/ref/machine/devcontainer/working directory,
idle timeout, retention, and display name;
- list/view/start/stop/delete states are parsed;
- 401/403/404/409/422/503 errors produce actionable, secret-safe diagnostics;
Retry-After is honored for rate/temporary failures where exposed;
- pending-operation states do not pretend SSH is ready.
- Backend tests for:
- acquire happy path;
- resolve by lease id, slug, and codespace name;
- release delete-on-release and stop-on-release modes;
- cleanup dry-run and claim-backed delete;
- missing SSH server failure message;
- identity mismatch refusal;
- no local claim means no destructive cleanup by default.
- Docs:
docs/providers/github-codespaces.md;
- generated provider matrix update;
docs/source-map.md;
- explicit devcontainer SSHD prerequisite;
- token/auth guidance and no-token-on-argv warning;
- limitations around no desktop/browser/code/coordinator in Phase 1.
Suggested opt-in live smoke:
CRABBOX_LIVE=1 \
CRABBOX_LIVE_PROVIDERS=github-codespaces \
CRABBOX_GITHUB_CODESPACES_REPO=example-org/example-repo \
scripts/live-github-codespaces-smoke.sh
The smoke should:
- Build
bin/crabbox.
- Verify
gh auth status.
- Require a repo configured for Codespaces and an SSH-capable devcontainer.
- Create a small codespace with a short idle timeout and retention period.
- Run
bin/crabbox status --provider github-codespaces --id <lease> --wait.
- Run
bin/crabbox run --provider github-codespaces --id <lease> --no-sync -- echo crabbox-codespaces-ok.
- Run a tiny sync-backed command if the fixture repo is safe for rsync.
- Stop/delete the codespace.
- Verify no Crabbox-owned claim-backed Codespaces remain.
Final classifications should include:
classification=live_github_codespaces_smoke_passed
classification=environment_blocked
classification=auth_blocked
classification=quota_or_billing_blocked
classification=repo_policy_blocked
classification=ssh_server_missing
classification=validation_failed
Acceptance criteria
crabbox providers lists github-codespaces as a Linux SSH-lease provider.
- Aliases
codespaces and gh-codespaces resolve if maintainers accept the
alias surface.
crabbox doctor --provider github-codespaces performs only non-mutating
checks: gh availability, GitHub auth, repo access, Codespaces availability,
machine/default discovery, and claim-backed inventory.
crabbox warmup --provider github-codespaces --github-codespaces-repo owner/repo
creates or starts a codespace and returns a Crabbox lease id and slug.
crabbox run --provider github-codespaces -- echo ok can execute over the
generated OpenSSH target when the devcontainer includes SSHD.
crabbox ssh --provider github-codespaces --id <lease-or-slug> connects
through the same provider-specific SSH route.
crabbox list/status --provider github-codespaces --json show normalized
lease state without leaking tokens or generated SSH config secrets.
crabbox stop --provider github-codespaces <lease-or-slug> stops or deletes
only the claim-backed codespace according to config.
crabbox cleanup --provider github-codespaces --dry-run reports only
claim-backed Crabbox codespaces and does not mutate anything.
- Destructive cleanup refuses foreign, mismatched, or claimless codespaces.
- Errors do not print
GH_TOKEN, GITHUB_TOKEN, authorization headers,
generated SSH private key material, or full OpenSSH config contents.
- Docs explain auth, repo/ref/devcontainer/machine settings, the SSHD
prerequisite, lifecycle, cleanup safety, and Phase 1 limitations.
- Unit tests use fake HTTP/command runners and do not require live GitHub
credentials.
- Optional live smoke either passes or exits with one of the explicit
classifications above.
Verification commands for the implementation PR
go test ./internal/providers/githubcodespaces ./internal/providers/all ./internal/cli
go test -race ./internal/providers/githubcodespaces ./internal/providers/all ./internal/cli
go vet ./...
npm test --prefix worker
node scripts/generate-provider-matrix.mjs
scripts/check-docs.sh
Optional live validation:
CRABBOX_LIVE=1 \
CRABBOX_LIVE_PROVIDERS=github-codespaces \
CRABBOX_GITHUB_CODESPACES_REPO=example-org/example-repo \
scripts/live-github-codespaces-smoke.sh
Research notes
Local GitHub docs inspected:
docs/content/rest/codespaces/codespaces.md
docs/content/rest/codespaces/machines.md
docs/src/rest/data/fpt-2022-11-28/codespaces.json
docs/src/rest/data/ghec-2026-03-10/codespaces.json
docs/content/codespaces/developing-in-a-codespace/using-github-codespaces-with-github-cli.md
docs/content/codespaces/reference/security-in-github-codespaces.md
docs/content/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers.md
Crabbox docs and source inspected:
docs/features/provider-authoring.md
docs/provider-backends.md
docs/providers/README.md
docs/providers/digitalocean.md
docs/providers/daytona.md
docs/providers/semaphore.md
docs/providers/tenki.md
docs/source-map.md
internal/cli/provider_backend.go
internal/cli/config.go
internal/providers/all/all.go
internal/providers/digitalocean
internal/providers/daytona
internal/providers/semaphore
internal/providers/tenki
internal/providers/sprites
Official current references checked:
Live verification notes
Verified live against the coygeek GitHub account on 2026-06-13 with a
short-lived GitHub token. Do not copy tokens into issues, PRs, logs, commits, or
command-line arguments.
Disposable fixture:
- Fixture repo created:
coygeek/crabbox-codespaces-smoke-june13.
- Default branch:
main.
- Fixture files added:
README.md
.devcontainer/devcontainer.json
- The devcontainer uses
mcr.microsoft.com/devcontainers/base:ubuntu and the
ghcr.io/devcontainers/features/sshd:1 feature.
- The fixture repo was intentionally retained so the next developer can rerun
the smoke without recreating the repo. The live codespace created during this
verification was deleted.
Token/API behavior proved:
GH_TOKEN=<redacted> authenticated as coygeek.
GET /user/codespaces?per_page=1 returned HTTP 200 with
X-Accepted-GitHub-Permissions: codespaces=read.
gh codespace list --json ... worked and initially returned an empty list.
GET /repos/coygeek/crabbox-codespaces-smoke-june13/codespaces/new worked
and returned billable_owner=coygeek; most defaults were null before an
explicit create request.
GET /repos/coygeek/crabbox-codespaces-smoke-june13/codespaces/machines
returned four Linux machines:
basicLinux32gb - 2 cores, 8 GB RAM, 32 GB storage
standardLinux32gb - 4 cores, 16 GB RAM, 32 GB storage
premiumLinux - 8 cores, 32 GB RAM, 64 GB storage
largePremiumLinux - 16 cores, 64 GB RAM, 128 GB storage
GET /repos/coygeek/crabbox-codespaces-smoke-june13/codespaces/permissions_check?ref=main&devcontainer_path=.devcontainer/devcontainer.json
returned { "accepted": true }.
DELETE /user/codespaces/{name} returned HTTP 202 with
X-Accepted-GitHub-Permissions: codespaces=write.
Codespace lifecycle proved:
- Create endpoint used:
POST /repos/coygeek/crabbox-codespaces-smoke-june13/codespaces.
- Create parameters used:
ref=main
machine=basicLinux32gb
devcontainer_path=.devcontainer/devcontainer.json
idle_timeout_minutes=10
retention_period_minutes=60
display_name="crabbox smoke june13"
- Created codespace name followed GitHub's generated name format, e.g.
crabbox-smoke-june13-<suffix>.
- Observed create state transition:
Provisioning -> Available.
gh codespace view --json ... returned:
repository as the string coygeek/crabbox-codespaces-smoke-june13, not
an object. Provider code should not assume repository.nameWithOwner for
gh JSON output.
devcontainerPath=.devcontainer/devcontainer.json
machineName=basicLinux32gb
idleTimeoutMinutes=10
retentionPeriodDays=0 for a 60-minute retention request.
- Stop/start endpoints worked:
POST /user/codespaces/{name}/stop
POST /user/codespaces/{name}/start
- Observed stop/start state transitions:
ShuttingDown -> Shutdown -> Starting -> Available.
- After delete,
gh codespace list -R coygeek/crabbox-codespaces-smoke-june13
returned an empty list.
SSH and sync behavior proved:
gh codespace ssh --config -c <name> generated a 0600 OpenSSH config with:
- host alias format
cs.<codespace-name>.<branch>
User vscode
- a
ProxyCommand that calls GitHub CLI's Codespaces SSH stdio path
IdentityFile ~/.ssh/codespaces.auto
- Plain OpenSSH with the generated config worked:
ssh -F /tmp/crabbox-codespaces-smoke-ssh-config <generated-host-alias> 'echo openssh-ok'.
- Remote command execution over plain OpenSSH worked.
rsync -e "ssh -F /tmp/crabbox-codespaces-smoke-ssh-config" to /tmp worked
and the transferred proof file was readable remotely.
- The same generated config still worked after stop/start.
- Direct
gh codespace ssh -c <name> -- <command> failed in this environment
with:
vscode@localhost: Permission denied (publickey,password).
shell closed: exit status 255
- Provider implementation should therefore prefer the generated config plus
plain ssh -F <config> / rsync -e "ssh -F <config>" path for command and
sync execution. Do not assume direct gh codespace ssh -- <command> is
equivalent.
Remote environment observed:
- Remote user:
vscode.
- Default remote working directory for SSH:
/home/vscode.
- Repository workspace exists at:
/workspaces/crabbox-codespaces-smoke-june13.
- Passwordless sudo works:
sudo -n true.
- Present by default in the minimal fixture:
git=/usr/local/bin/git
rsync=/usr/bin/rsync
tar=/usr/bin/tar
sudo=/usr/bin/sudo
apt-get=/usr/bin/apt-get
- Missing by default in the minimal fixture:
- Installing Python proved possible:
sudo apt-get update -qq && sudo apt-get install -y -qq python3, after which
python3 --version reported Python 3.12.3.
Open questions
- Should the canonical provider name be
github-codespaces or simply
codespaces? Recommendation: github-codespaces canonical, codespaces and
gh-codespaces aliases, because the provider is GitHub-specific and
codespaces alone is generic.
- Should Phase 1 create through direct REST or through
gh codespace create?
Recommendation: implement both behind one client if cheap, but use gh first
for parity with local auth and SSH behavior.
- Should release default to delete or stop? Recommendation: delete non-kept
Crabbox-created leases by default to avoid cost/retention surprises, but stop
kept leases.
- Can Crabbox safely parse and use generated
gh codespace ssh --config
without modifying the user's global ~/.ssh/config? Recommendation: store a
per-lease private config file and pass it explicitly to OpenSSH.
- Should the provider support
--fresh-pr by creating a codespace from a pull
request endpoint? Recommendation: defer until the base repo/ref flow is stable.
- Which exact GitHub machine type should map to Crabbox
standard? Recommendation:
do not hardcode global mapping until machine discovery is implemented against
the selected repository.
Implementation PR
Implemented by #347.
Add GitHub Codespaces as a Crabbox Linux provider
Summary
Add a first-party
github-codespacesprovider for Crabbox so users can acquireshort-lived Linux leases backed by GitHub Codespaces, run normal Crabbox
repo-sync and command workflows, then stop or delete the codespace safely.
The best Phase 1 shape is an SSH-lease provider that relies on GitHub's
supported Codespaces access path:
gh codespaceJSON commands forcreate/list/view/start/stop/delete;
gh codespace ssh --configor an equivalentgh-mediated OpenSSH configto expose the codespace as a normal OpenSSH target;
ssh, status rendering, localclaims, and release once a usable SSH target is available.
This is not a brokered coordinator provider in the first implementation.
Codespaces are owned by the authenticated GitHub user or billed organization,
and the direct CLI should reuse the operator's existing
ghauthentication or aGitHub token with Codespaces access.
Motivation
GitHub Codespaces is a strong missing Crabbox target:
idle timeout, and retention period, which matches Crabbox's short-lived
repo-workflow use case.
start/stop, repository defaults, permission checks, and machine type discovery.
gh codespace create,list --json,view --json,ssh --config,cp,ports,stop, anddelete.gh codespace ssh --configcangenerate OpenSSH configuration, letting Crabbox reuse its existing SSH/sync/run
machinery instead of inventing a new remote execution protocol.
The main risk is that Codespaces is not a raw VM API. The public REST object does
not hand back a normal host, port, username, and private key. Crabbox should
treat the GitHub CLI/OpenSSH integration as the supported connection layer and
fail clearly when the selected devcontainer image does not include an SSH server.
Recommended phase 1 scope
Implement
provider: github-codespacesas a Linux-only, direct-only SSH leaseprovider.
Provider spec:
Phase 1 should support:
crabbox warmup --provider github-codespacescrabbox run --provider github-codespaces -- <command>crabbox ssh --provider github-codespaces --id <lease-or-slug>crabbox list --provider github-codespaces --jsoncrabbox status --provider github-codespaces --id <lease-or-slug> --jsoncrabbox stop --provider github-codespaces <lease-or-slug>crabbox cleanup --provider github-codespaces --dry-runcrabbox doctor --provider github-codespacesPhase 1 should not attempt Windows, macOS, VNC/desktop/browser/code-server,
coordinator brokerage, GitHub organization admin listing, Codespaces secrets
management, prebuild management, port visibility management, or unpublished
codespace export/publish flows.
GitHub API and CLI fit
The local GitHub docs and live official docs show the lifecycle operations
Crabbox needs:
POST /repos/{owner}/{repo}/codespacescreates a repository codespace.POST /repos/{owner}/{repo}/pulls/{pull_number}/codespacescreates acodespace from a pull request.
GET /repos/{owner}/{repo}/codespaceslists repository codespaces for theauthenticated user.
GET /user/codespaceslists the authenticated user's codespaces.GET /user/codespaces/{codespace_name}gets one codespace.PATCH /user/codespaces/{codespace_name}updates machine/display metadata.POST /user/codespaces/{codespace_name}/startstarts a codespace.POST /user/codespaces/{codespace_name}/stopstops a codespace.DELETE /user/codespaces/{codespace_name}deletes a codespace.GET /repos/{owner}/{repo}/codespaces/newgets default attributes.GET /repos/{owner}/{repo}/codespaces/permissions_checkchecks whetherdevcontainer permissions have been accepted.
GET /repos/{owner}/{repo}/codespaces/machineslists repository machinetypes.
GET /user/codespaces/{codespace_name}/machineslists alternate machinetypes for an existing codespace.
Useful create request fields include:
refgeoor legacylocationmachinedevcontainer_pathmulti_repo_permissions_opt_outworking_directoryidle_timeout_minutesdisplay_nameretention_period_minutesUseful codespace response fields include:
namedisplay_nameenvironment_idstatemachinerepositorylocationidle_timeout_minutesweb_urlmachines_urlstart_urlstop_urlretention_period_minutesretention_expires_atGitHub CLI help adds the most important SSH details:
gh codespace ssh --configwrites OpenSSH config for codespaces.~/.ssh/config, ordinaryssh,scp,rsync, and relatedOpenSSH tools can target codespaces.
gh codespace sshautomatically manages a local SSH key if needed.adding the
ghcr.io/devcontainers/features/sshd:1devcontainer feature whenthe image does not include one.
That makes
gha practical first implementation dependency even if direct RESTis used for some control-plane calls.
Recommended implementation strategy
Use a hybrid
ghplus REST design, with all subprocess calls routed throughCrabbox
Runtime.Execand all HTTP calls routed throughRuntime.HTTP.The strongest Phase 1 path:
ghonPATH, unless explicit REST-only mode is later designed.gh auth statusor a lightweight authenticatedgh apicall indoctorto prove the user has GitHub API access without printing tokens.githubCodespaces.repo,--repo, or thecurrent git remote using Crabbox's existing GitHub repo parsing helpers.
where possible so missing Codespaces entitlement, missing repo access,
unaccepted devcontainer permissions, or unavailable machine types fail before
Crabbox creates a lease claim.
POST /repos/{owner}/{repo}/codespacesorgh codespace create, passing ref, machine, devcontainer path, working directory,idle timeout, retention, and display name.
gh codespace view --json ...orGET /user/codespaces/{name}untilthe state is usable.
gh codespace ssh --config -c <name>.0600,similar to providers that need provider-specific SSH/proxy state.
SSHTargetthat uses OpenSSH with that config. The exact mechanicsshould follow existing proxy-based providers such as Tenki, KubeVirt,
Sprites, or External rather than hardcoding a public host assumption.
default, for example
/workspaces/<repo-name>/crabbox, unless the userconfigured
githubCodespaces.workRoot.owned by the local claim.
Do not make the first implementation depend on an undocumented internal VS Code
or Codespaces protocol. If
gh codespace ssh --configcannot provide a stableOpenSSH target for Crabbox, downgrade the implementation plan to a delegated-run
provider that shells through
gh codespace ssh -c <name> -- <command>anddocuments the reduced sync/artifact surface.
Configuration
Suggested config:
Suggested flags:
Suggested environment overrides:
Do not accept GitHub tokens as command-line arguments. Keep tokens in
ghauth,environment variables, or the user's credential store.
--classcan map to a small set of GitHub machine types only after the providercan query available machine types for the selected repo. Until then, explicit
githubCodespaces.machine/--github-codespaces-machineis safer than guessingglobal names. If
--typeis already the generic exact machine-type flag inCrabbox, map it to Codespaces
machine.Lifecycle
Phase 1 lifecycle:
target=linux; Codespaces is a Linux devcontainer surface for thisprovider.
ghavailability and authenticated access.when the endpoints are available.
a human-readable way.
github-codespacesgh codespace ssh --config -c <codespace>.git,rsync,tar, and the configured work root are usable. Do notassume
python3, Node.js, or npm are present in a minimal Codespaces baseimage; install or validate them only when Crabbox core or a requested
workflow actually needs them.
there is no provider-side Crabbox tag equivalent.
deleteOnRelease.state and matching GitHub identity/repo. Without local claim proof, it may
report candidate names but should not delete them.
Ownership and cleanup safety
Codespaces does not expose cloud-style arbitrary resource tags equivalent to
DigitalOcean, Linode, or Hetzner. Use local claims as the primary ownership
source, plus display-name naming as a human hint only.
Suggested display name:
Suggested local claim identity fields:
Stop/delete should refuse when:
--reclaimwas not supplied;github-codespaces;forced delete, unless the user explicitly passes a force flag that Crabbox
wires through intentionally.
Cleanup should be conservative. Because Codespaces are personal development
environments, accidental deletion is more damaging than leaving a stale stopped
codespace. The first PR should prefer
cleanup --dry-runand claim-backeddelete only.
Devcontainer and SSH-server contract
Crabbox should document this provider's strongest prerequisite plainly:
gh codespace ssh.{ "features": { "ghcr.io/devcontainers/features/sshd:1": { "version": "latest" } } }The provider should detect SSH config/readiness failures and return a clear
error that tells the user to add SSHD to the devcontainer rather than presenting
it as a generic Crabbox sync failure.
Implementation outline
Likely files to add or modify:
internal/providers/githubcodespaces/provider.goConfigure,ConfigureDoctor, and server-type mapping.internal/providers/githubcodespaces/backend.gointernal/providers/githubcodespaces/client.gogh apior direct REST calls for create/list/view/start/stop/delete/machines/defaults/permissions.internal/providers/githubcodespaces/gh.gogh codespacesubprocess calls throughRuntime.Exec, includingssh --config.internal/providers/githubcodespaces/ssh_config.goSSHTarget.internal/providers/githubcodespaces/provider_test.gointernal/providers/githubcodespaces/client_test.gointernal/providers/githubcodespaces/gh_test.goghinvocation and argv redaction.internal/providers/githubcodespaces/backend_test.gointernal/providers/all/all.gointernal/providers/all/all_test.gointernal/cli/config.goGitHubCodespacesConfig, file config, defaults, env overrides, and config show support.internal/cli/config_test.godocs/providers/github-codespaces.mddocs/providers/provider-metadata.jsondocs/providers/README.mdnode scripts/generate-provider-matrix.mjs.docs/features/providers.mddocs/source-map.mdscripts/live-github-codespaces-smoke.shscripts/live-github-codespaces-smoke.test.jsDo not touch
run.go,repo.go,coordinator.go, or shared providerinterfaces unless implementation proves a provider-neutral extension is required.
The provider authoring guide explicitly treats such changes as a checkpoint.
Tests and docs
Implementation should include:
github-codespaces,codespaces, andgh-codespacesresolve as expected.behavior.
ghis invoked throughRuntime.Exec;gh codespace ssh --config -c <name>is called for SSH target generation;intended.
idle timeout, retention, and display name;
Retry-Afteris honored for rate/temporary failures where exposed;docs/providers/github-codespaces.md;docs/source-map.md;Suggested opt-in live smoke:
The smoke should:
bin/crabbox.gh auth status.bin/crabbox status --provider github-codespaces --id <lease> --wait.bin/crabbox run --provider github-codespaces --id <lease> --no-sync -- echo crabbox-codespaces-ok.Final classifications should include:
Acceptance criteria
crabbox providerslistsgithub-codespacesas a Linux SSH-lease provider.codespacesandgh-codespacesresolve if maintainers accept thealias surface.
crabbox doctor --provider github-codespacesperforms only non-mutatingchecks:
ghavailability, GitHub auth, repo access, Codespaces availability,machine/default discovery, and claim-backed inventory.
crabbox warmup --provider github-codespaces --github-codespaces-repo owner/repocreates or starts a codespace and returns a Crabbox lease id and slug.
crabbox run --provider github-codespaces -- echo okcan execute over thegenerated OpenSSH target when the devcontainer includes SSHD.
crabbox ssh --provider github-codespaces --id <lease-or-slug>connectsthrough the same provider-specific SSH route.
crabbox list/status --provider github-codespaces --jsonshow normalizedlease state without leaking tokens or generated SSH config secrets.
crabbox stop --provider github-codespaces <lease-or-slug>stops or deletesonly the claim-backed codespace according to config.
crabbox cleanup --provider github-codespaces --dry-runreports onlyclaim-backed Crabbox codespaces and does not mutate anything.
GH_TOKEN,GITHUB_TOKEN, authorization headers,generated SSH private key material, or full OpenSSH config contents.
prerequisite, lifecycle, cleanup safety, and Phase 1 limitations.
credentials.
classifications above.
Verification commands for the implementation PR
Optional live validation:
Research notes
Local GitHub docs inspected:
docs/content/rest/codespaces/codespaces.mddocs/content/rest/codespaces/machines.mddocs/src/rest/data/fpt-2022-11-28/codespaces.jsondocs/src/rest/data/ghec-2026-03-10/codespaces.jsondocs/content/codespaces/developing-in-a-codespace/using-github-codespaces-with-github-cli.mddocs/content/codespaces/reference/security-in-github-codespaces.mddocs/content/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers.mdCrabbox docs and source inspected:
docs/features/provider-authoring.mddocs/provider-backends.mddocs/providers/README.mddocs/providers/digitalocean.mddocs/providers/daytona.mddocs/providers/semaphore.mddocs/providers/tenki.mddocs/source-map.mdinternal/cli/provider_backend.gointernal/cli/config.gointernal/providers/all/all.gointernal/providers/digitaloceaninternal/providers/daytonainternal/providers/semaphoreinternal/providers/tenkiinternal/providers/spritesOfficial current references checked:
gh codespace sshmanual: https://cli.github.com/manual/gh_codespace_sshLive verification notes
Verified live against the
coygeekGitHub account on 2026-06-13 with ashort-lived GitHub token. Do not copy tokens into issues, PRs, logs, commits, or
command-line arguments.
Disposable fixture:
coygeek/crabbox-codespaces-smoke-june13.main.README.md.devcontainer/devcontainer.jsonmcr.microsoft.com/devcontainers/base:ubuntuand theghcr.io/devcontainers/features/sshd:1feature.the smoke without recreating the repo. The live codespace created during this
verification was deleted.
Token/API behavior proved:
GH_TOKEN=<redacted>authenticated ascoygeek.GET /user/codespaces?per_page=1returned HTTP 200 withX-Accepted-GitHub-Permissions: codespaces=read.gh codespace list --json ...worked and initially returned an empty list.GET /repos/coygeek/crabbox-codespaces-smoke-june13/codespaces/newworkedand returned
billable_owner=coygeek; most defaults werenullbefore anexplicit create request.
GET /repos/coygeek/crabbox-codespaces-smoke-june13/codespaces/machinesreturned four Linux machines:
basicLinux32gb- 2 cores, 8 GB RAM, 32 GB storagestandardLinux32gb- 4 cores, 16 GB RAM, 32 GB storagepremiumLinux- 8 cores, 32 GB RAM, 64 GB storagelargePremiumLinux- 16 cores, 64 GB RAM, 128 GB storageGET /repos/coygeek/crabbox-codespaces-smoke-june13/codespaces/permissions_check?ref=main&devcontainer_path=.devcontainer/devcontainer.jsonreturned
{ "accepted": true }.DELETE /user/codespaces/{name}returned HTTP 202 withX-Accepted-GitHub-Permissions: codespaces=write.Codespace lifecycle proved:
POST /repos/coygeek/crabbox-codespaces-smoke-june13/codespaces.ref=mainmachine=basicLinux32gbdevcontainer_path=.devcontainer/devcontainer.jsonidle_timeout_minutes=10retention_period_minutes=60display_name="crabbox smoke june13"crabbox-smoke-june13-<suffix>.Provisioning -> Available.gh codespace view --json ...returned:repositoryas the stringcoygeek/crabbox-codespaces-smoke-june13, notan object. Provider code should not assume
repository.nameWithOwnerforghJSON output.devcontainerPath=.devcontainer/devcontainer.jsonmachineName=basicLinux32gbidleTimeoutMinutes=10retentionPeriodDays=0for a 60-minute retention request.POST /user/codespaces/{name}/stopPOST /user/codespaces/{name}/startShuttingDown -> Shutdown -> Starting -> Available.gh codespace list -R coygeek/crabbox-codespaces-smoke-june13returned an empty list.
SSH and sync behavior proved:
gh codespace ssh --config -c <name>generated a 0600 OpenSSH config with:cs.<codespace-name>.<branch>User vscodeProxyCommandthat calls GitHub CLI's Codespaces SSH stdio pathIdentityFile ~/.ssh/codespaces.autossh -F /tmp/crabbox-codespaces-smoke-ssh-config <generated-host-alias> 'echo openssh-ok'.rsync -e "ssh -F /tmp/crabbox-codespaces-smoke-ssh-config"to/tmpworkedand the transferred proof file was readable remotely.
gh codespace ssh -c <name> -- <command>failed in this environmentwith:
vscode@localhost: Permission denied (publickey,password).shell closed: exit status 255plain
ssh -F <config>/rsync -e "ssh -F <config>"path for command andsync execution. Do not assume direct
gh codespace ssh -- <command>isequivalent.
Remote environment observed:
vscode./home/vscode./workspaces/crabbox-codespaces-smoke-june13.sudo -n true.git=/usr/local/bin/gitrsync=/usr/bin/rsynctar=/usr/bin/tarsudo=/usr/bin/sudoapt-get=/usr/bin/apt-getpython3nodenpmsudo apt-get update -qq && sudo apt-get install -y -qq python3, after whichpython3 --versionreported Python 3.12.3.Open questions
github-codespacesor simplycodespaces? Recommendation:github-codespacescanonical,codespacesandgh-codespacesaliases, because the provider is GitHub-specific andcodespacesalone is generic.gh codespace create?Recommendation: implement both behind one client if cheap, but use
ghfirstfor parity with local auth and SSH behavior.
Crabbox-created leases by default to avoid cost/retention surprises, but stop
kept leases.
gh codespace ssh --configwithout modifying the user's global
~/.ssh/config? Recommendation: store aper-lease private config file and pass it explicitly to OpenSSH.
--fresh-prby creating a codespace from a pullrequest endpoint? Recommendation: defer until the base repo/ref flow is stable.
standard? Recommendation:do not hardcode global mapping until machine discovery is implemented against
the selected repository.
Implementation PR
Implemented by #347.