# Python client and CLI

> Use RealHW from a terminal, Python script, or LLM agent.

Source: https://docs.realhw.dev/guides/cli/
Documentation index: https://docs.realhw.dev/llms.txt

The `realhw-cli` repository provides a Python REST client and the Click-based `realhw`
command. For the pinned checkout installation, browser login, and read-only verification,
start with [Set up with your agent](https://docs.realhw.dev/get-started/agent-setup/).

The client is in early access and is **not published on PyPI**. Repository access is
required. Supported operations include discovery, runner listing, and interactive
console reservation. CLI 0.3.0 adds [preview image workloads](https://docs.realhw.dev/guides/run-image-workload/),
including logs and artifacts, when the server advertises `workload.submit`.

## Authenticate

For automation, inject `REALHW_API_TOKEN` from your secret manager. Create the token in
**Settings → API keys** with the permissions and project restriction you need.
`REALHW_API_KEY` is also accepted. Do not include tokens in command arguments or prompts.

For an interactive terminal:

```sh
realhw auth login
realhw consoles list
```

Login opens RealHW in your browser. Sign in, review the requested permissions, confirm
that the displayed code matches your terminal, then authorize the CLI. Never approve a
code sent by someone else. For SSH or a terminal without a browser, use
`realhw auth login --no-browser` and open the printed URL on another device.

This is the OAuth device authorization flow (RFC 8628), using the public client ID
`realhw-cli`. The default permissions are `runners:read` and `consoles:read`. Access lasts
24 hours; log in again after expiry. No refresh token is issued. The credential appears
in **Settings → API keys**, where you can revoke it.

```sh
realhw auth login --scope consoles:read --scope consoles:write --project-id <project-uuid>
realhw auth status
realhw auth logout
```

Logout forgets the local credential; it does not revoke remote access or unset environment
tokens. Status shows local metadata without exposing the token. Saved credentials use
private files (0600) in `~/.config/realhw`, separated by API origin. This storage requires
macOS/Linux file permissions; other platforms can use environment tokens.

## Reserve a console

```sh
realhw runners list --account-id <account-uuid>
realhw consoles reserve --account-id <account-uuid> --project-id <project-uuid> \
  --label arch:x86_64 --label kvm --image ubuntu:24.04 --ttl 3600
realhw consoles list
realhw consoles close <session-uuid> --reason 'Finished debugging'
```

Use current labels returned by `hardware list --json`. Reservation may allocate billable hardware.
This creates an interactive shell reservation, not a one-shot image job. Console
WebSocket streaming remains available through the web app.

## Scripts and Python

Commands return JSON on stdout and errors on stderr, with nonzero exit codes on failure.
Use `--compact` before the subcommand for single-line JSON. Login instructions go to
stderr. There are no hidden confirmation prompts.

```python
import os
from realhw_cli import RealHWClient

with RealHWClient(token=os.environ["REALHW_API_TOKEN"]) as client:
    sessions = client.consoles()
```

`REALHW_API_URL` overrides the default `https://realhw.dev`. Existing ingress/IP access
restrictions still apply; tokens do not bypass them. HTTPS is required except for
loopback development. Redirects are disabled and mutations are never automatically
retried. After a timeout, inspect state before repeating a reservation.

Browser login requires the matching server OAuth endpoints and `/device` page. An older
server returns 404; use an API token until that server is upgraded.

## JSON output contract v1

The CLI uses Click. `--json` is accepted before, between, or after subcommands:

```sh
realhw --json capabilities
realhw hardware list --json
realhw consoles --json list
```

Successful commands write exactly one JSON value followed by a newline to stdout.
`--json` emits compact single-line JSON; `--compact` is an alias. Without either flag,
output is still JSON, formatted for people. REST responses pass through without a
CLI envelope, renamed fields, or type conversions. Unknown server fields are retained;
empty HTTP responses become `null`. Help and version are plain text.

Errors write `{"error":{"code":"…","message":"…","status":null}}` to stderr and leave
stdout empty. `status` is an HTTP status integer when applicable. Parse `code` and
`status`; messages are for people and may change. Login instructions also use stderr.
Local authentication commands return credential metadata, never the token.

Exit codes: 0 success; 1 API/configuration/protocol error; 2 usage error;
3 authentication/permission error; 4 network/timeout; 130 interrupted.
Consumers must ignore unknown fields. Discovery responses carry `schema_version: 1`;
additive fields are compatible, while breaking changes require a new contract version.
The CLI preserves each API endpoint's response schema rather than imposing its own.

## Current hardware and supported operations

```sh
realhw capabilities --json
realhw hardware list --json
```

`GET /api/v1/capabilities` is public and enumerates supported token-authenticated
operations, methods, paths, permissions, CLI equivalents, and mutation request schemas.
The console connection uses WebSocket and has no CLI streaming command yet.

`GET /api/v1/fleet/hardware` requires `runners:read`. It returns:

```json
{"schema_version":1,"observed_at":"2026-09-22T12:00:00Z","host_freshness_seconds":120,"labels":["arch:x86_64","kvm"]}
```

`labels` remains the sorted, deduplicated union from online, non-decommissioned
hosts seen within 120 seconds. A reported `kvm` label requires the host's explicit
`/dev/kvm` report. Busy or draining hosts can still report capabilities.

The additive `provisionable` array lists enabled cloud shapes even when no host is
running. Each entry has `provider`, `pool`, `shape`, sorted `labels`, `cpu_count`,
`memory_bytes` (a decimal string), and `boot_disk_size_gb`. Resource fields can be
null when unspecified. These are configured shape contracts, not measured hosts;
boot disk size is not a guarantee of free workspace disk.

For example, an enabled GCP N2 KVM pool advertises `arch:x86_64` and `kvm`, while
ARM64 cloud pools advertise `arch:aarch64`. Disabled/unpriced shapes and providers
excluded by platform policy are omitted. The server reads the scheduler's catalog
and rate enablement; the CLI does not maintain its own machine list.

`availability_checked_at` records the provider's last availability publication.
`quota_fits` is null if there is no report or it is older than five minutes;
false indicates the latest fresh quota check failed. `cooldown_until` is non-null
while a capacity retry cooldown is active. These fields do not guarantee provider
stock, active provider credentials, project authorization, or immediate placement.
Admission makes those decisions. An empty running fleet can therefore return
`labels: []` with a nonempty `provisionable` array. Older servers may omit the array;
clients must treat that as unknown, not proof that cloud capacity is unavailable.

ARM QEMU can run with `-accel tcg` without KVM on ARM64 or x86 hosts. The container
image must match the host architecture; the emulated guest architecture can differ.
Request `kvm` only for a workload that requires hardware acceleration, and use a
compatible host/guest architecture. Never silently substitute TCG for a KVM test.

The older public `hardware labels` command includes cached shape labels and is not a
current hardware snapshot. Use `hardware list` for current reports.
Preview workload operations appear in discovery after the API is deployed.
Cloud-init and Compose remain unsupported; do not infer them from image execution.

## GitLab webhook onboarding (CLI 0.4.0)

Use `realhw runners webhook inspect RUNNER_UUID` and
`realhw runners webhook configure RUNNER_UUID --reason REASON` to inspect and preview.
Add `--apply` after reviewing the result. Configuration needs `runners:write`;
inspection needs `runners:read`. Optional `--gitlab-token-env VARIABLE` names a secret
environment variable, never a token value. Ops equivalents are under
`realhw ops runners webhook`, with separate Ops credentials and policy discovery.
See [GitLab event onboarding](https://docs.realhw.dev/guides/gitlab-webhook/) for the API, safe credential
handling, audit IDs, server prerequisites, and actual delivery verification.
