plugin-vm
| Placement | compiled-in (in-process) |
| Source | github.com/opencharly/charly/candy/plugin-vm |
| Version | 2026.177.0400 |
| Candy | plugin-vm |
This plugin is listed in charly/charly.yml’s compiled_plugins:, so its providers are compiled into the charly binary and register in-process.
Providers
Section titled “Providers”The reserved words this plugin serves:
vm— command classlibvirt— verb class
What it does
Section titled “What it does”The VM subsystem relocated into a COMPILED-IN plugin candy (P10). It provides TWO
capabilities: verb:libvirt — the libvirt live check verb (list, info, screenshot,
send-key, QMP, qemu-guest-agent, snapshots, events) plus the internal VM-resolution ops
(resolve-target / domain-state / list-domains the host’s spice/vnc/ssh/status/preempt
consumers call), dispatched through the provider registry; and command:vm — the
charly vm … lifecycle CLI (build / create / start / stop / destroy / console / ssh /
snapshot / gpu / import / clone / cp-box / list), COMPILED-IN and dispatched IN-PROC via
Invoke(OpRun), so its handlers run in charly’s own process (native stdio/TTY for
console/ssh). The handlers own the CLI + the libvirt/qemu engine in-package and reach the
host-only Mechanisms over generic seams: the config loader + deploy-ledger READ via
HostBuild(“config-resolve”), the deploy-ledger WRITE plugin-side via
deploykit.SaveVmDeployState/RemoveVmDeployEntry directly (#55 coneC-dsh β2 — the
config-persist host seam is deleted), the VM-disk build engine plugin-side
(candy/plugin-vm/vm_build_resolve.go — the former HostBuild(“vm-build”) is deleted),
egress via verb:egress, preempt via verb:arbiter, GPU via verb:gpu.
All of it is backed by go-libvirt + kata-containers/govmm + libvirt.org/go/libvirtxml,
compiled into charly for this canonical placement. The libvirt: verb dispatches through
the provider registry exactly like a built-in (the libvirt: <method> sugar desugars to
plugin/plugin_input — the method + every libvirt-exclusive modifier ride the input map,
validated against this plugin’s own #LibvirtInput; authoring unchanged), running the
LibvirtCmd Kong tree IN this plugin’s process.
Parameter schema
Section titled “Parameter schema”The CUE schema below is the authoritative grammar for this plugin’s input. It is the same single source that generates the plugin’s Go parameter types and answers the runtime Describe RPC, so this page cannot disagree with either.
schema/vm.cue
Section titled “schema/vm.cue”// This out-of-tree VM plugin's OWN CUE schema, served over the Describe channel — the// typed plugin_input for the `libvirt` VM-probe check verb. It is the SINGLE SOURCE// for this plugin's params, used two ways (the same contract core `spec` and the http// plugin use)://// 1. GENERATE the Go param struct — `cue exp gengotypes` (driven by task cue:gen,// which wraps this with `package params` + `@go(params)`) emits// ../params/cue_types_gen.go, so the provider decodes plugin_input into a TYPED// struct, never a hand-parsed map.// 2. VALIDATE authored input AT RUNTIME — the plugin serves this source over the// Describe channel; the host splices it onto the base (base ++ plugin) and// validates every authored `libvirt:` step's plugin_input against #LibvirtVerbInput.//// Since the schema-compaction cutover the per-verb fields left core #Op: a step's// `libvirt: <method>` sugar desugars to the internal plugin/plugin_input pair, the// method name rides the input's `method` key (the former core #LibvirtMethod enum),// and every libvirt-exclusive modifier lives HERE — including `command` (the// guest/exec argv, ABSORBED from the former shared #Op command modifier). The// snapshot methods keep requiring the step-level `target:` field — a genuinely// SHARED #Op modifier that stays on the op, like timeout and the// exit_status/stdout/stderr matchers.//// SELF-CONTAINED: it references NO base def, so it compiles standalone (the SDK's// serve-side check + gengotypes) AND splices onto the base (base ++ plugin is a// def-name collision check, not a base-reference resolver).//// The plugin ALSO serves command:vm (`charly vm …`, the externalized VM lifecycle// CLI) — the command parses its own args out-of-process and carries NO plugin_input,// so no input def for it lives here.
// #LibvirtVerbInput is the `libvirt` verb's plugin_input: the method name plus its// method-exclusive modifiers.#LibvirtVerbInput: { // method — the libvirt method name (the former core #LibvirtMethod enum; the // verb's PRIMARY input field, so `libvirt: info` desugars to {method: "info"}). method: ("list" | "info" | "screenshot" | "send-key" | "passwd" | "qmp" | "domain-xml" | "console" | "events" | "guest/ping" | "guest/info" | "guest/os-info" | "guest/time" | "guest/hostname" | "guest/users" | "guest/interfaces" | "guest/disks" | "guest/fsinfo" | "guest/vcpus" | "guest/exec" | "guest/fstrim" | "snapshot/list" | "snapshot/create" | "snapshot/info" | "snapshot/revert" | "snapshot/delete") @go(Method,type=string) // text — passwd's new graphics password / qmp's command name. text?: string // input — qmp's optional JSON args blob. input?: string // key — send-key's key/chord spec (whitespace-split into keycode slots). key?: string @go(KeyName) // command — guest/exec's argv (whitespace-split; absorbed from the former // shared #Op command modifier — authored `command:` on a step is the command // plugin's sugar key now). command?: string // uri — libvirt connection URI override ("" → qemu:///session; the nested CLI // also honours CHARLY_LIBVIRT_URI). uri?: string @go(URI) // artifact + validators — screenshot's PNG output path and the post-run // artifact-reality assertions (sdk.RunArtifactValidators reads them off the input). artifact?: string artifact_min_bytes?: int & >=0 @go(ArtifactMinBytes,type=int) artifact_min_dimensions?: string & =~"^[0-9]+x[0-9]+$" @go(ArtifactMinDimensions) artifact_not_uniform?: bool @go(ArtifactNotUniform) artifact_min_cast_events?: int & >=0 @go(ArtifactMinCastEvents,type=int)}See also the candy reference for this candy’s install surface.