eval/skills/spice/SKILL.md
SPICE wire-level client for VMs — `charly eval spice <vm>` handshake, inputs, native display screenshots via the Shells-com/spice library.
npx skillsauth add overthinkos/overthink-plugins spiceInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
MUST be invoked before any work involving: charly eval spice commands,
SPICE protocol debugging, libvirt-VM display testing via the SPICE
wire, or anything that needs to prove a SPICE server is speaking
the protocol correctly (not just that the TCP port is open).
charly eval spice status <vm> # handshake + channel enumeration
charly eval spice screenshot <vm> [FILE] # native SPICE display-channel decode → PNG
charly eval spice click <vm> X Y # mouse press/release via inputs channel
charly eval spice mouse <vm> X Y # pointer move (no click)
charly eval spice type <vm> TEXT # type text as PC-AT scancodes
charly eval spice key <vm> NAME # press one named key (Return, Escape, F2, …)
charly eval spice cursor <vm> [FILE] # capture cursor bitmap + position
Global flags on every subcommand:
--address host:port — bypass vm.yml lookup; targets an arbitrary TCP SPICE server.--socket /path — bypass vm.yml lookup; targets an arbitrary UNIX-socket SPICE server.--password SECRET — SPICE ticket for --address mode.--uri qemu+ssh://[user@]host/session — resolve the VM on a remote libvirt host. charly auto-opens an SSH tunnel and forwards the remote SPICE socket (or TCP port) to a local endpoint for the lifetime of the command. Also accepts the CHARLY_LIBVIRT_URI env var.Screenshot/cursor <file> args accept - to write PNG bytes to stdout:
charly eval spice screenshot arch - > /tmp/shot.png. Status messages
go to stderr so stdout stays binary-clean — pipeline-friendly.
When --uri qemu+ssh://… is set, charly eval spice runs locally but pokes a
remote libvirt. Libvirt RPC is tunneled over the SSH control channel for
free; the SPICE display channel needs a side tunnel, which charly opens
transparently:
charly eval spice status arch --uri qemu+ssh://o.atrawog.org/session
# → opens SSH → discovers remote virtqemud socket via `id -u` → forwards
# the SPICE UNIX socket → dials it → prints channel enumeration.
For VMs that declare <listen type='socket'/> (the default for
arch after the socket-listen cutover), charly forwards the UNIX
socket. For TCP-listener VMs, charly opens a 127.0.0.1:<random> forward.
Alternative: charly --host o test spice status arch runs charly on
the remote machine itself — no side tunnel needed because the VM's SPICE
socket is local to the remote host. Useful when you want artifacts to stay
remote, or when the SPICE server doesn't bind loopback on the remote side.
GUI clients (virt-manager, remote-viewer --connect qemu+ssh://…) don't
need any charly involvement for socket listeners — they auto-forward via
libvirt RPC fd-passing. See /charly-vm:arch "Connecting from a
remote workstation" for the complete story.
github.com/Shells-com/spice.
Audio channels drag in portaudio + opusfile (Arch packages
listed in pkg/arch/PKGBUILD); these are required at build + run
time but the CLI itself never plays/records audio.charly eval spice <vm> loads
vm.yml, finds the running libvirt domain, parses live XML via
libvirtxml.Domain, and extracts the SPICE host/port/passwd from
the <graphics type="spice"> element (honoring autoport='yes').libvirt passwd, libvirt guest exec) for the corresponding management ops.# Start a VM with SPICE graphics (arch ships this by default).
charly vm start arch
# Handshake + report channels.
charly eval spice status arch
# → connected: 127.0.0.1:5901
# display: 1280x800
# inputs: ready
# Native SPICE screenshot (not libvirt DomainScreenshot).
charly eval spice screenshot arch /tmp/out.png
# → Screenshot saved to /tmp/out.png (1280x800, native SPICE display decode)
# Drive the login.
charly eval spice key arch return
charly eval spice type arch arch
charly eval spice key arch tab
charly eval spice type arch "my-password"
charly eval spice key arch return
charly eval libvirt)The two commands are single-protocol by design:
charly eval spice — every byte flows through the SPICE wire.
Use when the thing under test is "is SPICE itself healthy?".charly eval libvirt — every call goes through libvirtd RPC.
Use when the thing under test is "is the VM working?" (framebuffer
capture, keyboard injection, snapshots, domain state).For input testing, prefer charly eval spice type/key/click to prove
the SPICE wire delivers input to the guest. For display testing,
compare charly eval spice screenshot against charly eval libvirt screenshot — if both render the same pixels, the SPICE server +
the guest framebuffer agree.
charly/spice.go — Kong command tree, per-verb structs.charly/spice_session.go — thin wrapper over Shells-com/spice's
Connector/Driver interfaces. The Driver stub captures
display/cursor updates into a sync.Mutex-guarded field; the CLI
reads them back for screenshot/cursor verbs.charly/vm_target.go — shared target resolution (vm.yml → libvirt
domain → live XML → SPICE address).charly/spice.go (friendly keyname
→ scancode for type and key).github.com/Shells-com/spice (MIT) — SPICE client library.portaudio + opusfile Arch system packages (cgo dependencies
pulled in by the library's audio channels).libvirt.org/go/libvirtxml — XML parsing for SPICE address
extraction.github.com/digitalocean/go-libvirt — domain lookup.tools
Use when authoring or modifying a charly PLUGIN — a candy with a `plugin:` block that contributes Providers (verbs/kinds/deploy-targets/steps/builders/commands), its own CUE schema, builtin (compiled-in) or external (out-of-tree git repo). Covers the unified Provider model, the per-plugin CUE-schema contract (single source → Go params for dev + schema-over-Describe RPC for runtime), the SDK, and the loader.
tools
The CUE data-validation / configuration CLI (cue), pinned to v0.16.1. Use when working with the cue candy, installing the cue binary into a box or onto a target:local dev host, or running the offline schema-vendoring pipeline that feeds charly's egress validation.
tools
CUE EGRESS validation — validating (and, where it adds value, generating) the config files charly WRITES to a system BEFORE the bytes hit disk. MUST be invoked before working on charly/egress.go, the vendored schemas under candy/plugin-egress/egress-schemas/vendor/, the ValidateEgress / registerVendoredEgressKind path, the offline `task cue:vendor` pipeline, or adding an egress schema for any written artifact (cloud-init, k8s manifests, traefik routes, runtime config, install ledger, systemd/quadlet units, ssh_config, libvirt XML).
tools
Kubernetes cluster-probe declarative check verb — the `kube:` check verb (nodes, pods, ingress, storage class, addon health, apply/delete, and arbitrary resource GETs) served out-of-process by the candy/plugin-kube plugin (vendored client-go; no external kubectl required).