Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
a7e73f1
feat: add GitHub Codespaces provider contract
coygeek Jun 14, 2026
3c4a3ee
feat(github-codespaces): implement SSH lease lifecycle
coygeek Jun 14, 2026
ec561c6
docs(github-codespaces): add docs and live smoke
coygeek Jun 14, 2026
f94f897
fix(github-codespaces): harden lifecycle defaults
coygeek Jun 14, 2026
86996cd
fix(github-codespaces): scope live smoke cleanup check
coygeek Jun 14, 2026
260499a
fix(github-codespaces): propagate runtime SSH config
coygeek Jun 14, 2026
52ca809
fix(github-codespaces): cap display names
coygeek Jun 14, 2026
625f11c
fix(github-codespaces): retain dirty leases on release
coygeek Jun 14, 2026
1045336
fix(github-codespaces): retain fallback claims
coygeek Jun 14, 2026
7f86408
fix(github-codespaces): accept start no-op
coygeek Jun 14, 2026
188147e
fix(github-codespaces): honor type aliases
coygeek Jun 14, 2026
b1e90d6
fix(github-codespaces): accept delete no-op
coygeek Jun 14, 2026
36318f7
fix(github-codespaces): probe status waits
coygeek Jun 14, 2026
7cf06db
fix(github-codespaces): preserve delete release policy
coygeek Jun 14, 2026
9e3e0ef
fix(github-codespaces): block repo config redirects
coygeek Jun 14, 2026
dc792ee
fix(github-codespaces): remove unused helper wrappers
vincentkoc Jun 24, 2026
539144b
test(github-codespaces): wire shared live smoke
vincentkoc Jun 24, 2026
b9eaa73
fix(github-codespaces): preflight codespace auth scope
vincentkoc Jun 24, 2026
c36edc0
docs: add Codespaces provider changelog
steipete Jul 2, 2026
8263b2d
test: cover Codespaces provider defaults
steipete Jul 2, 2026
4228cc3
test: harden Codespaces live cleanup
steipete Jul 2, 2026
883f865
fix: protect Codespaces release policy
steipete Jul 2, 2026
306c1e3
docs: refresh provider matrix count
vincentkoc Jul 3, 2026
f8fb09a
fix(github-codespaces): retain failed rollback ownership
steipete Jul 5, 2026
5f5c202
refactor(github-codespaces): remove obsolete claim helper
steipete Jul 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## 0.35.1 - Unreleased

### Added

- Added GitHub Codespaces direct Linux SSH leases with token-scope preflight, repository and machine selection, claim-bound ownership, generated OpenSSH configuration, and guarded lifecycle smoke coverage. Thanks @coygeek.

### Changed

- Renamed the `apple-vz` provider to `apple-vm`; the old provider name/aliases, `appleVZ:` config keys, `--apple-vz-*` flags, and `CRABBOX_APPLE_VZ_*` environment variables keep working as deprecated aliases, existing leases and claims stay manageable, and the state directory migrates automatically.
Expand Down Expand Up @@ -77,6 +81,7 @@
- Required exact resource-bound local lease claims before Apple Container, local-container, or Apple VZ stop operations can delete provider resources; legacy unbound claims require explicit `--reclaim` adoption before stop. Thanks @coygeek.
- Hardened Azure Windows snapshot forks to fail closed through credential rehydration and quarantine cleanup, reuse only writable NIC payloads, reject unknown differential disks, and retry in-use security-group cleanup. Thanks @fcoury-oai.
- Rolled back brokered Hetzner servers when post-create readiness fails, deleting only lease-owned SSH keys created by the failed attempt after server cleanup succeeds while preserving explicit no-delete retention until a later delete. Thanks @coygeek.
- Prevented repository-local configuration from retaining billable GitHub Codespaces or overriding trusted deletion policy. Thanks @coygeek.

## 0.34.0 - 2026-07-02

Expand Down
8 changes: 8 additions & 0 deletions docs/operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ CRABBOX_LIVE=1 CRABBOX_LIVE_PROVIDERS=nvidia-brev scripts/live-smoke.sh
CRABBOX_LIVE=1 CRABBOX_LIVE_PROVIDERS=phala CRABBOX_LIVE_COORDINATOR=0 CRABBOX_BIN=./bin/crabbox scripts/live-smoke.sh
CRABBOX_LIVE=1 CRABBOX_LIVE_PROVIDERS=anthropic-sandbox-runtime scripts/live-smoke.sh
CRABBOX_LIVE=1 CRABBOX_LIVE_PROVIDERS=opensandbox CRABBOX_LIVE_COORDINATOR=0 scripts/live-smoke.sh
CRABBOX_LIVE=1 CRABBOX_LIVE_PROVIDERS=github-codespaces CRABBOX_LIVE_COORDINATOR=0 CRABBOX_GITHUB_CODESPACES_SMOKE_REPO=example-org/my-app GH_TOKEN=... scripts/live-smoke.sh
CRABBOX_LIVE=1 CRABBOX_LIVE_PROVIDERS=proxmox CRABBOX_LIVE_COORDINATOR=0 CRABBOX_BIN=./bin/crabbox scripts/live-smoke.sh
CRABBOX_LIVE=1 CRABBOX_LIVE_PROVIDERS=xcp-ng CRABBOX_LIVE_COORDINATOR=0 CRABBOX_BIN=./bin/crabbox scripts/live-smoke.sh
CRABBOX_LIVE=1 CRABBOX_LIVE_PROVIDERS=agent-sandbox CRABBOX_LIVE_COORDINATOR=0 scripts/live-smoke.sh
Expand Down Expand Up @@ -222,6 +223,13 @@ Per-provider smoke prerequisites:
`scripts/live-opensandbox-smoke.sh` is coordinator-free, proves archive sync,
off-argv environment forwarding, retained sandbox reuse, staged `sync.delete`
replacement, list/status, and cleanup.
- **GitHub Codespaces** — an authenticated `gh` CLI, an explicit
`CRABBOX_GITHUB_CODESPACES_SMOKE_REPO`, and either `GH_TOKEN`,
`GITHUB_TOKEN`, or `CRABBOX_GITHUB_CODESPACES_USE_GH_AUTH=1`.
`scripts/live-smoke.sh` delegates to
`scripts/live-github-codespaces-smoke.sh`, which is coordinator-free,
creates one short-lived Codespace lease, verifies status, sync/run, SSH
command generation, list, stop, and dry-run cleanup.
- **Proxmox** — a locally built Crabbox binary (`CRABBOX_BIN`), Proxmox API
credentials/config, and `jq`/`perl`. `scripts/proxmox-live-smoke.sh` is
coordinator-free and read-only by default; set `CRABBOX_PROXMOX_LIVE_SMOKE=1`
Expand Down
3 changes: 2 additions & 1 deletion docs/providers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ selection metadata. Regenerate it with `node scripts/generate-provider-matrix.mj
`scripts/check-docs.sh` fails when provider registration, metadata, docs paths, or
this generated table drift.

Current built-in surface: 71 providers (41 SSH lease, 28 delegated run, 2 service control).
Current built-in surface: 72 providers (42 SSH lease, 28 delegated run, 2 service control).

Access terms:

Expand Down Expand Up @@ -99,6 +99,7 @@ Access terms:
| [firecracker](firecracker.md) | built-in; `ssh-lease` · self-hosted-virtualization | Crabbox-managed SSH; `crabbox-sync` · direct only; features: `ssh`, `crabbox-sync`, `cleanup` | `linux`; Firecracker microVM | `self-hosted`; GPU: no | Crabbox direct lifecycle; microVM and local artifact cleanup | Self-hosted Linux KVM host with prepared Firecracker kernel, rootfs, and CNI | Requires Linux, /dev/kvm, Firecracker assets, and a working CNI setup on the host |
| [freestyle](freestyle.md) | built-in; `delegated-run` · delegated-sandbox | No SSH; `archive-sync` · direct only; features: `archive-sync`, `run-session` | `linux`; Freestyle VM | `provider-managed`; GPU: unknown | Freestyle; provider VM cleanup | Hosted delegated Linux VM execution | No Crabbox-managed SSH path |
| [gcp](gcp.md) (`google`, `google-cloud`) | built-in; `ssh-lease` · brokerable-cloud | Crabbox-managed SSH; `crabbox-sync` · coordinator optional; features: `ssh`, `crabbox-sync`, `cleanup`, `tailscale` | `linux`; Google Compute Engine VM | `cloud`; GPU: optional | Crabbox or coordinator; instance and firewall cleanup | Linux compute with broad machine selection | Project, IAM, quota, and firewall setup required |
| [github-codespaces](github-codespaces.md) (`codespaces`, `gh-codespaces`) | built-in; `ssh-lease` · direct-cloud | Crabbox-managed SSH; `crabbox-sync` · direct only; features: `ssh`, `crabbox-sync`, `cleanup` | `linux`; GitHub Codespace | `provider-managed`; GPU: no | GitHub Codespaces; delete or stop claim-owned Codespace | Repository-backed Linux devcontainer over SSH | Requires gh auth, Codespaces quota, and an SSH-enabled devcontainer |
| [hetzner](hetzner.md) | built-in; `ssh-lease` · brokerable-cloud | Crabbox-managed SSH; `crabbox-sync` · coordinator optional; features: `ssh`, `crabbox-sync`, `cleanup`, `desktop`, `browser`, `code`, `tailscale` | `linux`; Hetzner Cloud server | `cloud`; GPU: no | Crabbox or coordinator; server delete | Cost-effective high-CPU Linux VM | Linux-only and capacity varies by location |
| [hostinger](hostinger.md) | built-in; `ssh-lease` · direct-cloud | Crabbox-managed SSH; `crabbox-sync` · direct only; features: `ssh`, `crabbox-sync`, `cleanup` | `linux`; Hostinger VPS | `cloud`; GPU: no | Hostinger subscription; stop only | Direct Linux VPS with persistent subscription | Purchase needs opt-in and release does not cancel billing |
| [hyperv](hyperv.md) | built-in; `ssh-lease` · local-vm | Crabbox-managed SSH; `crabbox-sync` · direct only; features: `ssh`, `crabbox-sync`, `cleanup` | `windows/normal`; Microsoft Hyper-V VM | `local`; GPU: no | Crabbox; VM delete | Local native Windows VM | Windows host with Hyper-V required |
Expand Down
273 changes: 273 additions & 0 deletions docs/providers/github-codespaces.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
# GitHub Codespaces Provider

Read this when you are:

- choosing `provider: github-codespaces`;
- validating a direct GitHub Codespaces SSH lease;
- changing `internal/providers/githubcodespaces` or the guarded live smoke.

GitHub Codespaces is a Linux-only **SSH lease** provider. Crabbox creates a
Codespace from a GitHub repository, asks `gh codespace ssh --config` for the
OpenSSH connection details, stores that generated SSH config in Crabbox state,
and then uses the normal Crabbox SSH sync, `run`, `ssh`, `status`, and
`stop` paths.

The provider is **direct-only** in this release. It never routes through the
coordinator, so the local CLI must have GitHub CLI authentication and the
operator owns quota, billing, retention, and cleanup.

## When To Use It

Use GitHub Codespaces when the desired execution surface is a repository-backed
Codespace and the project already has an SSH-enabled Linux devcontainer. Prefer
AWS, Azure, GCP, Hetzner, Linode, or DigitalOcean when you need a plain VM,
coordinator-side credentials, broad OS support, or cloud-specific cost controls.

## Commands

```sh
crabbox doctor --provider github-codespaces --github-codespaces-repo example-org/my-app
crabbox warmup --provider github-codespaces --github-codespaces-repo example-org/my-app --type basicLinux32gb
crabbox run --provider github-codespaces --id my-app -- pnpm test
crabbox ssh --provider github-codespaces --id my-app
crabbox stop --provider github-codespaces my-app
crabbox cleanup --provider github-codespaces --dry-run
```

Aliases: `codespaces`, `gh-codespaces`.

`--id` accepts the canonical lease id (`cbx_...`), the friendly slug, or the
GitHub Codespace name when a matching local Crabbox claim exists. Crabbox
refuses to manage an unclaimed Codespace by name.

## Requirements

- Install the GitHub CLI as `gh`, or point Crabbox at it with
`githubCodespaces.ghPath`, `CRABBOX_GITHUB_CODESPACES_GH_PATH`, or
`--github-codespaces-gh-path`.
- Authenticate `gh` with an account that can create Codespaces for the selected
repository:

```sh
gh auth login
gh auth status
```

- Ensure `GH_TOKEN`, `GITHUB_TOKEN`, or the `gh` credential store has a token
with access to Codespaces and the selected repository. For local `gh` auth,
refresh the missing OAuth scope before live smoke:
```sh
gh auth refresh -h github.com -s codespace
gh codespace list --limit 1
```
- Configure the repository with an SSH-enabled Linux devcontainer. The image
must run an SSH server and include Git, `rsync`, and `tar`. A common
devcontainer feature is `ghcr.io/devcontainers/features/sshd:1`.
- Keep local OpenSSH and `rsync` available for Crabbox's data plane.

The provider asks GitHub for an OpenSSH config rather than shelling through
`gh codespace ssh -- <command>`. That keeps the normal Crabbox sync/run/ssh
behavior intact, including `rsync -e "ssh -F ..."`.

## Configuration

Use the full example in trusted user config or an explicitly selected
`CRABBOX_CONFIG` file. Repository-local config cannot change the repository,
GitHub API or CLI routing, or release deletion policy.

```yaml
provider: github-codespaces
target: linux
githubCodespaces:
repo: example-org/my-app
ref: main
machine: basicLinux32gb
devcontainerPath: .devcontainer/devcontainer.json
workingDirectory: /workspaces/my-app
geo: ""
idleTimeout: 30m
retentionPeriod: 168h
deleteOnRelease: true
ghPath: gh
workRoot: /workspaces/my-app
```

Config keys under `githubCodespaces:`:

| Key | Default | Notes |
| --- | --- | --- |
| `apiUrl` | `https://api.github.com` | Trusted config only; useful for GitHub Enterprise-style API routing when supported by the environment. |
| `ghPath` | `gh` | Trusted config only; local GitHub CLI executable. |
| `repo` | inferred from the GitHub remote when possible | Repository in `owner/name` form. Trusted config, environment, or CLI flag only; repo-local config cannot redirect Codespaces creation. Required when no GitHub remote can be inferred. |
| `ref` | empty | Git ref for new Codespaces. Empty uses GitHub's default behavior. |
| `machine` | `basicLinux32gb` | GitHub Codespaces machine slug. `--type` is an alias for this value. |
| `devcontainerPath` | empty | Optional devcontainer path for creation. |
| `workingDirectory` | empty | Optional Codespaces working directory setting. |
| `geo` | empty | Optional GitHub geographic location preference. |
| `idleTimeout` | `30m` | Codespaces idle timeout sent to GitHub on create. |
| `retentionPeriod` | `168h` | Codespaces retention period sent to GitHub on create. |
| `deleteOnRelease` | `true` | Trusted config, environment, or CLI only; repository-local config is ignored. Delete on `stop` unless a retained lease claim says release by stopping. |
| `workRoot` | `/workspaces/<repo>` when repo is known | Remote path Crabbox syncs to and runs from. |

Provider flags:

```text
--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-path
--github-codespaces-work-root
```

Environment overrides:

```text
CRABBOX_GITHUB_CODESPACES_API_URL
CRABBOX_GITHUB_CODESPACES_GH_PATH
CRABBOX_GITHUB_CODESPACES_REPO
CRABBOX_GITHUB_CODESPACES_REF
CRABBOX_GITHUB_CODESPACES_MACHINE
CRABBOX_GITHUB_CODESPACES_DEVCONTAINER_PATH
CRABBOX_GITHUB_CODESPACES_WORKING_DIRECTORY
CRABBOX_GITHUB_CODESPACES_GEO
CRABBOX_GITHUB_CODESPACES_IDLE_TIMEOUT
CRABBOX_GITHUB_CODESPACES_RETENTION_PERIOD
CRABBOX_GITHUB_CODESPACES_DELETE_ON_RELEASE
CRABBOX_GITHUB_CODESPACES_WORK_ROOT
```

Do not put GitHub tokens in Crabbox config or on command lines. Use
`GH_TOKEN`, `GITHUB_TOKEN`, or the GitHub CLI credential store.

## Lifecycle

1. Read GitHub CLI auth state and login identity.
2. Resolve the repository from `githubCodespaces.repo`, flags, env, or the
current GitHub remote.
3. Check available Codespaces machines for the repo/ref.
4. Create a Codespace with the configured machine, ref, devcontainer path,
working directory, geo, idle timeout, retention period, and Crabbox display
name.
5. Store a local Crabbox claim that binds the lease id, slug, Codespace name,
repository, machine, and GitHub login.
6. Wait for the Codespace to become available.
7. Ask `gh codespace ssh --config -c <codespace>` for OpenSSH config, store it
under Crabbox state, select the matching target, and wait for SSH readiness.
8. Use normal Crabbox SSH and rsync behavior for `run`, `sync`, and `ssh`.
9. On `stop`, delete or stop the claim-owned Codespace according to the release
policy.

If a retained Codespace is stopped, resolving it later starts it and waits for
availability before refreshing the generated SSH config.

## Ownership And Cleanup

GitHub Codespaces does not expose custom user labels. Crabbox therefore uses a
local claim as the ownership predicate. Release and cleanup require the claim to
match the provider, Codespace name, and creating GitHub login.

Deletion is conservative:

- Crabbox refuses to release a Codespace without a local claim.
- Crabbox refuses to delete when GitHub reports uncommitted or unpushed changes.
- Cleanup mutates only expired claim-owned Codespaces.
- Account switches are rejected when the current `gh` login differs from the
claim login.

Use dry-run cleanup before mutation:

```sh
crabbox list --provider github-codespaces --json
crabbox cleanup --provider github-codespaces --dry-run
crabbox cleanup --provider github-codespaces
```

## SSHD And Devcontainer Contract

`gh codespace ssh --config` requires an SSH server inside the Codespace. A plain
devcontainer image that does not start `sshd` is not enough for Crabbox because
Crabbox needs direct OpenSSH and rsync access.

For a devcontainer-based smoke, include an SSH feature such as:

```json
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/sshd:1": {}
}
}
```

The ready path also expects Git, `rsync`, `tar`, and a writable work root.

## Guarded Live Smoke

The repeatable live check is opt-in and local-only:

```sh
CRABBOX_LIVE=1 \
CRABBOX_LIVE_PROVIDERS=github-codespaces \
CRABBOX_GITHUB_CODESPACES_SMOKE_REPO=example-org/my-app \
GH_TOKEN=... \
scripts/live-smoke.sh
```

The script defaults to a skipped classification and does not call Crabbox unless
`CRABBOX_LIVE=1`, the provider filter selects `github-codespaces`, a smoke repo
is supplied, and GitHub credentials are explicitly available. It runs a read-only
doctor first, creates a short-lived Codespace lease, runs a command through the
normal synced Crabbox path, prints the Crabbox SSH command, releases the lease,
runs dry-run cleanup, and verifies the claim-owned inventory is empty.
`scripts/live-smoke.sh` delegates this provider to the standalone
`scripts/live-github-codespaces-smoke.sh`, so the standalone script can also be
run directly when isolating Codespaces smoke failures.

Final classifications include:

```text
classification=live_github_codespaces_smoke_passed
classification=environment_blocked
classification=credential_bound
classification=quota_blocked
classification=validation_failed
classification=cleanup_failed
```

If credentials, entitlement, quota, or local `gh` auth are unavailable, report
the classification instead of treating the live smoke as a provider failure.

## Capabilities

- **OS target**: Linux only.
- **SSH**: yes, from `gh codespace ssh --config`.
- **Crabbox sync**: yes, through normal OpenSSH/rsync.
- **Coordinator**: never; direct CLI only.
- **Desktop / browser / code**: not advertised in this release.
- **Tailscale**: not advertised; GitHub's SSH path is used.
- **Cleanup**: yes, claim-owned only.

## Gotchas

- `--class` is not supported. Use `--type <machine>` or
`--github-codespaces-machine <machine>`.
- `provider=github-codespaces` supports `target=linux` only.
- A Codespace without an SSH server fails during SSH config or readiness.
- Manual Codespaces are intentionally invisible to Crabbox unless a local
Crabbox claim exists.
- `deleteOnRelease: true` still refuses deletion when GitHub reports uncommitted
or unpushed work.

## Related Docs

- [Provider reference](README.md)
- [Provider backends](../provider-backends.md)
- [Provider feature overview](../features/providers.md)
- [providers command](../commands/providers.md)
- [ssh command](../commands/ssh.md)
14 changes: 14 additions & 0 deletions docs/providers/provider-metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,20 @@
"caveat": "Project, IAM, quota, and firewall setup required",
"docs": "gcp.md"
},
"github-codespaces": {
"status": "built-in",
"category": "direct-cloud",
"substrate": "GitHub Codespace",
"location": "provider-managed",
"ssh": "crabbox-managed",
"sync": "crabbox-sync",
"gpu": "no",
"lifecycle": "GitHub Codespaces",
"cleanup": "delete or stop claim-owned Codespace",
"bestFit": "Repository-backed Linux devcontainer over SSH",
"caveat": "Requires gh auth, Codespaces quota, and an SSH-enabled devcontainer",
"docs": "github-codespaces.md"
},
"hetzner": {
"status": "built-in",
"category": "brokerable-cloud",
Expand Down
Loading