Skip to content

Python client and CLI

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.

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, including logs and artifacts, when the server advertises workload.submit.

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:

Terminal window
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.

Terminal window
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.

Terminal window
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.

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.

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.

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

Terminal window
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.

Terminal window
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:

{"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.

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 for the API, safe credential handling, audit IDs, server prerequisites, and actual delivery verification.