Command-line interface for the Cloud Console. A single static binary for managing Xcloud instances, data volumes, images, networks, security groups and elastic IPs — from a terminal or from CI.
This repository is a read-only mirror, published from the Cloud Console monorepo on every release. Issues and feature requests are welcome here; pull requests are not, since changes here are overwritten by the next mirror — see CONTRIBUTING.md.
Homebrew
brew install studio-ch/tap/cloudconsoleShell installer (Linux and macOS, verifies the checksum)
curl -fsSL https://raw.githubusercontent.com/studio-ch/cloudconsole-cli/main/install.sh | shPin a version, or choose where it lands:
CLOUDCONSOLE_VERSION=v0.1.0 CLOUDCONSOLE_INSTALL_DIR="$HOME/.local/bin" \
sh -c "$(curl -fsSL https://raw.githubusercontent.com/studio-ch/cloudconsole-cli/main/install.sh)"Go
go install github.com/studio-ch/cloudconsole-cli/cmd/cloudconsole@latestManual — download an archive from
Releases, verify it
against SHA256SUMS, and put cloudconsole on your PATH. Windows builds are
published as .zip.
cloudconsole versionIssue an API key in the panel under Settings → API keys. The secret is shown once, at creation. Choose the Read + Write preset if you intend to change anything — a read-only key is rejected on mutations.
cloudconsole auth login # prompts, without echoing
cloudconsole auth status # organisation, key, scopes, expiryIn CI, set the token in the environment instead:
export CLOUDCONSOLE_API_TOKEN=sk_live_…A key belongs to exactly one organisation. For several, use one profile
per key (cloudconsole auth login --profile acme, then --profile acme or
cloudconsole config use acme).
cloudconsole region list
cloudconsole instance list
cloudconsole instance list -o json | jq -r '.[].name'
cloudconsole instance create \
--name build-01 --region ZRH1 \
--image ghcr.io/example/macos-sequoia:latest \
--cpu 10 --memory 28 --disk 480 --wait
cloudconsole instance suspend <id> --wait # suspend to disk
cloudconsole instance boot-mode <id> --recovery --wait
cloudconsole instance delete <id> --yes --wait
cloudconsole volume create --name scratch --region ZRH1 --size 500
cloudconsole volume attach <volume-id> --instance <instance-id>Most instance operations are asynchronous. --wait blocks until the
instance reaches its new state — use it whenever you script two operations
in a row, because a second action while one is pending is rejected.
cloudconsole --help lists everything; cloudconsole <command> --help goes deeper.
Pass a complete first-boot document with --user-data-file and
--user-data-format. For Fedora CoreOS, use Ignition JSON (convert Butane YAML
first):
cloudconsole instance create \
--name coreos-01 --region ALP2 --platform linux \
--image coreos --cpu 2 --memory 4 --disk 40 \
--admin-username core \
--user-data-file ./config.ign --user-data-format ignition --waitChoose an image and sizing available in your region. For cloud-init images, use
--user-data-file ./cloud-config.yaml --user-data-format cloud-init; the YAML
must start with #cloud-config. Use --user-data-file - to read stdin.
The document replaces automatic setup. Define users and SSH keys inside it and
omit --ssh-key. --admin-username records the user for connection information;
it does not create a user when you provide custom data. Documents are limited to
64 KiB of UTF-8, encrypted by the API, and never returned by instance reads. They
run only on first boot; changing a local file does not reconfigure an existing VM.
--output json emits the API's response body unchanged, with only the
list envelope unwrapped. Field names and types are exactly what the API
returns, so jq recipes transfer between curl and cloudconsole. Table output
carries no such promise — do not parse it.
Progress and warnings go to stderr, so this is safe:
id=$(cloudconsole instance create … --wait -o json | jq -r .id)Exit codes are stable; cloudconsole exit-codes prints the full table. The ones
worth branching on:
| Code | Meaning |
|---|---|
| 0 | success |
| 3 | authentication failed |
| 4 | permission denied (read-only key, or a disabled service) |
| 5 | not found |
| 6 | the resource is busy with another operation |
| 7 | quota exceeded |
| 12 | --wait timed out (the operation may still finish) |
- name: Provision a build VM
env:
CLOUDCONSOLE_API_TOKEN: ${{ secrets.CLOUDCONSOLE_API_TOKEN }}
run: |
curl -fsSL https://raw.githubusercontent.com/studio-ch/cloudconsole-cli/main/install.sh | sh
id=$(cloudconsole instance create \
--name "ci-${GITHUB_RUN_ID}" --region ZRH1 \
--image ghcr.io/example/macos-sequoia:latest \
--cpu 10 --memory 28 --disk 480 \
--wait -o json | jq -r .id)
echo "INSTANCE_ID=$id" >> "$GITHUB_ENV"
- name: Tear down
if: always()
env:
CLOUDCONSOLE_API_TOKEN: ${{ secrets.CLOUDCONSOLE_API_TOKEN }}
run: cloudconsole instance delete "$INSTANCE_ID" --yes --wait--yes is required for destructive commands without a terminal — the CLI
refuses rather than guessing.
~/.config/cloudconsole/config.yaml, created with 0600 permissions because it
holds a token; the CLI refuses to read it if the permissions are looser.
Settings resolve per field: flag, then environment
(CLOUDCONSOLE_API_URL, CLOUDCONSOLE_API_TOKEN, CLOUDCONSOLE_OUTPUT, CLOUDCONSOLE_PROFILE),
then the profile, then the default. So a token from the environment
combines with a URL from the profile — the usual CI arrangement.
cloudconsole config explain shows what was resolved and from where.
The default endpoint is https://api.cloud.flow.swiss. Point the CLI at a
different deployment with --api-url, CLOUDCONSOLE_API_URL, or an
api_url in the profile — one profile per environment is the usual shape.
To keep the secret out of the file, use a command instead:
profiles:
prod:
api_url: https://api.cloud.flow.swiss
token_command: op read op://Private/cloud/api-key--debug traces every request to stderr with the credential redacted to
its public prefix. Every error carries a request-id — quote it to
support.
- REST API — the CLI is a client of the documented public API; anything
it does can be done with
curl. - MCP server — connect an AI client to your account with the same key.
- Packer plugin — studio-ch/packer-plugin-cloudconsole bakes custom macOS images.
The CLI authenticates with an API key, and some parts of the panel are not reachable that way: the VM console and terminal, the AI assistant, the admin area, and issuing or revoking API keys (listing works; issuing is a panel operation).