spec

package
v0.2026187.1130 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 6, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Pure structural methods on the generated/aliased spec types, relocated here from package main by the CUE-single-source cutover (WF-B THE REPOINT). A method on an aliased type (e.g. type BoxConfig = spec.Box in package main) cannot be declared in package main — the type is foreign — so every method that touches ONLY the type's own fields + the stdlib lives here, beside the type it acts on. Methods that reach package-main internals (VerbCatalog, Phase/Venue, EmitOpts, the readiness/preempt consts, …) stay in package main as free functions taking a *spec.X — they are NOT here. STANDALONE: this package imports nothing from package main.

Charly-name aliases for the generated `spec` types.

WF-B repoints package main onto this package via zero-churn `type <CharlyName> = spec.<CharlyName>` aliases. For that to compile the spec package must EXPOSE every param type under its charly name. The DEFMAP names the (cue-def → charly) mapping.

MECHANISM (RDD finding): `cue exp gengotypes` v0.16.1 does NOT propagate a def-level `@go(CharlyName)` rename to the FIELDS that reference that def — the referencing field keeps the original def name and the emitted Go dangles (uncompilable). Verified on a live spike. So the charly NAME is exposed here as a Go type alias (`type BoxConfig = Box`) instead of via a def-level attribute: identical drop-in semantics (the alias and the generated type are the SAME type), zero generated-file churn, and no broken references. The field-level `@go(GoName,…)` attributes (which DO work) carry the per-field name + pointer + type overrides in schema/*.cue.

HAND-WRITTEN — not emitted by `task cue:gen`; the reproducibility gate (gen_repro_test.go) only covers cue_types_gen.go + vocab_gen.go.

Hand-written types for shapes `cue exp gengotypes` cannot express faithfully:

  • PortSpec — the candy `port:` element is a (int | "proto:port") disjunction the Go normalizer canonicalizes to {port, protocol}; gengotypes degrades the disjunction to `any`.
  • VmDeployState — MACHINE-WRITTEN runtime deploy state. Its CUE def carries an open `...` tail (forward-evolving state record), which makes gengotypes degrade the whole def to `map[string]any`. The hand Go field is a CONCRETE struct, so #VmDeployState is @go(-)'d and the faithful struct (+ its nested state sub-types) is mirrored HERE.

Ported from the package-main param structs (the source of truth); keep in lockstep until WF-B repoints package main onto this package. STANDALONE — no package-main symbol is referenced.

Scalar collapses for the CUE-single-source cutover (WF-B THE REPOINT).

CUE validates these named scalars (CalVer/EntityRef/CandyRef pattern strings, VmSize/Size/PortPin/Duration patterns, the 11 live-verb method enums, the build-format/-type enums) at load — so the Go side gains nothing from a DISTINCT named string type, and package-main code uses plain string/[]string throughout. The matching CUE defs are `@go(-)` (no generated named type); the referencing fields keep the def name, which these Go ALIASES resolve to the builtin. An alias (not a defined type) is load-bearing: `&op.Cdp` is a `*string`, `[]PortPin` IS `[]string`, and an untyped/string assignment to a CalVer/EntityRef field just works — exactly the charly drop-in semantics.

MECHANISM (RDD finding, mirrors spec/charly_names.go): `cue exp gengotypes` v0.16.1 does NOT propagate a def-level rename/suppression to the FIELDS that reference the def, so a bare `@go(-)` leaves the field dangling on the deleted name. Exposing the name as a Go alias here makes every reference compile while collapsing the Go type to the builtin.

HAND-WRITTEN — not emitted by `task cue:gen`.

Hand-written union / shorthand types for the generated `spec` package.

`cue exp gengotypes` degrades every CUE disjunction to `any` / `map[string]any` / an empty struct (the Go type system cannot express a disjunction). For the handful of schema defs whose Go shape charly relies on, the matching CUE def is annotated `@go(-)` in schema/*.cue (suppressing the lossy generated type) and the faithful type is hand-written HERE, in the SAME package, so the generated structs reference these by name. These are ported from the hand-written charly param types (the source of truth named in each comment); keep them in lockstep until WF-B repoints package main onto this package.

STANDALONE: this package builds on its own (`go build ./spec/...`) and reaches into NO package-main symbol — every method here is self-contained.

Index

Constants

View Source
const (
	// PreemptStopShutdown is the only supported stop mechanism (graceful ACPI
	// shutdown / podman stop; disk preserved).
	PreemptStopShutdown = "shutdown"
	// PreemptRestoreAlways restarts the holder regardless of the claim's outcome
	// (the default).
	PreemptRestoreAlways = "always"
	// PreemptRestoreSuccess restarts the holder only if the claim released cleanly;
	// on a failed claim it is left stopped for operator inspection.
	PreemptRestoreSuccess = "on-success"
)
View Source
const (
	ArbiterSeamGather    = "gather"     // -> ArbiterGatherReply (preemptible holders, projected)
	ArbiterSeamResources = "resources"  // -> ArbiterResourcesReply (gpu-backed tokens -> vendor)
	ArbiterSeamRunning   = "running"    // ArbiterHolderReq -> ArbiterBoolReply
	ArbiterSeamStop      = "stop"       // ArbiterHolderReq -> ArbiterErrReply (stop + wait-until-stopped)
	ArbiterSeamStart     = "start"      // ArbiterHolderReq -> ArbiterErrReply
	ArbiterSeamSwitch    = "switchMode" // ArbiterSwitchReq -> ArbiterErrReply (routes to plugin-gpu)
	ArbiterSeamEnsureCDI = "ensureCDI"  // (no payload) -> (no reply)
	ArbiterSeamGpuCDI    = "gpuCDI"     // (no payload) -> ArbiterGpuCDIReply (running charly GPU-CDI pods)
)
View Source
const (
	ArbiterActionAcquireExclusive = "acquire-exclusive"
	ArbiterActionAcquireShared    = "acquire-shared"
	ArbiterActionRelease          = "release-claimant"
	ArbiterActionStatus           = "status"
	ArbiterActionReconcile        = "reconcile-stranded"
	ArbiterActionClearPoison      = "clear-poison"
	ArbiterActionResourcePoisoned = "resource-poisoned"
)
View Source
const (
	EncMethodMount   = "mount"   // mount the not-yet-mounted volumes (init must already exist)
	EncMethodUnmount = "unmount" // fusermount3 -u + stop the scope unit
	EncMethodEnsure  = "ensure"  // auto-init then mount (charly start transparent setup)
	EncMethodPasswd  = "passwd"  // gocryptfs -passwd for every initialized volume
)

EncMethod selects which gocryptfs operation plugin-enc runs.

View Source
const (
	// GpuModeVfio — EVERY function of the GPU's IOMMU group bound to vfio-pci
	// (free for VM passthrough; the boot default on a passthrough host).
	GpuModeVfio = "vfio"
	// GpuModeNvidia — each function on its correct host driver (display -> nvidia
	// for CDI-shared pods, HDMI-audio -> snd_hda_intel).
	GpuModeNvidia = "nvidia"

	// NvidiaVendorID is the normalized PCI vendor of NVIDIA cards (the device_lock
	// wedge is an nvidia-driver concept).
	NvidiaVendorID = "0x10de"

	// HostDriverDisplay / HostDriverAudio / HostDriverVfio are the host drivers a
	// group function binds to: the display (VGA/3D, class 0x03xx) takes nvidia in
	// host mode; the HDMI-audio (class 0x0403) takes snd_hda_intel; everything takes
	// vfio-pci for passthrough.
	HostDriverDisplay = "nvidia"
	HostDriverAudio   = "snd_hda_intel"
	HostDriverVfio    = "vfio-pci"
)
View Source
const (
	GpuSwitchActionMode          = "switch-mode"     // GpuSwitchInput{Gpu?,Vendor?,Mode} -> flip the group's driver mode (Gpu set = exact card; Vendor set = tolerant vendor-select)
	GpuSwitchActionEnsureCDI     = "ensure-cdi-root" // regenerate /etc/cdi/nvidia.yaml as ROOT (distinct from the C11 user-scope "ensure-cdi" detection action)
	GpuSwitchActionWedgeDetected = "wedge-detected"  // read-only D-state probe -> Bool
	GpuSwitchActionGroupInMode   = "group-in-mode"   // GpuSwitchInput{Gpu,Mode} -> Bool
	GpuSwitchActionCurrentMode   = "current-mode"    // GpuSwitchInput{Gpu} -> Str (vfio|nvidia)
	GpuSwitchActionDisplayDriver = "display-driver"  // GpuSwitchInput{Addr} -> Str (live driver of a PCI function)
	GpuSwitchActionPlan          = "switch-plan"     // GpuSwitchInput{Gpu,Mode} -> Plan (DRY-RUN commands; NO sysfs write) — the C9 cred/hw-free dispatch proof
)

GPU driver-switch actions served by verb:gpu (cutover C9).

View Source
const ReverseOpPluginScriptKey = "script"

ReverseOpPluginScriptKey is the Extra map key carrying a ReverseOpPluginScript's shell-script body. Exported so both the host handler and the SDK builder name the one key (R3 — no magic-string drift across the process boundary).

View Source
const SchemaFloor = "2026.174.1100"

SchemaFloor is the oldest schema version `charly migrate` can migrate FROM (schema/version.cue #SchemaFloor).

View Source
const SchemaVersion = "2026.186.2323"

SchemaVersion is the HEAD schema CalVer (schema/version.cue #SchemaVersion).

Variables

View Source
var AuthoringVerbs = []string{
	"build",
	"cache",
	"caps",
	"comment",
	"content",
	"context",
	"copy",
	"depends_on",
	"description",
	"download",
	"env",
	"eventually",
	"exclude_distro",
	"exit_status",
	"extract",
	"extract_include",
	"id",
	"link",
	"mkdir",
	"mode",
	"retry_interval",
	"run_as",
	"setcap",
	"skip",
	"stderr",
	"stdout",
	"strip_components",
	"tag",
	"target",
	"timeout",
	"to",
	"uninstall",
	"write",
}

AuthoringVerbs is the AUTHORABLE #Op field vocabulary (#Op fields minus the never-authored origin/venue/intent_do/plugin/plugin_input/command).

View Source
var ContextWords = []string{
	"build",
	"deploy",
	"runtime",
}

ContextWords is the plan-step execution contexts (#Context).

View Source
var DocDirectives = []string{
	"compiled_plugins",
	"context_ignore_baseline",
	"defaults",
	"device_descriptions",
	"device_patterns",
	"discover",
	"distro_family_map",
	"distro_package_managers",
	"gpu_vendors",
	"import",
	"install_hints",
	"ovmf_distro_aliases",
	"ovmf_paths",
	"pci_class_labels",
	"providers",
	"provides",
	"repo",
	"version",
}

DocDirectives is the reserved document directives (#NodeDoc top-level keys).

View Source
var ErrGPUSwitchWedged = errors.New("GPU driver switch wedged: nvidia .remove stuck holding the device_lock — host reboot required")

ErrGPUSwitchWedged signals that a GPU driver switch did not complete because the nvidia `.remove` is stuck holding the device_lock (deadline exceeded, or a self-detected D-state task in nv_pci_remove). candy/plugin-gpu carries the wedge as a bool over the wire (GpuSwitchReply.Wedged); the core gpu shim maps that bool back to this sentinel so callers (vm_gpu_cmd) keep matching it with errors.Is. Recovery is a host reboot.

View Source
var KindValueDefs = map[string]string{
	"android": "#AndroidValue",
	"candy":   "#CandyValue",
	"k8s":     "#K8sValue",
	"local":   "#LocalValue",
	"pod":     "#PodValue",
	"vm":      "#VmValue",
}

KindValueDefs is the word→#<Kind>Value CUE-def map the host uses to closedness-gate a substrate/candy node's authored VALUE (validateKindValueCUE). DERIVED from the #<X>Value defs themselves (every one except the shared #DeployValue disjunct), so a new value-gated kind needs no hand-maintained map.

View Source
var KindWords = []string{}

KindWords is the reserved kind keywords (the #Node disjunction discriminators).

View Source
var OpFields = []string{
	"build",
	"cache",
	"caps",
	"command",
	"comment",
	"content",
	"context",
	"copy",
	"depends_on",
	"description",
	"download",
	"env",
	"eventually",
	"exclude_distro",
	"exit_status",
	"extract",
	"extract_include",
	"id",
	"intent_do",
	"link",
	"mkdir",
	"mode",
	"origin",
	"plugin",
	"plugin_input",
	"retry_interval",
	"run_as",
	"setcap",
	"skip",
	"stderr",
	"stdout",
	"strip_components",
	"tag",
	"target",
	"timeout",
	"to",
	"uninstall",
	"venue",
	"write",
}

OpFields is every #Op verb/modifier field name (the flat Op vocabulary).

View Source
var OpVerbs = []string{
	"mkdir",
	"copy",
	"write",
	"link",
	"download",
	"setcap",
	"build",
	"plugin",
}

OpVerbs is the verb DISCRIMINATOR vocabulary (#OpVerb) — the exactly-one-set verb subset of #Op fields (Op.Kind() + the VerbCatalog dispatch table gate against it).

View Source
var ResourceKinds = []string{
	"pod",
	"vm",
	"k8s",
	"local",
	"android",
	"group",
}

ResourceKinds is the DEPLOYABLE subset of the kind keywords — the kinds whose #Node arm nests a sub-ENTITY (resource) child (#ResourceKind).

View Source
var StepKeywords = []string{
	"agent-check",
	"agent-run",
	"check",
	"include",
	"run",
}

StepKeywords is the plan-step intent keywords (#Step arms minus #Op fields).

Functions

func NormalizePCIVendor

func NormalizePCIVendor(s string) string

NormalizePCIVendor lowercases a PCI vendor id and ensures the "0x" prefix, so "10DE" / "0X10de" / "0x10de" all compare equal to the sysfs form ("0x10de").

Types

type AUR

type AUR struct {
	Package []PackageItem `yaml:"package,omitempty" json:"package,omitempty"`

	Options []string `yaml:"option,omitempty" json:"option,omitempty"`

	Replaces []string `yaml:"replace,omitempty" json:"replace,omitempty"`
}

type AdbEndpoint

type AdbEndpoint struct {
	Host string `yaml:"host,omitempty" json:"host"`
}

adb endpoint host is "host:port" but MAY carry a ${HOST_PORT:N} var-ref (resolved at deploy time), so only non-emptiness is enforced.

type Agent

type Agent struct {
	Description string `yaml:"description,omitempty" json:"description,omitempty"`

	Command []string `yaml:"command,omitempty" json:"command"`

	PromptVia string `yaml:"prompt_via,omitempty" json:"prompt_via"`

	VersionCommand []string `yaml:"version_command,omitempty" json:"version_command,omitempty"`

	Timeout Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`

	Env StrMap `yaml:"env,omitempty" json:"env,omitempty"`

	WorkingDir string `yaml:"working_dir,omitempty" json:"working_dir,omitempty"`

	Credential []CredentialMount `yaml:"credential,omitempty" json:"credential,omitempty"`

	ProgressCheckInterval Duration `yaml:"progress_check_interval,omitempty" json:"progress_check_interval,omitempty"`

	ProgressNoImprovementTimeout Duration `yaml:"progress_no_improvement_timeout,omitempty" json:"progress_no_improvement_timeout,omitempty"`

	OutputFormat string `yaml:"output_format,omitempty" json:"output_format"`
}

type AliasConfig

type AliasConfig = BoxAlias

--- box / deploy sub-types ---

type AliasYAML

type AliasYAML = CandyAlias

--- candy sub-types ---

type AlpineBootstrap

type AlpineBootstrap struct {
	MirrorURL string `yaml:"mirror_url,omitempty" json:"mirror_url,omitempty"`
}

type Android

type Android struct {
	Serial string `yaml:"serial,omitempty" json:"serial,omitempty"`

	Device string `yaml:"device,omitempty" json:"device,omitempty"`

	ApiLevel int `yaml:"api_level,omitempty" json:"api_level,omitempty"`

	GoogleAccount *GoogleAccount `yaml:"google_account,omitempty" json:"google_account,omitempty"`

	Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`

	// Device source — exactly one of box/adb (enforced in Go, see header).
	Box string `yaml:"box,omitempty" json:"box,omitempty"`

	Adb *AdbEndpoint `yaml:"adb,omitempty" json:"adb,omitempty"`
}

CUE schema for the `android` kind. #Android validates ONE kind:android entity (a device). A device is EITHER an in-pod emulator hosted by a kind:box (box) XOR a remote/physical adb endpoint (adb) — never both, never neither. Shared #Step from _common.cue.

CLOSED: the base struct (no trailing `...`) models every authored key, so an unknown key is still a typo. The box⊻adb exactly-one mutual-exclusion is enforced in GO (validateAndroidDevices in unified.go, the load-time sibling of validateCheckBeds) — NOT by a trailing `& ({box:_} | {adb:_})` disjunction: `cue exp gengotypes` collapses an entity-level top-level disjunction to an EMPTY `struct{}`, which would make spec.Android useless as a drop-in for AndroidSpec. The plain closed struct below generates the full field set, and the Go rule restores the exactly-one-of-box/adb XOR.

func (*Android) EffectiveSerial

func (a *Android) EffectiveSerial() string

EffectiveSerial returns the device serial, defaulting to the emulator serial.

func (*Android) IsEndpoint

func (a *Android) IsEndpoint() bool

IsEndpoint reports whether the device targets a remote adb endpoint.

type AndroidDeployVenue

type AndroidDeployVenue struct {
	AdbAddr     string           `json:"adb_addr"`
	Engine      string           `json:"engine,omitempty"`
	Container   string           `json:"container,omitempty"`
	Serial      string           `json:"serial,omitempty"`
	GoogleEmail string           `json:"google_email,omitempty"`
	GoogleToken string           `json:"google_token,omitempty"`
	Installs    []ApkPackageSpec `json:"installs,omitempty"`
	// BootTimeout / InstallDeadline / InstallInterval are the readiness +
	// install-retry windows the host ships (no magic numbers in the plugin):
	// boot gates sys.boot_completed; the install retries past PackageManager
	// post-boot init (a real synchronization condition, not a fixed sleep).
	BootTimeout     string `json:"boot_timeout,omitempty"`
	InstallDeadline string `json:"install_deadline,omitempty"`
	InstallInterval string `json:"install_interval,omitempty"`
}

AndroidDeployVenue is the preresolved deploy:android substrate payload the host's android deploy preresolver produces (in DeployVenue.Substrate) and the candy/plugin-adb deploy:android provider decodes. The host resolves the device endpoint (adb-server addr / in-pod engine+container / serial / google-play creds) and collects the apk install specs from the deploy's compiled plans (committed-APK Apk fields rewritten to ABSOLUTE host paths the plugin reads; package entries carry package/source/arch/app_version), so the plugin needs no project context and no goadb-less host resolution — it only drives the device.

type AndroidSpec

type AndroidSpec = Android

--- top-level entity types ---

type ApkPackageSpec

type ApkPackageSpec struct {
	Package    string `yaml:"package,omitempty" json:"package,omitempty"`
	Apk        string `yaml:"apk,omitempty" json:"apk,omitempty"`
	Source     string `yaml:"source,omitempty" json:"source,omitempty"`
	Arch       string `yaml:"arch,omitempty" json:"arch,omitempty"`
	AppVersion string `yaml:"app_version,omitempty" json:"app_version,omitempty"`
}

--------------------------------------------------------------------------- ApkPackageSpec — #CandyApk (android_spec.go). package XOR apk. ---------------------------------------------------------------------------

func (ApkPackageSpec) EffectiveArch

func (s ApkPackageSpec) EffectiveArch() string

EffectiveArch returns the APK ABI, defaulting to x86_64.

func (ApkPackageSpec) EffectiveSource

func (s ApkPackageSpec) EffectiveSource() string

EffectiveSource returns the APK source store, defaulting to apk-pure.

type ArbiterBoolReply

type ArbiterBoolReply struct {
	Bool bool `json:"bool"`
}

ArbiterBoolReply is the running-seam reply.

type ArbiterErrReply

type ArbiterErrReply struct {
	Error string `json:"error,omitempty"`
}

ArbiterErrReply carries a seam's operation error ("" = success) — the reverse-channel convention (the RPC itself succeeds; the op error rides here).

type ArbiterGatherReply

type ArbiterGatherReply struct {
	Holders []HolderDescriptor `json:"holders"`
}

ArbiterGatherReply is the host's projection of every RUNNING-or-not preemptible holder the arbiter may stop: PreemptionHolds() + holderAddrFor() + preemptEffectiveRestore() are applied host-side so the plugin's holdersToStop is pure config-free coordination.

type ArbiterGpuCDIReply added in v0.2026187.747

type ArbiterGpuCDIReply struct {
	Holders []HolderAddr `json:"holders"`
}

ArbiterGpuCDIReply lists every RUNNING charly pod container that holds the nvidia GPU as a CDI device. The arbiter reclaims the ones NOT covered by an active lease (leaked/kept eval beds whose transient owner-PID-tied lease died with the check-runner process) before a nvidia->vfio flip, so a stray container's open GPU handle can never wedge `modprobe -r nvidia`.

type ArbiterHolderReq

type ArbiterHolderReq struct {
	Addr HolderAddr `json:"addr"`
}

ArbiterHolderReq addresses one deployment for the running/stop/start seams.

type ArbiterInvokeInput

type ArbiterInvokeInput struct {
	Action    string     `json:"action"`
	Claimant  string     `json:"claimant,omitempty"`
	Tokens    []string   `json:"tokens,omitempty"`
	ClaimAddr HolderAddr `json:"claim_addr,omitempty"`
	Transient bool       `json:"transient,omitempty"`
	Success   bool       `json:"success,omitempty"` // release-claimant
	Token     string     `json:"token,omitempty"`   // clear-poison / resource-poisoned
}

ArbiterInvokeInput is the action-multiplexed input the proxy ships to verb:arbiter. Each action populates only the field(s) it needs.

type ArbiterInvokeReply

type ArbiterInvokeReply struct {
	Active   bool           `json:"active,omitempty"`   // acquire-* : the lease is active (env must be marked)
	Bool     bool           `json:"bool,omitempty"`     // resource-poisoned
	Ledger   *PreemptLedger `json:"ledger,omitempty"`   // status
	Stranded []string       `json:"stranded,omitempty"` // status
	Error    string         `json:"error,omitempty"`
}

ArbiterInvokeReply is the action-multiplexed reply from verb:arbiter.

type ArbiterResourcesReply

type ArbiterResourcesReply struct {
	Gpu map[string]string `json:"gpu"`
}

ArbiterResourcesReply maps each GPU-BACKED arbitration token to its PCI vendor (e.g. "nvidia-gpu" -> "0x10de"). A token ABSENT from the map is arbitration-only (no device to flip; applyMode skips it, firstPoisonedToken ignores it).

type ArbiterSwitchReply

type ArbiterSwitchReply struct {
	Error  string `json:"error,omitempty"`
	Wedged bool   `json:"wedged,omitempty"`
}

ArbiterSwitchReply is the switchMode-seam reply: the op error plus whether the flip WEDGED the device_lock (the sentinel can't cross the process boundary, so the bool carries it — the plugin's applyMode poisons the resource on Wedged).

type ArbiterSwitchReq

type ArbiterSwitchReq struct {
	Vendor string `json:"vendor"`
	Mode   string `json:"mode"`
}

ArbiterSwitchReq flips a GPU-backed token's whole IOMMU group to a driver mode.

type ArtifactView

type ArtifactView struct {
	ContainerPath string `json:"container_path,omitempty"`
	HostPath      string `json:"host_path,omitempty"`
	Chown         bool   `json:"chown,omitempty"`
}

ArtifactView is the wire mirror of package main's ArtifactRef (a BuilderStep output path to extract back to the venue / final image).

type BaseUser

type BaseUser struct {
	Name string `yaml:"name,omitempty" json:"name"`

	UID int `yaml:"uid,omitempty" json:"uid"`

	GID int `yaml:"gid,omitempty" json:"gid"`

	Home string `yaml:"home,omitempty" json:"home"`
}

type Bootloader

type Bootloader struct {
	InstallTemplate string `yaml:"install_template,omitempty" json:"install_template,omitempty"`

	InitramfsTemplate string `yaml:"initramfs_template,omitempty" json:"initramfs_template,omitempty"`

	FstabTemplate string `yaml:"fstab_template,omitempty" json:"fstab_template,omitempty"`
}

type Bootstrap

type Bootstrap struct {
	InstallCmd string `yaml:"install_cmd,omitempty" json:"install_cmd"`

	Package []string `yaml:"package,omitempty" json:"package,omitempty"`

	CacheMount []CacheMount `yaml:"cache_mount,omitempty" json:"cache_mount,omitempty"`
}

install_cmd is the bootstrap command; ubuntu sets it to "" (kept WITHOUT `& !=""` so the empty-string base case validates).

type Box

type Box struct {
	Name EntityRef `yaml:"name,omitempty" json:"name,omitempty"`

	Version CalVer `yaml:"version,omitempty" json:"version,omitempty"`

	Description string `yaml:"description,omitempty" json:"description,omitempty"`

	Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`

	Base string `yaml:"base,omitempty" json:"base,omitempty"`

	From string `yaml:"from,omitempty" json:"from,omitempty"`

	BootstrapBuilderImage string `yaml:"bootstrap_builder_image,omitempty" json:"bootstrap_builder_image,omitempty"`

	Platforms []string `yaml:"platform,omitempty" json:"platform,omitempty"`

	Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`

	Registry string `yaml:"registry,omitempty" json:"registry,omitempty"`

	Distro []string `yaml:"distro,omitempty" json:"distro,omitempty"`

	// BuildFormats: a scalar OR a list on the wire (#BuildFormats UnmarshalYAML
	// normalizes "rpm" → ["rpm"]); the Go field is []string. gengotypes degrades
	// the disjunction to `any`, so pin the Go shape explicitly (the typed enum is
	// a future enhancement — collapse for now).
	Build []string `yaml:"build,omitempty" json:"build,omitempty"`

	Candy []CandyRef `yaml:"candy,omitempty" json:"candy,omitempty"`

	User string `yaml:"user,omitempty" json:"user,omitempty"`

	UID *int `yaml:"uid,omitempty" json:"uid,omitempty"`

	GID *int `yaml:"gid,omitempty" json:"gid,omitempty"`

	UserPolicy string `yaml:"user_policy,omitempty" json:"user_policy,omitempty"`

	Builder map[string]string `yaml:"builder,omitempty" json:"builder,omitempty"`

	Produce []BuildType `yaml:"produce,omitempty" json:"produce,omitempty"`

	Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`

	EnvFile string `yaml:"env_file,omitempty" json:"env_file,omitempty"`

	Security *Security `yaml:"security,omitempty" json:"security,omitempty"`

	Network string `yaml:"network,omitempty" json:"network,omitempty"`

	Init string `yaml:"init,omitempty" json:"init,omitempty"`

	DataImage bool `yaml:"data_image,omitempty" json:"data_image,omitempty"`

	Bootc bool `yaml:"bootc,omitempty" json:"bootc,omitempty"`

	Readiness *Readiness `yaml:"readiness,omitempty" json:"readiness,omitempty"`

	Jobs *int `yaml:"jobs,omitempty" json:"jobs,omitempty"`

	PodmanJobs *int `yaml:"podman_jobs,omitempty" json:"podman_jobs,omitempty"`

	PodmanJobsCap *int `yaml:"podman_jobs_cap,omitempty" json:"podman_jobs_cap,omitempty"`

	ContextIgnore []string `yaml:"context_ignore,omitempty" json:"context_ignore,omitempty"`

	Cache string `yaml:"cache,omitempty" json:"cache,omitempty"`

	KeepImages *int `yaml:"keep_images,omitempty" json:"keep_images,omitempty"`

	KeepCheckRuns *int `yaml:"keep_check_runs,omitempty" json:"keep_check_runs,omitempty"`

	Merge *BoxMerge `yaml:"merge,omitempty" json:"merge,omitempty"`

	Alias []BoxAlias `yaml:"alias,omitempty" json:"alias,omitempty"`

	Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`

	CheckLevel string `yaml:"check_level,omitempty" json:"check_level,omitempty"`

	Shell *Shell `yaml:"shell,omitempty" json:"shell,omitempty"`
}

base and from are mutually exclusive; neither is also valid (scratch box). The entity-level base⊻from mutual-exclusion is enforced in GO (BoxConfig.HasBaseFromConflict, surfaced by validateBoxBaseFrom in validate.go and resolveBase in config.go) — NOT by a trailing `& ({from?: _|_} | {base?: _|_})` disjunction. `cue exp gengotypes` collapses an entity-level top-level disjunction to an EMPTY `struct{}`, which would make spec.Box useless as a drop-in for BoxConfig; the plain closed struct below generates the full field set, and the Go rule restores the XOR. The struct stays CLOSED (no `...`) so an unknown key is still a typo.

func (*Box) HasBaseFromConflict

func (ic *Box) HasBaseFromConflict() bool

HasBaseFromConflict reports whether the box authored BOTH base: and from:.

func (*Box) IsEnabled

func (ic *Box) IsEnabled() bool

IsEnabled reports whether the box is enabled (default true when unset).

type BoxAlias

type BoxAlias struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Command string `yaml:"command,omitempty" json:"command,omitempty"`
}

AliasConfig (config.go). CLOSED. command defaults to name when omitted.

type BoxConfig

type BoxConfig = Box

--- top-level entity types ---

type BoxMerge

type BoxMerge struct {
	Auto bool `yaml:"auto,omitempty" json:"auto,omitempty"`

	MaxMB int `yaml:"max_mb,omitempty" json:"max_mb,omitempty"`

	MaxTotalMB int `yaml:"max_total_mb,omitempty" json:"max_total_mb,omitempty"`
}

MergeConfig (config.go). CLOSED.

type BuildEnv

type BuildEnv struct {
	Distros []string `json:"distros,omitempty"`
	Image   string   `json:"image,omitempty"`
}

BuildEnv is the build-context descriptor the host puts in op.Env for an OpEmit Invoke at image-generation time: the image's distro tags + name, so a plugin can tailor its emitted Containerfile fragment per distro/arch. The build-time analogue of DeployVenue (deploy) / the verb check-env. Placement-agnostic: a builtin reads it in-proc, an external over gRPC.

type BuildFormat

type BuildFormat = string

--- build vocabulary enums (string) ---

type BuildReply

type BuildReply struct {
	Written []string `json:"written,omitempty"`
	Error   string   `json:"error,omitempty"`
}

BuildReply is what a build:box / build:generate plugin echoes back from its HostBuild call: the opaque list of artifacts the engine wrote (image refs for build, Containerfile paths for generate) plus a build error string (empty on success). A build FAILURE rides Error (the RPC itself succeeds — the reply-error convention), so the dispatcher surfaces it to the CLI.

type BuildRequest

type BuildRequest struct {
	Boxes           []string `json:"boxes,omitempty"`            // positional box selection ("" → all enabled)
	Tag             string   `json:"tag,omitempty"`              // --tag override (empty → CalVer)
	Dir             string   `json:"dir,omitempty"`              // project dir the host reconstructs config from
	IncludeDisabled bool     `json:"include_disabled,omitempty"` // --include-disabled
	DevLocalPkg     bool     `json:"dev_local_pkg,omitempty"`    // --dev-local-pkg (localpkg from local source; build only)
	Push            bool     `json:"push,omitempty"`             // --push (build only)
	Platform        string   `json:"platform,omitempty"`         // --platform (build only)
	Cache           string   `json:"cache,omitempty"`            // --cache mode (build only)
	NoCache         bool     `json:"no_cache,omitempty"`         // --no-cache (build only)
	Jobs            int      `json:"jobs,omitempty"`             // --jobs outer concurrency (build only)
	PodmanJobs      int      `json:"podman_jobs,omitempty"`      // --podman-jobs inner concurrency (build only)
}

BuildRequest is the BUILD-ENGINE DISPATCH envelope (F10 HostBuild seam): what `charly box build` / `charly box generate` marshal into a build:box / build:generate plugin's Invoke (op.Params), which the plugin forwards VERBATIM to the host via Executor.HostBuild. The heavy engine (Generator / OCITarget / the runtime Candy graph) STAYS host-side in-proc — only this small envelope crosses the seam. Everything the engine reads (Config / ResolvedBox / Candy) is reconstructed HOST-SIDE from Dir inside the registered host-builder (exactly as pod_deploy_lifecycle re-runs NewGenerator(dir,…)); the fields here are the CLI-supplied inputs that are NOT reconstructable from Dir alone. The generate path reads only Boxes/Tag/Dir/ IncludeDisabled; the build path additionally reads DevLocalPkg + the buildImages knobs (Push/Platform/Cache/NoCache/Jobs/PodmanJobs).

type BuildType

type BuildType = string

--- build vocabulary enums (string) ---

type Builder

type Builder struct {
	DetectFiles []string `yaml:"detect_file,omitempty" json:"detect_file,omitempty"`

	DetectConfig string `yaml:"detect_config,omitempty" json:"detect_config,omitempty"`

	RequiresSrcDir bool `yaml:"requires_src_dir,omitempty" json:"requires_src_dir,omitempty"`

	Inline bool `yaml:"inline,omitempty" json:"inline,omitempty"`

	CacheMount []CacheMount `yaml:"cache_mount,omitempty" json:"cache_mount,omitempty"`

	Env StrMap `yaml:"env,omitempty" json:"env,omitempty"`

	RuntimeEnv StrMap `yaml:"runtime_env,omitempty" json:"runtime_env,omitempty"`

	InstallTemplate string `yaml:"install_template,omitempty" json:"install_template,omitempty"`

	ManylinuxFix string `yaml:"manylinux_fix,omitempty" json:"manylinux_fix,omitempty"`

	BuildScript string `yaml:"build_script,omitempty" json:"build_script,omitempty"`

	Phases *PhaseSet `yaml:"phase,omitempty" json:"phase,omitempty"`

	InstallCommands map[string]string `yaml:"install_command,omitempty" json:"install_command,omitempty"`

	CopyArtifacts []Copy `yaml:"copy_artifact,omitempty" json:"copy_artifact,omitempty"`

	CopyBinary *Copy `yaml:"copy_binary,omitempty" json:"copy_binary,omitempty"`

	PathContributions []string `yaml:"path_contribution,omitempty" json:"path_contribution,omitempty"`

	Kind string `yaml:"kind,omitempty" json:"kind"`

	Privileged bool `yaml:"privileged,omitempty" json:"privileged"`

	OutputArtifact string `yaml:"output_artifact,omitempty" json:"output_artifact,omitempty"`
}

func (*Builder) IsBootstrap

func (b *Builder) IsBootstrap() bool

IsBootstrap reports whether the builder is a bootstrap builder.

type BuilderCollectInput

type BuilderCollectInput struct {
	Candy    string   `json:"candy"`
	Builder  string   `json:"builder"`
	Home     string   `json:"home,omitempty"`
	Packages []string `json:"packages,omitempty"` // the builder's detect-config section packages (aur)
	Replaces []string `json:"replaces,omitempty"` // aur `replaces:` — repo packages removed before pacman -U
}

BuilderCollectInput is the OpCollectContext params: the host-supplied candy descriptor an external builder plugin reads to produce its per-candy stage context. The host fills the generic fields it can derive without builder-specific knowledge (Candy/Builder/Home always; Packages/Replaces from the builder's detect-config package section, used today only by aur). A builder reads the subset it needs (pixi uses none → a constant env; cargo/npm none).

type BuilderCollectReply

type BuilderCollectReply struct {
	Context map[string]any `json:"context,omitempty"`
}

BuilderCollectReply is the OpCollectContext reply: the builder-specific stage-context keys the host merges onto the base context ({layer,builder,home}) to form the BuilderStep.RawStageContext (e.g. pixi → {env_name}, aur → {packages,replaces}).

type BuilderDef

type BuilderDef = Builder

--- top-level entity types ---

type BuilderResolveInput

type BuilderResolveInput struct {
	Candy            string   `json:"candy"`
	Builder          string   `json:"builder,omitempty"`
	BuilderRef       string   `json:"builder_ref,omitempty"`
	StageName        string   `json:"stage_name,omitempty"`
	LayerStage       string   `json:"layer_stage,omitempty"`
	CopySrc          string   `json:"copy_src,omitempty"`
	UID              int      `json:"uid,omitempty"`
	GID              int      `json:"gid,omitempty"`
	Home             string   `json:"home,omitempty"`
	User             string   `json:"user,omitempty"`
	Manifest         string   `json:"manifest,omitempty"`
	HasLockFile      bool     `json:"has_lock_file,omitempty"`
	InstallCmd       string   `json:"install_cmd,omitempty"`
	ManylinuxFix     string   `json:"manylinux_fix,omitempty"`
	HasBuildScript   bool     `json:"has_build_script,omitempty"`
	BuildScript      string   `json:"build_script,omitempty"`
	Packages         []string `json:"packages,omitempty"`
	Options          []string `json:"options,omitempty"`
	CacheMountsOwned string   `json:"cache_mounts_owned,omitempty"`
	CacheMountsAuto  string   `json:"cache_mounts_auto,omitempty"`
	Inline           bool     `json:"inline,omitempty"`
}

BuilderResolveInput is the OpResolve params: the RENDER CONTEXT the host computes and hands a builder plugin so it can render its build-time multi-stage self-contained. The host owns DETECTION (which candy triggers which builder — filesystem probes) and the serializable render context (the resolved builder image ref, the target user identity, the pre-rendered cache-mount flag strings); the plugin (via kit.BuilderResolve) owns the multi-stage TEMPLATE + the COPY-artifact/binary shape. The `external_builder:` path sends a MINIMAL input (Candy only — an out-of-tree builder renders a self-contained stage that reads none of the detection fields); the DETECTION path (pixi/npm/aur/cargo) sends the full context. Cache mounts arrive PRE-RENDERED (CacheMountsOwned/CacheMountsAuto) because the host owns the cache_mount vocab + the RenderCacheMounts helper — so the plugin needs no cache-mount render engine and the emitted bytes stay byte-identical to the former embedded-vocabulary render.

type BuilderResolveReply

type BuilderResolveReply struct {
	Stage          string   `json:"stage,omitempty"`
	CopyArtifacts  []string `json:"copy_artifacts,omitempty"`
	CopyBinary     string   `json:"copy_binary,omitempty"`
	InlineFragment string   `json:"inline_fragment,omitempty"`
}

BuilderResolveReply is what a builder plugin returns from an OpResolve Invoke at image-generation time — the build-time BUILDER leg, the multi-stage counterpart of a verb/step's EmitReply. It serves BOTH the `external_builder:`-SELECTED out-of-tree builders AND the four DETECTION-builders (pixi/npm/aur/cargo, whose build-time multi-stage moved out of the embedded builder: vocabulary into their plugins — see kit.BuilderResolve). Stage is the `FROM <ref> AS <name>` block (its RUN/COPY body included) spliced PRE-main-FROM (emitExternalBuilderStages for external_builder; emitBuilderStages for detection builders); CopyArtifacts are the `COPY --from=<stage> …` directives spliced POST-main-FROM to pull the built artifacts into the final image; CopyBinary is the single builder-BINARY copy line (pixi → /usr/local/bin/pixi) the host emits ONCE per builder (deduped across candies, mirroring the former emitBuilderArtifacts copy_binary handling); InlineFragment is the in-candy RUN an INLINE builder (cargo) emits IN the main image (no separate FROM stage) — Stage/CopyArtifacts are empty for an inline builder, InlineFragment empty for a multi-stage one. All fields are egress-validated with the rest of the Containerfile before it hits disk.

type BuilderReverseInput

type BuilderReverseInput struct {
	Candy   string         `json:"candy"`
	Builder string         `json:"builder"`
	Context map[string]any `json:"context,omitempty"`
}

BuilderReverseInput is the OpReverse params: the candy + its resolved stage-context keys (the BuilderCollectReply.Context). The plugin maps these to its teardown ops — the builder-specific reverse-op KIND (pixi-env-remove / package-remove / …) is exactly the per-builder Go logic this externalization moves out-of-process.

type BuilderReverseReply

type BuilderReverseReply struct {
	ReverseOps []ReverseOp `json:"reverse_ops,omitempty"`
}

BuilderReverseReply is the OpReverse reply: the builder's teardown ops, stashed by the host onto BuilderStep.PreResolvedReverse so BuilderStep.Reverse() is a pure getter (no RPC at its host-side call sites). For aur the host renders UninstallCmd later via fillReverseUninstallCmds (the format's uninstall_template), the same as a built-in package-remove op — the plugin names only Kind/Format/Targets/Scope.

type BundleNode

type BundleNode = Deploy

--- top-level entity types ---

type CacheMount

type CacheMount struct {
	Dst string `yaml:"dst,omitempty" json:"dst"`

	Sharing string `yaml:"sharing,omitempty" json:"sharing,omitempty"`

	Owned bool `yaml:"owned,omitempty" json:"owned,omitempty"`
}

A BuildKit cache mount. dst is the absolute in-builder cache path; sharing is the BuildKit sharing mode; owned renders a uid/gid-owned cache.

type CacheMountView

type CacheMountView struct {
	Dst     string `json:"dst,omitempty"`
	Sharing string `json:"sharing,omitempty"`
}

CacheMountView is the wire mirror of package main's CacheMountSpec (a BuildKit cache-mount config carried on a SystemPackagesStep). Kept tiny + spec-homed so the step view is fully self-describing on the SDK side.

type CalVer

type CalVer = string

--- named-pattern scalars (string) ---

type Candy

type Candy struct {
	// --- identity (required: ADE mandates version+name+description+plan) ---
	Version CalVer `yaml:"version,omitempty" json:"version"`

	Name EntityRef `yaml:"name,omitempty" json:"name,omitempty"`

	Description string `yaml:"description,omitempty" json:"description"`

	Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`

	// --- maturity / engine ---
	Status string `yaml:"status,omitempty" json:"status,omitempty"`

	// engine — a candy's required run engine; per-image resolution walks the
	// candy chain (engine.go ResolveBoxEngine) and cross-candy conflicts are a
	// validate error. RETAINED by the schema-compaction consumer audit.
	Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`

	// `from:` is NOT a candy field — EDGE-INHERIT cutover D: a candy: node carrying
	// `base:` or `from:` is a full IMAGE (#Box, the former box:), routed there by the
	// loader; a LAYER fragment has neither. So `from:` lives only on #Box.
	Reboot bool `yaml:"reboot,omitempty" json:"reboot,omitempty"`

	// --- composition ---
	Candy []CandyRef `yaml:"candy,omitempty" json:"candy,omitempty"`

	Require []CandyRef `yaml:"require,omitempty" json:"require,omitempty"`

	// bake_plugin — the OUT-OF-TREE plugin candies whose pre-built provider binary
	// this candy BAKES into every composing image at /usr/lib/charly/plugins/, so a
	// DEPLOYED container (no candy source, no go toolchain) can run an external plugin
	// its in-container charly needs at runtime — e.g. charly-mcp bakes plugin-mcp so
	// `charly mcp serve` resolves the external `mcp` command in-container. The S0
	// baked-plugin BUILD-side seam, the deploy-time counterpart of resolvePluginBinary's
	// bakedPluginBinary fallback (plugin_loader.go). See generate.go emitBakedPlugins.
	BakePlugin []CandyRef `yaml:"bake_plugin,omitempty" json:"bake_plugin,omitempty"`

	RequiresCapability []string `yaml:"requires_capability,omitempty" json:"requires_capability,omitempty"`

	Capability *CandyCapability `yaml:"capability,omitempty" json:"capability,omitempty"`

	// plugin — declaring this block makes the candy a PLUGIN: it provides
	// reserved-word Providers (built-in OR out-of-tree). The candy is otherwise
	// authored, validated, built, deployed, and checked like any candy (R3 — one
	// authoring surface). See provider.go / plugin_loader.go.
	Plugin *Plugin `yaml:"plugin,omitempty" json:"plugin,omitempty"`

	// external_builder — the reserved word of an EXTERNAL builder plugin
	// (`builder:<word>`, an out-of-tree grpcProvider) this candy SELECTS to produce
	// a multi-stage build artifact. At image build the generator resolves the word,
	// Invokes the provider's OpResolve, and splices the returned BuilderResolveReply
	// (the FROM…AS stage pre-main-FROM + the COPY --from artifacts post-main-FROM).
	// The build-time BUILDER leg — the counterpart of a `run:` step's `plugin:` verb
	// (the STEP leg). A builtin builder (pixi/cargo/npm/aur) is selected by detection
	// files, NOT this field. See generate.go emitExternalBuilderStages.
	ExternalBuilder string `yaml:"external_builder,omitempty" json:"external_builder,omitempty"`

	// --- runtime env / local vars / PATH ---
	// env forbids PATH (validate.go: use path_append instead). Values are
	// Go-coerced scalars (#StrVal) — an unquoted `PORT: 8080` is a string. The Go
	// field is map[string]string (yaml.v3 coerces the scalar), so pin the shape
	// (gengotypes degrades the #StrVal value to `any`).
	Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`

	// var: Go field is `Vars map[string]string`; gengotypes degrades the
	// pattern-keyed map to an empty struct, so pin name + shape.
	Vars map[string]string `yaml:"var,omitempty" json:"var,omitempty"`

	PathAppend []string `yaml:"path_append,omitempty" json:"path_append,omitempty"`

	// --- package surface (Calamares-aligned) ---
	Package []PackageItem `yaml:"package,omitempty" json:"package,omitempty"`

	Distro map[string]*DistroPackages `yaml:"distro,omitempty" json:"distro,omitempty"`

	Apk []ApkPackageSpec `yaml:"apk,omitempty" json:"apk,omitempty"`

	// localpkg maps a native package FORMAT to a bundled source dir; a scalar
	// form is rejected by Go. Closed to the three known formats.
	// localpkg Go field is `LocalPkg map[string]string` (a per-format → source-dir
	// map); pin name + shape (gengotypes would emit an inline struct named
	// `Localpkg`).
	LocalPkg map[string]string `yaml:"localpkg,omitempty" json:"localpkg,omitempty"`

	// --- networking / routing ---
	// PortSpec: a plain int OR a "proto:port" string (proto ∈ http/https/tcp/…);
	// the Go normalizer canonicalizes either form to the {port, protocol}
	// PortSpec struct, so the Go field is []PortSpec (gengotypes degrades the
	// scalar|string disjunction element to `any`).
	Port []PortSpec `yaml:"port,omitempty" json:"port,omitempty"`

	PortRelay []int `yaml:"port_relay,omitempty" json:"port_relay,omitempty"`

	Route *CandyRoute `yaml:"route,omitempty" json:"route,omitempty"`

	// --- services / volumes / aliases / extract / data ---
	Service []CandyService `yaml:"service,omitempty" json:"service,omitempty"`

	Volume []CandyVolume `yaml:"volume,omitempty" json:"volume,omitempty"`

	Alias []CandyAlias `yaml:"alias,omitempty" json:"alias,omitempty"`

	Extract []CandyExtract `yaml:"extract,omitempty" json:"extract,omitempty"`

	Data []CandyData `yaml:"data,omitempty" json:"data,omitempty"`

	// --- security / libvirt / hooks ---
	Security *Security `yaml:"security,omitempty" json:"security,omitempty"`

	Libvirt []string `yaml:"libvirt,omitempty" json:"libvirt,omitempty"`

	Hook *CandyHook `yaml:"hook,omitempty" json:"hook,omitempty"`

	// --- env/secret/mcp dependency + provides surface ---
	// All six env/secret/mcp dependency lists decode into the ONE Go type
	// []EnvDependency (pinned via @go(...,type=[]EnvDependency)); the per-context
	// CUE refinements (env-var name regex; the charly/ secret-key prefix) only
	// TIGHTEN validation — they don't change the emitted Go type (an inline
	// `#EnvDependency & {…}` would otherwise make gengotypes emit an anonymous
	// struct). mcp_* keep the base shape (an MCP server name is not env-var-
	// constrained).
	EnvProvides StrMap `yaml:"env_provide,omitempty" json:"env_provide,omitempty"`

	EnvRequire []EnvDependency `yaml:"env_require,omitempty" json:"env_require,omitempty"`

	EnvAccept []EnvDependency `yaml:"env_accept,omitempty" json:"env_accept,omitempty"`

	SecretAccept []EnvDependency `yaml:"secret_accept,omitempty" json:"secret_accept,omitempty"`

	SecretRequire []EnvDependency `yaml:"secret_require,omitempty" json:"secret_require,omitempty"`

	MCPProvide []CandyMCPProvide `yaml:"mcp_provide,omitempty" json:"mcp_provide,omitempty"`

	MCPRequire []EnvDependency `yaml:"mcp_require,omitempty" json:"mcp_require,omitempty"`

	MCPAccept []EnvDependency `yaml:"mcp_accept,omitempty" json:"mcp_accept,omitempty"`

	SecretYAML []CandySecret `yaml:"secret,omitempty" json:"secret,omitempty"`

	// --- shell init (same shape as box shell) ---
	Shell *Shell `yaml:"shell,omitempty" json:"shell,omitempty"`

	// --- operator-facing artifacts ---
	Artifact []CandyArtifact `yaml:"artifact,omitempty" json:"artifact,omitempty"`
}

type CandyAlias

type CandyAlias struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Command string `yaml:"command,omitempty" json:"command"`
}

AliasYAML — for a CANDY alias `command` is REQUIRED (validateAliases); the box-level #BoxAlias keeps it optional (defaults to name).

type CandyArtifact

type CandyArtifact struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Path string `yaml:"path,omitempty" json:"path"`

	RetrieveTo string `yaml:"retrieve_to,omitempty" json:"retrieve_to"`

	Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`

	Optional bool `yaml:"optional,omitempty" json:"optional,omitempty"`

	WaitSeconds int `yaml:"wait_second,omitempty" json:"wait_second,omitempty"`

	Rewrite []CandyArtifactRewrite `yaml:"rewrite,omitempty" json:"rewrite,omitempty"`
}

CandyArtifact — a file the candy publishes back to the operator post-setup.

type CandyArtifactRewrite

type CandyArtifactRewrite struct {
	Find string `yaml:"find,omitempty" json:"find"`

	Replace string `yaml:"replace,omitempty" json:"replace,omitempty"`
}

type CandyCapabilities

type CandyCapabilities = CandyCapability

--- candy sub-types ---

type CandyCapability

type CandyCapability struct {
	PreserveUser bool `yaml:"preserve_user,omitempty" json:"preserve_user,omitempty"`

	NeedsRootAfterInit bool `yaml:"needs_root_after_init,omitempty" json:"needs_root_after_init,omitempty"`

	InitSystemHint string `yaml:"init_system_hint,omitempty" json:"init_system_hint,omitempty"`

	DataOnly bool `yaml:"data_only,omitempty" json:"data_only,omitempty"`

	OCILabels map[string]string `yaml:"oci_label,omitempty" json:"oci_label,omitempty"`
}

CandyCapabilities — image-level facts a candy contributes (aggregated at resolve time). oci_label is a genuine open string→string passthrough.

type CandyData

type CandyData struct {
	Src string `yaml:"src,omitempty" json:"src"`

	Volume string `yaml:"volume,omitempty" json:"volume"`

	Dest string `yaml:"dest,omitempty" json:"dest,omitempty"`
}

DataYAML — stage candy-dir data into a volume at build, provision at deploy.

type CandyExtract

type CandyExtract struct {
	Source string `yaml:"source,omitempty" json:"source"`

	Path string `yaml:"path,omitempty" json:"path"`

	Dest string `yaml:"dest,omitempty" json:"dest"`
}

ExtractYAML — copy a path out of another OCI image into this one. source is an image ref; path (in the source image) and dest (in this image) are absolute (validateCandyContents).

type CandyHook

type CandyHook struct {
	PostEnable string `yaml:"post_enable,omitempty" json:"post_enable,omitempty"`

	PreRemove string `yaml:"pre_remove,omitempty" json:"pre_remove,omitempty"`
}

HooksConfig — lifecycle hook scripts.

type CandyMCPProvide

type CandyMCPProvide struct {
	Name string `yaml:"name,omitempty" json:"name"`

	URL string `yaml:"url,omitempty" json:"url"`

	Transport string `yaml:"transport,omitempty" json:"transport,omitempty"`
}

MCPServerYAML — mcp_provide entry exposed to peer containers.

type CandyRef

type CandyRef = string

--- named-pattern scalars (string) ---

type CandyRoute

type CandyRoute struct {
	Host string `yaml:"host,omitempty" json:"host"`

	Port int `yaml:"port,omitempty" json:"port"`
}

RouteYAML — generic service-route metadata (traefik / tunnel).

type CandySecret

type CandySecret struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Target string `yaml:"target,omitempty" json:"target,omitempty"`

	Env string `yaml:"env,omitempty" json:"env,omitempty"`
}

SecretYAML — a candy-owned secret (target defaults to /run/secrets/<name>).

type CandyService

type CandyService struct {
	Name string `yaml:"name,omitempty" json:"name"`

	UsePackaged string `yaml:"use_packaged,omitempty" json:"use_packaged,omitempty"`

	Exec string `yaml:"exec,omitempty" json:"exec,omitempty"`

	// distro restricts this entry to the named distros — a bare distro name
	// ("debian") or a versioned tag ("debian:13"). Empty = every distro (the
	// backward-compatible default). The service analogue of a check step's
	// exclude_distros: — it lets ONE candy carry per-distro-DIVERGENT packaged
	// units / exec daemons (the modular virtqemud.socket + virtnetworkd.socket
	// on Fedora/Arch vs the monolithic libvirtd.socket on Debian/Ubuntu, whose
	// libvirt is built without the split daemons) WITHOUT a <name>-host sibling
	// candy (CLAUDE.md "Init-system polymorphism"; R3). Filtered at render time
	// against the target distro tag chain (compileServiceSteps + generate.go).
	Distro []string `yaml:"distro,omitempty" json:"distro,omitempty"`

	Env StrMap `yaml:"env,omitempty" json:"env,omitempty"`

	Restart string `yaml:"restart,omitempty" json:"restart,omitempty"`

	WorkingDirectory string `yaml:"working_directory,omitempty" json:"working_directory,omitempty"`

	User string `yaml:"user,omitempty" json:"user,omitempty"`

	After []string `yaml:"after,omitempty" json:"after,omitempty"`

	Before []string `yaml:"before,omitempty" json:"before,omitempty"`

	WantedBy []string `yaml:"wanted_by,omitempty" json:"wanted_by,omitempty"`

	Stdout string `yaml:"stdout,omitempty" json:"stdout,omitempty"`

	StopTimeout string `yaml:"stop_timeout,omitempty" json:"stop_timeout,omitempty"`

	Scope string `yaml:"scope,omitempty" json:"scope,omitempty"`

	Enable bool `yaml:"enable,omitempty" json:"enable,omitempty"`

	Overrides *CandyServiceOverrides `yaml:"overrides,omitempty" json:"overrides,omitempty"`

	Kind string `yaml:"kind,omitempty" json:"kind,omitempty"`

	Events string `yaml:"event,omitempty" json:"event,omitempty"`

	AutoStart *bool `yaml:"auto_start,omitempty" json:"auto_start,omitempty"`

	StartRetries int `yaml:"start_retry,omitempty" json:"start_retry,omitempty"`

	StartSecs int `yaml:"start_sec,omitempty" json:"start_sec,omitempty"`

	StopSignal string `yaml:"stop_signal,omitempty" json:"stop_signal,omitempty"`

	ExitCode string `yaml:"exit_code,omitempty" json:"exit_code,omitempty"`

	Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
}

ServiceEntry (service_render.go). use_packaged XOR exec is a Go cross-field rule (mixed-entry polymorphism allows the SAME name twice: one packaged form, one exec form) — neither is required at the CUE layer.

func (*CandyService) EffectiveScope

func (e *CandyService) EffectiveScope() string

EffectiveScope returns the service scope, defaulting to "system".

func (*CandyService) IsPackaged

func (e *CandyService) IsPackaged() bool

IsPackaged reports whether the service is the packaged (use_packaged) form.

type CandyServiceOverrides

type CandyServiceOverrides struct {
	Env StrMap `yaml:"env,omitempty" json:"env,omitempty"`

	After []string `yaml:"after,omitempty" json:"after,omitempty"`

	Exec string `yaml:"exec,omitempty" json:"exec,omitempty"`
}

type CandyVolume

type CandyVolume struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Path string `yaml:"path,omitempty" json:"path"`
}

VolumeYAML — name is lowercase-hyphen (validateVolume); path may be home-relative (~/…) or absolute, so not anchored to /.

type CandyYAML

type CandyYAML = Candy

--- top-level entity types ---

type Check

type Check Deploy

#Check — a kind:check bed. Structurally IDENTICAL to #Deploy (same BundleNode Go struct), so it is a plain reference (R3 — no field duplication; stays CLOSED because #Deploy is closed). The bed-mode invariants the former `& (A|B|C)` disjunction expressed — disposable required + bed-legal target ∈ {pod,vm,local, android} for the deterministic/ephemeral modes, the iterate AI-benchmark mode, and the ephemeral⇒disposable promotion — are enforced in GO at load time (validateCheckBeds + validateEphemeralUnified in unified.go), which is the SINGLE source of truth for the actual bundle-form beds (a node-form check bed is a `bundle:` node validated via #BundleValue=#Deploy, so the disjunction was only ever applied to the legacy root-shape `check:` collection). Relaxing it to the alias removes that divergent parallel spec and lets gengotypes emit a real Check struct instead of an empty `struct{}`.

type CliReply added in v0.2026186.239

type CliReply struct {
	Stdout   string `json:"stdout,omitempty"`
	ExitCode int    `json:"exit_code,omitempty"`
	Error    string `json:"error,omitempty"`
}

CliReply is the "cli" host-builder reply: captured stdout (Capture=true), the exit code, and an error string on a non-zero exit that was not BestEffort-swallowed.

type CliRequest added in v0.2026186.239

type CliRequest struct {
	Argv       []string `json:"argv"`
	Capture    bool     `json:"capture,omitempty"`
	BestEffort bool     `json:"best_effort,omitempty"`
}

CliRequest is the "cli" host-builder envelope (M4): a lifecycle plugin asks the HOST to run a `charly <argv>` subcommand — Capture=false inherits stdin/out/err (interactive: charly shell, logs -f), Capture=true captures stdout (a status parse). BestEffort swallows a non-zero exit.

type Context

type Context = string

--- named-pattern scalars (string) ---

type Copy

type Copy struct {
	Src string `yaml:"src,omitempty" json:"src"`

	Dst string `yaml:"dst,omitempty" json:"dst"`

	Chown bool `yaml:"chown,omitempty" json:"chown,omitempty"`
}

type CredentialHealth added in v0.2026186.1117

type CredentialHealth struct {
	BackendName       string   `json:"backend_name"`
	ConfiguredBackend string   `json:"configured_backend"`
	KeyringAvailable  bool     `json:"keyring_available"`
	KeyringLocked     bool     `json:"keyring_locked"`
	PlaintextCount    int      `json:"plaintext_count"`
	NoSession         bool     `json:"no_session"`
	CollErr           string   `json:"coll_err,omitempty"`
	HealthyColls      []string `json:"healthy_colls,omitempty"`
	BrokenColls       []string `json:"broken_colls,omitempty"`
	IndexTotal        int      `json:"index_total"`
	IndexMissing      []string `json:"index_missing,omitempty"`
}

CredentialHealth is the credential-store health snapshot (moved here from charly core — it now has core + plugin-doctor consumers, R3). Rendered into the doctor "secret storage" checks by the plugin.

type CredentialMount

type CredentialMount struct {
	Src string `yaml:"src,omitempty" json:"src"`

	Dst string `yaml:"dst,omitempty" json:"dst"`

	Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`

	Optional bool `yaml:"optional,omitempty" json:"optional,omitempty"`
}

type DataYAML

type DataYAML = CandyData

--- candy sub-types ---

type Debootstrap

type Debootstrap struct {
	Suite string `yaml:"suite,omitempty" json:"suite,omitempty"`

	Mirror string `yaml:"mirror,omitempty" json:"mirror,omitempty"`

	Variant string `yaml:"variant,omitempty" json:"variant,omitempty"`

	Components string `yaml:"components,omitempty" json:"components,omitempty"`

	IncludePackages []string `yaml:"include_package,omitempty" json:"include_package,omitempty"`

	BasePackages []string `yaml:"base_package,omitempty" json:"base_package,omitempty"`

	ExtraRepos []DebootstrapRepo `yaml:"extra_repo,omitempty" json:"extra_repo,omitempty"`
}

type DebootstrapRepo

type DebootstrapRepo struct {
	Name string `yaml:"name,omitempty" json:"name"`

	URL string `yaml:"url,omitempty" json:"url"`

	Suite string `yaml:"suite,omitempty" json:"suite,omitempty"`

	Components string `yaml:"components,omitempty" json:"components,omitempty"`
}

type Deploy

type Deploy struct {
	Version CalVer `yaml:"version,omitempty" json:"version,omitempty"`

	Description string `yaml:"description,omitempty" json:"description,omitempty"`

	// target is DERIVED from the node's discriminator kind + cross-ref at load
	// (buildBundleNode/inferBundleTarget) — NOT authored in node-form. Optional
	// here so #Check's arms can pin it; the #BundleValue arm (node.cue) rejects an
	// authored `target:` outright. The former default `*"pod"` is dropped (Go's
	// classifyTarget supplies the empty→pod default). Generated as a plain Go
	// `string` (the loader stamps it; the CUE enum still validates a pinned value).
	Target string `yaml:"target,omitempty" json:"target,omitempty"`

	// member_of + inside are loader-DERIVED runtime fields (never authored;
	// rejected by #BundleValue): member_of marks a folded sibling-member entry,
	// inside names the venue a nested resource deploys into. Generated for the Go
	// tree-walker, forbidden in authoring.
	MemberOf string `yaml:"member_of,omitempty" json:"member_of,omitempty"`

	Inside string `yaml:"inside,omitempty" json:"inside,omitempty"`

	// agent_provisioned marks a resource member/child the AI deploys at run time
	// (the iterate-benchmark contract): image-less (no box:), not folded to a
	// top-level entry, exempt from the box-required validators. See deploy.go.
	AgentProvisioned bool `yaml:"agent_provisioned,omitempty" json:"agent_provisioned,omitempty"`

	// EDGE-INHERIT cutover B: the substrate kind is the EDGE discriminator (pod:/vm:/
	// k8s:/local:/android:/group:), so the deploy carries only NON-kind cross-refs:
	//
	//	from  — inherit a SAME-kind template by name (vm/k8s/local/android deploys).
	//	image — the box/OCI artifact a pod/k8s/android RUNS (the former `box:`).
	//
	// Per-substrate validity (image⊻from, source⊻from) is enforced in Go
	// (classifyTarget / validateDeploy), not CUE, so a `vm:` node is a VmSpec template
	// (source:) OR a deploy (from:) under ONE arm — the disjunction #Vm|#Deploy.
	From EntityRef `yaml:"from,omitempty" json:"from,omitempty"`

	Image string `yaml:"image,omitempty" json:"image,omitempty"`

	Kind string `yaml:"kind,omitempty" json:"kind,omitempty"`

	Replica int `yaml:"replica,omitempty" json:"replica,omitempty"`

	Restart string `yaml:"restart,omitempty" json:"restart,omitempty"`

	Schedule string `yaml:"schedule,omitempty" json:"schedule,omitempty"`

	Tunnel *TunnelYAML `yaml:"tunnel,omitempty" json:"tunnel,omitempty"`

	DNS string `yaml:"dns,omitempty" json:"dns,omitempty"`

	AcmeEmail string `yaml:"acme_email,omitempty" json:"acme_email,omitempty"`

	Port []PortPin `yaml:"port,omitempty" json:"port,omitempty"`

	ResolvedPort []PortPin `yaml:"resolved_port,omitempty" json:"resolved_port,omitempty"`

	// resolved_image: the concrete image ref the pod deploy's add_candy: overlay
	// build produced (`<deploy-key>-overlay:<hash>`), persisted by PrepareVenue so
	// config/start deploy EXACTLY that overlay (carrying the add_candy layers)
	// instead of re-resolving the base image: short-name by a CalVer sort that the
	// overlay alias can lose to the base on a same-minute build. charly-written
	// state (like resolved_port), never authored; empty for a plain pod.
	ResolvedImage string `yaml:"resolved_image,omitempty" json:"resolved_image,omitempty"`

	Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`

	EnvFile string `yaml:"env_file,omitempty" json:"env_file,omitempty"`

	Network string `yaml:"network,omitempty" json:"network,omitempty"`

	Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`

	Security *Security `yaml:"security,omitempty" json:"security,omitempty"`

	Secret []DeploySecret `yaml:"secret,omitempty" json:"secret,omitempty"`

	Volume []DeployVolume `yaml:"volume,omitempty" json:"volume,omitempty"`

	Sidecar map[string]Sidecar `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`

	ForwardGpgAgent *bool `yaml:"forward_gpg_agent,omitempty" json:"forward_gpg_agent,omitempty"`

	ForwardSshAgent *bool `yaml:"forward_ssh_agent,omitempty" json:"forward_ssh_agent,omitempty"`

	Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`

	Iterate *Iterate `yaml:"iterate,omitempty" json:"iterate,omitempty"`

	Shell []DeployShellOverlay `yaml:"shell,omitempty" json:"shell,omitempty"`

	AddCandy []string `yaml:"add_candy,omitempty" json:"add_candy,omitempty"`

	InstallOpts *InstallOpts `yaml:"install_opts,omitempty" json:"install_opts,omitempty"`

	Host string `yaml:"host,omitempty" json:"host,omitempty"`

	User string `yaml:"user,omitempty" json:"user,omitempty"`

	SSHArgs []string `yaml:"ssh_arg,omitempty" json:"ssh_arg,omitempty"`

	Cpus int `yaml:"cpus,omitempty" json:"cpus,omitempty"`

	Ram VmSize `yaml:"ram,omitempty" json:"ram,omitempty"`

	DiskSize VmSize `yaml:"disk_size,omitempty" json:"disk_size,omitempty"`

	Kubernetes *K8sDeploy `yaml:"kubernetes,omitempty" json:"kubernetes,omitempty"`

	Resources *DeployResources `yaml:"resources,omitempty" json:"resources,omitempty"`

	Expose *DeployExpose `yaml:"expose,omitempty" json:"expose,omitempty"`

	Storage []DeployStorage `yaml:"storage,omitempty" json:"storage,omitempty"`

	Probes *DeployProbes `yaml:"probes,omitempty" json:"probes,omitempty"`

	FromSnapshot string `yaml:"from_snapshot,omitempty" json:"from_snapshot,omitempty"`

	CloudInitClean bool `yaml:"cloud_init_clean,omitempty" json:"cloud_init_clean,omitempty"`

	VmState *VmDeployState `yaml:"vm_state,omitempty" json:"vm_state,omitempty"`

	Disposable *bool `yaml:"disposable,omitempty" json:"disposable,omitempty"`

	Lifecycle string `yaml:"lifecycle,omitempty" json:"lifecycle,omitempty"`

	Ephemeral *EphemeralLifetime `yaml:"ephemeral,omitempty" json:"ephemeral,omitempty"`

	Preemptible *PreemptibleConfig `yaml:"preemptible,omitempty" json:"preemptible,omitempty"`

	RequiresExclusive []string `yaml:"requires_exclusive,omitempty" json:"requires_exclusive,omitempty"`

	RequiresShared []string `yaml:"requires_shared,omitempty" json:"requires_shared,omitempty"`

	// nested/peer map keys carry no dots (validateDeploymentName). Loader-built
	// runtime tree maps (Children = nested-inside venue; Members = brought-up
	// alongside on the shared network) — gengotypes can't express a pattern-keyed
	// self-referential map, so the Go type is pinned explicitly.
	Children map[string]*Deploy `yaml:"nested,omitempty" json:"nested,omitempty"`

	Members map[string]*Deploy `yaml:"peer,omitempty" json:"peer,omitempty"`
}

func (*Deploy) HasChildren

func (n *Deploy) HasChildren() bool

HasChildren reports whether this node has any nested deployments.

func (Deploy) IsDisposable

func (c Deploy) IsDisposable() bool

IsDisposable reports whether the deploy is safe to destroy + rebuild.

func (Deploy) IsEphemeral

func (c Deploy) IsEphemeral() bool

IsEphemeral reports whether this deploy is marked ephemeral.

func (Deploy) IsGroup

func (c Deploy) IsGroup() bool

IsGroup reports whether this is a GROUP bundle — no workload cross-ref but with sibling members (the cross-deployment subject+driver shape).

func (Deploy) IsPreemptible

func (c Deploy) IsPreemptible() bool

IsPreemptible reports whether this deploy holds a preemptible exclusive resource (graceful-stop side).

func (Deploy) LifecycleTag

func (c Deploy) LifecycleTag() string

LifecycleTag returns the deploy's lifecycle tag.

func (Deploy) PreemptionHolds

func (c Deploy) PreemptionHolds() []string

PreemptionHolds returns the exclusive-resource token(s) this deploy occupies.

func (Deploy) RequiredExclusive

func (c Deploy) RequiredExclusive() []string

RequiredExclusive returns the exclusive-resource token(s) this deploy claims.

func (Deploy) RequiredShared

func (c Deploy) RequiredShared() []string

RequiredShared returns the shared-mode resource token(s) this deploy claims.

type DeployExpose

type DeployExpose struct {
	Host string `yaml:"host,omitempty" json:"host,omitempty"`

	Path string `yaml:"path,omitempty" json:"path,omitempty"`

	TLS bool `yaml:"tls,omitempty" json:"tls,omitempty"`

	Port string `yaml:"port,omitempty" json:"port,omitempty"`
}

type DeployProbes

type DeployProbes struct {
	Liveness *Op `yaml:"liveness,omitempty" json:"liveness,omitempty"`

	Readiness *Op `yaml:"readiness,omitempty" json:"readiness,omitempty"`

	Startup *Op `yaml:"startup,omitempty" json:"startup,omitempty"`
}

DeployProbes (deploy.go) — each probe is an inline Op (the check verb vocab).

type DeployReply

type DeployReply struct {
	ReverseOps []ReverseOp       `json:"reverse_ops,omitempty"`
	Record     DeployReplyRecord `json:"record"`
}

DeployReply is the structured result an external deploy provider returns from an OpExecute Invoke: the teardown ops the host records into the ledger, plus a provenance record. The host writes both via the SAME install_ledger.go path a built-in Add uses — identical persistence, record-and-replay preserved.

type DeployReplyRecord

type DeployReplyRecord struct {
	Candy   string `json:"candy"`
	Version string `json:"version,omitempty"`
}

DeployReplyRecord names the ledger CandyRecord the host writes for an external deploy: the logical candy whose ReverseOps drive teardown, plus its version.

type DeployResources

type DeployResources struct {
	CPURequest string `yaml:"cpu_request,omitempty" json:"cpu_request,omitempty"`

	MemoryRequest string `yaml:"memory_request,omitempty" json:"memory_request,omitempty"`
}

type DeploySecret

type DeploySecret struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Source string `yaml:"source,omitempty" json:"source,omitempty"`
}

type DeployShellOverlay

type DeployShellOverlay struct {
	ID string `yaml:"id,omitempty" json:"id,omitempty"`

	Init string `yaml:"init,omitempty" json:"init,omitempty"`

	Origin string `yaml:"origin,omitempty" json:"origin,omitempty"`

	PathAppend []string `yaml:"path_append,omitempty" json:"path_append,omitempty"`

	Path string `yaml:"path,omitempty" json:"path,omitempty"`

	Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`

	Bash *ShellSpec `yaml:"bash,omitempty" json:"bash,omitempty"`

	Skip bool `yaml:"skip,omitempty" json:"skip,omitempty"`

	Zsh *ShellSpec `yaml:"zsh,omitempty" json:"zsh,omitempty"`

	Fish *ShellSpec `yaml:"fish,omitempty" json:"fish,omitempty"`

	Sh *ShellSpec `yaml:"sh,omitempty" json:"sh,omitempty"`
}

DeployShellOverlay (deploy.go) — per-deploy shell-rc overlay: the shared #Shell body extended with the overlay identity/skip keys (id-keyed replace / skip merge semantics, MergeDeployShell).

func (*DeployShellOverlay) ByShell

func (o *DeployShellOverlay) ByShell() map[string]*ShellSpec

ByShell returns the per-shell overlay map (nil when none authored).

type DeployStorage

type DeployStorage struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Size string `yaml:"size,omitempty" json:"size,omitempty"`

	ClassHint string `yaml:"class_hint,omitempty" json:"class_hint,omitempty"`

	Access string `yaml:"access,omitempty" json:"access,omitempty"`

	Path string `yaml:"path,omitempty" json:"path,omitempty"`
}

type DeployVenue

type DeployVenue struct {
	DeployName string            `json:"deploy_name"`
	Env        map[string]string `json:"env,omitempty"`
	Substrate  json.RawMessage   `json:"substrate,omitempty"`
}

DeployVenue is the venue descriptor the host puts in op.Env for an external deploy Invoke: the deploy's name plus the merged deploy-node env (KEY=VALUE lines flattened to a map). The plugin reads it to locate where to apply its effects (e.g. a scratch dir) — the analogue of snapshotCheckEnv for a verb.

Substrate carries a substrate-SPECIFIC preresolved payload a registered host-side deploy preresolver produced for the external substrate word (e.g. AndroidDeployVenue for deploy:android — the resolved adb endpoint + the apk install specs). It is OPAQUE to the generic externalDeployTarget (which never branches on the substrate); only the matching plugin decodes it. nil for an external substrate with no preresolver (the marker-only example).

type DeployVolume

type DeployVolume struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Host string `yaml:"host,omitempty" json:"host,omitempty"`

	Path string `yaml:"path,omitempty" json:"path,omitempty"`

	DataSeeded bool `yaml:"data_seeded,omitempty" json:"data_seeded,omitempty"`

	DataSource string `yaml:"data_source,omitempty" json:"data_source,omitempty"`
}

type DetectedDevices

type DetectedDevices struct {
	GPU           bool     `json:"gpu"`             // NVIDIA GPU detected AND CDI achievable (driver + (spec OR nvidia-ctk))
	AMDGPU        bool     `json:"amd_gpu"`         // AMD GPU detected (/dev/kfd + video/render groups)
	AMDGFXVersion string   `json:"amd_gfx_version"` // AMD GFX version for HSA_OVERRIDE_GFX_VERSION (e.g. "10.3.0")
	RenderNode    string   `json:"render_node"`     // First /dev/dri/renderD* path for DRINODE/DRI_NODE
	Devices       []string `json:"devices"`         // Other device paths to pass via --device
}

DetectedDevices holds the results of host device auto-detection.

type Diagnostic

type Diagnostic struct {
	Severity string `json:"severity,omitempty"` // "error" | "warning" (empty → error)
	Message  string `json:"message"`
	Path     string `json:"path,omitempty"`
}

Diagnostic is one finding from a plugin kind's deep OpValidate check (F7/C8) — a message, an optional dotted field PATH within the entity body, and a severity ("error" fails validation; "warning" is surfaced but non-fatal; empty is treated as "error").

type Diagnostics

type Diagnostics struct {
	Items []Diagnostic `json:"items,omitempty"`
}

Diagnostics is the OpValidate reply: the structured findings a plugin kind returns when the host asks it to validate an authored entity body BEYOND the static CUE input-def gate. An empty Diagnostics (no items) means the body is valid.

func (Diagnostics) HasErrors

func (d Diagnostics) HasErrors() bool

HasErrors reports whether any item is error-severity (empty severity counts as error).

type Distro

type Distro struct {
	Inherits string `yaml:"inherits,omitempty" json:"inherits,omitempty"`

	InheritPackages bool `yaml:"inherit_packages,omitempty" json:"inherit_packages,omitempty"`

	Version string `yaml:"version,omitempty" json:"version,omitempty"`

	Bootstrap Bootstrap `yaml:"bootstrap,omitempty" json:"bootstrap,omitempty"`

	Workarounds []string `yaml:"workaround,omitempty" json:"workaround,omitempty"`

	Format map[string]*Format `yaml:"format,omitempty" json:"format,omitempty"`

	BaseUser *BaseUser `yaml:"base_user,omitempty" json:"base_user,omitempty"`

	Pacstrap *Pacstrap `yaml:"pacstrap,omitempty" json:"pacstrap,omitempty"`

	Debootstrap *Debootstrap `yaml:"debootstrap,omitempty" json:"debootstrap,omitempty"`

	AlpineBootstrap *AlpineBootstrap `yaml:"alpine_bootstrap,omitempty" json:"alpine_bootstrap,omitempty"`

	Bootloader *Bootloader `yaml:"bootloader,omitempty" json:"bootloader,omitempty"`

	Dnf *Dnf `yaml:"dnf,omitempty" json:"dnf,omitempty"`
}

func (*Distro) LocalPkgFormat

func (d *Distro) LocalPkgFormat(primaryFormat string) (string, *LocalPkg)

LocalPkgFormat picks the format whose local_pkg block to build the charly toolchain from: the caller's primary format, then the distro's own PrimaryFormat, then any localpkg-capable format (deterministic).

func (*Distro) PrimaryFormat

func (d *Distro) PrimaryFormat() string

PrimaryFormat returns the distro's primary (non-secondary) package format.

type DistroDef

type DistroDef = Distro

--- top-level entity types ---

type DistroPackages

type DistroPackages struct {
	Package []PackageItem `yaml:"package,omitempty" json:"package,omitempty"`

	Copr []string `yaml:"copr,omitempty" json:"copr,omitempty"`

	Repo []RepoBlock `yaml:"repo,omitempty" json:"repo,omitempty"`

	Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty"`

	Options []string `yaml:"option,omitempty" json:"option,omitempty"`

	Module []string `yaml:"module,omitempty" json:"module,omitempty"`

	AUR *AUR `yaml:"aur,omitempty" json:"aur,omitempty"`
}

type Dnf

type Dnf struct {
	MaxParallelDownloads int64 `yaml:"max_parallel_downloads,omitempty" json:"max_parallel_downloads,omitempty"`

	Fastestmirror bool `yaml:"fastestmirror,omitempty" json:"fastestmirror,omitempty"`
}

type Duration

type Duration = string

--- named-pattern scalars (string) ---

type EmitReply

type EmitReply struct {
	Fragment string `json:"fragment"`
}

EmitReply is what a plugin verb/builder returns from an OpEmit Invoke at build time: a verbatim Containerfile FRAGMENT (RUN/COPY/… directives) the generator splices into the emitted .build/<image>/Containerfile (egress-validated with the rest of the Containerfile before it hits disk). This is the build-context counterpart of a builtin verb's ProvisionActor.RenderProvisionScript (a shell RUN) generalized to any directive an external plugin owns. The host appends a trailing newline if absent.

type EncExecInput

type EncExecInput struct {
	Method     string          `json:"method"`
	ImageID    string          `json:"image_id"`
	BoxName    string          `json:"box_name"`
	Passphrase string          `json:"passphrase,omitempty"`
	OldPass    string          `json:"old_pass,omitempty"`
	NewPass    string          `json:"new_pass,omitempty"`
	Volumes    []EncVolumePlan `json:"volumes"`
}

EncExecInput is the self-contained gocryptfs-execution request the host ships to plugin-enc over OpExecute. ImageID is the systemd-ask-password / extpass id ("charly-<box>"); BoxName is the bare box name for remediation messages; Passphrase drives mount/ensure (gocryptfs init/mount via GOCRYPTFS_PASSWORD); OldPass/NewPass drive passwd. Volumes carries the host-resolved per-volume plan.

type EncExecReply

type EncExecReply struct {
	Error string `json:"error"`
}

EncExecReply is the execution verdict: Error == "" means success. The host shim turns a non-empty Error into a Go error.

type EncVolumePlan

type EncVolumePlan struct {
	Name        string `json:"name"`
	CipherDir   string `json:"cipher_dir"`
	PlainDir    string `json:"plain_dir"`
	ScopeUnit   string `json:"scope_unit"` // "charly-enc-<dir>-<name>" (no .scope suffix)
	Initialized bool   `json:"initialized"`
	Mounted     bool   `json:"mounted"`
}

EncVolumePlan is one encrypted volume, fully resolved HOST-SIDE: its charly name (for messages), the on-disk cipher/plain dirs, the systemd scope-unit name, and the host-probed initialized/mounted state. The plugin acts on these flags — it never re-derives a charly path convention nor re-probes state.

type EntityRef

type EntityRef = string

--- named-pattern scalars (string) ---

type EnvDependency

type EnvDependency struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Description string `yaml:"description,omitempty" json:"description"`

	Default string `yaml:"default,omitempty" json:"default,omitempty"`

	Key string `yaml:"key,omitempty" json:"key,omitempty"`
}

EnvDependency (layers.go) — ONE Go type reused for env_require/env_accept, secret_accept/secret_require, AND mcp_require/mcp_accept. The hand struct is a single SUPERSET {name, description, default, key}; the three former CUE defs (#CandyEnvDep/#CandySecretDep/#CandyMCPDep) were distinct only in their per-context name regex (env-var name vs mcp-server name) and the secret-only `key` override — all of which Go validate.go enforces. gengotypes cannot emit THREE shapes for ONE Go type, and a faithful drop-in for []EnvDependency requires every usage to carry all four fields (mcp_require's former 2-field def is missing default/key), so the three collapse to ONE shared #EnvDependency (R3 — one shared abstraction; per-context strictness stays in Go).

type EnvVar

type EnvVar = string

--- named-pattern scalars (string) ---

type EphemeralLifetime

type EphemeralLifetime struct {
	TTL           string `yaml:"ttl,omitempty" json:"ttl,omitempty"`
	KeepOnFailure bool   `yaml:"keep_on_failure,omitempty" json:"keep_on_failure,omitempty"`
	NamingPattern string `yaml:"naming_pattern,omitempty" json:"naming_pattern,omitempty"`
	// contains filtered or unexported fields
}

EphemeralLifetime parameterizes the auto-destruction lifecycle for a deploy.

func (*EphemeralLifetime) EffectiveNamingPattern

func (e *EphemeralLifetime) EffectiveNamingPattern() string

EffectiveNamingPattern returns the configured naming pattern, or the default.

func (*EphemeralLifetime) EffectiveTTL

func (e *EphemeralLifetime) EffectiveTTL() time.Duration

EffectiveTTL returns the configured TTL, or the 1h default.

func (*EphemeralLifetime) IsBoolForm

func (e *EphemeralLifetime) IsBoolForm() bool

IsBoolForm reports whether the field was authored as a bare boolean.

func (*EphemeralLifetime) UnmarshalJSON

func (e *EphemeralLifetime) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts the boolean shorthand and the block form (the loader is JSON-based via cue.Value.Decode). `ephemeral: false` is rejected.

type EphemeralRuntime

type EphemeralRuntime struct {
	ID              string `yaml:"id" json:"id"`
	ParentVm        string `yaml:"parent_vm,omitempty" json:"parent_vm,omitempty"`
	ParentSnapshot  string `yaml:"parent_snapshot,omitempty" json:"parent_snapshot,omitempty"`
	ParentEphemeral string `yaml:"parent_ephemeral,omitempty" json:"parent_ephemeral,omitempty"`
	ChildRefcount   int    `yaml:"child_refcount,omitempty" json:"child_refcount,omitempty"`
	TimerUnit       string `yaml:"timer_unit,omitempty" json:"timer_unit,omitempty"`
	TtlDeadline     string `yaml:"ttl_deadline,omitempty" json:"ttl_deadline,omitempty"`
	Status          string `yaml:"status,omitempty" json:"status,omitempty"`
	InstanceName    string `yaml:"instance_name,omitempty" json:"instance_name,omitempty"`
}

EphemeralRuntime is the live runtime state of an active ephemeral instantiation. Source: charly/deploy.go EphemeralRuntime.

type ExtractYAML

type ExtractYAML = CandyExtract

--- candy sub-types ---

type FeatureEntity added in v0.2026186.1143

type FeatureEntity struct {
	Kind             string        `json:"kind"`
	Name             string        `json:"name"`
	Description      string        `json:"description,omitempty"`
	Summary          string        `json:"summary,omitempty"`
	Steps            []FeatureStep `json:"steps,omitempty"`
	ValidationErrors []string      `json:"validation_errors,omitempty"`
}

FeatureEntity is one enumerated kind: entity + its plan data. Summary/Steps/ValidationErrors are populated only for entities WITH content (a non-empty description or plan) — an empty candy layer is still listed (as "(no description)") but not summarized or validated, matching the former engine.

type FeatureReply added in v0.2026186.1143

type FeatureReply struct {
	Entities []FeatureEntity `json:"entities,omitempty"`
	Error    string          `json:"error,omitempty"`
}

FeatureReply is the "feature" HostBuild kind reply — the enumerated entities the plugin formats into the list/pending/validate output. Error is a human-facing message on a load failure.

type FeatureRequest added in v0.2026186.1143

type FeatureRequest struct {
	Filter string `json:"filter,omitempty"`
}

FeatureRequest is the "feature" HostBuild kind request. Filter (empty | a kind "candy"/"box" | an entity id "candy:redis") narrows the enumeration.

type FeatureStep added in v0.2026186.1143

type FeatureStep struct {
	Index   int    `json:"index"`
	Keyword string `json:"keyword"`
	Text    string `json:"text,omitempty"`
	IsAgent bool   `json:"is_agent,omitempty"`
	IsCheck bool   `json:"is_check,omitempty"`
}

FeatureStep is one enumerated plan step flattened to plain data (no core Step model in the plugin).

type Format

type Format struct {
	CacheMount []CacheMount `yaml:"cache_mount,omitempty" json:"cache_mount,omitempty"`

	SectionFields map[string]string `yaml:"section_field,omitempty" json:"section_field,omitempty"`

	InstallTemplate string `yaml:"install_template,omitempty" json:"install_template,omitempty"`

	UninstallTemplate string `yaml:"uninstall_template,omitempty" json:"uninstall_template,omitempty"`

	Phases *PhaseSet `yaml:"phase,omitempty" json:"phase,omitempty"`

	Validate []FormatRule `yaml:"validate,omitempty" json:"validate,omitempty"`

	Secondary bool `yaml:"secondary,omitempty" json:"secondary,omitempty"`

	LocalPkg *LocalPkg `yaml:"local_pkg,omitempty" json:"local_pkg,omitempty"`
}

type FormatRule

type FormatRule struct {
	Field string `yaml:"field,omitempty" json:"field"`

	Rule string `yaml:"rule,omitempty" json:"rule"`
}

type GoogleAccount

type GoogleAccount struct {
	EmailSecret string `yaml:"email_secret,omitempty" json:"email_secret,omitempty"`

	TokenSecret string `yaml:"token_secret,omitempty" json:"token_secret,omitempty"`
}

type GpuProbeInput

type GpuProbeInput struct {
	Action         string            `json:"action"`                     // detect-gpu | detect-amd-gpu | detect-vfio | detect-host-devices | ensure-cdi | memlock | vfio-group-accessible | amd-gfx-version
	Group          int               `json:"group,omitempty"`            // vfio-group-accessible
	DevicePatterns []string          `json:"device_patterns,omitempty"`  // detect-host-devices
	GpuVendors     map[string]string `json:"gpu_vendors,omitempty"`      // detect-host-devices (pickRenderNode)
	PCIClassLabels map[string]string `json:"pci_class_labels,omitempty"` // detect-vfio (readPCIDevice)
}

GpuProbeInput is the action-multiplexed input the host ships to verb:gpu over OpRun. Action selects the host probe; the three data tables are threaded in from charly's embedded charly.yml (they stay in core for `charly doctor`, R3).

type GpuProbeReply

type GpuProbeReply struct {
	Bool        bool             `json:"bool,omitempty"`         // detect-gpu / detect-amd-gpu / vfio-group-accessible
	Str         string           `json:"str,omitempty"`          // amd-gfx-version
	Vfio        *VFIOReport      `json:"vfio,omitempty"`         // detect-vfio
	HostDevices *DetectedDevices `json:"host_devices,omitempty"` // detect-host-devices
	MemlockSoft uint64           `json:"memlock_soft,omitempty"` // memlock (RLIMIT_MEMLOCK soft)
	MemlockHard uint64           `json:"memlock_hard,omitempty"` // memlock (RLIMIT_MEMLOCK hard)
}

GpuProbeReply is the action-multiplexed reply from verb:gpu. Each action populates only the field(s) it produces.

type GpuSelector

type GpuSelector struct {
	Vendor string `yaml:"vendor,omitempty" json:"vendor"`
}

vendor required + non-empty; NOT regex-pinned (normalizePCIVendor accepts "10DE"/"0X10de"/"0x10de" — a strict regex would reject Go-valid input).

type GpuSwitchInput

type GpuSwitchInput struct {
	Action string   `json:"action"`           // switch-mode | ensure-cdi | wedge-detected | group-in-mode | current-mode | display-driver | switch-plan
	Gpu    *VFIOGpu `json:"gpu,omitempty"`    // switch-mode / group-in-mode / current-mode / switch-plan
	Mode   string   `json:"mode,omitempty"`   // switch-mode / group-in-mode / switch-plan
	Addr   string   `json:"addr,omitempty"`   // display-driver (a single PCI function)
	Vendor string   `json:"vendor,omitempty"` // switch-mode (tolerant vendor-select) / ensure-cdi-N/A
}

GpuSwitchInput is the action-multiplexed input the core driver-switch shims ship to verb:gpu over OpRun for the DRIVER-SWITCH actions (cutover C9). It rides the SAME verb:gpu provider as the C11 detection actions (GpuProbeInput); the action vocabularies are disjoint, so the plugin's Invoke dispatches by which envelope decodes — detection actions decode GpuProbeInput, switch actions decode this.

type GpuSwitchReply

type GpuSwitchReply struct {
	Bool   bool     `json:"bool,omitempty"`   // wedge-detected / group-in-mode
	Str    string   `json:"str,omitempty"`    // current-mode / display-driver
	Plan   []string `json:"plan,omitempty"`   // switch-plan: the exact rebind commands (DRY-RUN, no sysfs touched)
	Wedged bool     `json:"wedged,omitempty"` // switch-mode: the switch wedged the device_lock (errGPUSwitchWedged)
	Error  string   `json:"error,omitempty"`  // switch-mode / ensure-cdi op failure
}

GpuSwitchReply is the action-multiplexed reply for the DRIVER-SWITCH actions.

type HolderAddr

type HolderAddr struct {
	Name     string `yaml:"name" json:"name"`                             // full deploy key (for messages)
	Target   string `yaml:"target" json:"target"`                         // "vm" | "pod"
	Base     string `yaml:"base" json:"base"`                             // parseDeployKey base (pod container basis / vm fallback)
	Instance string `yaml:"instance,omitempty" json:"instance,omitempty"` // parseDeployKey instance
	Vm       string `yaml:"vm,omitempty" json:"vm,omitempty"`             // vm entity (target:vm)
}

HolderAddr is the self-contained address of a deployment (holder or claimant) — enough for the host seams to probe/stop/start it WITHOUT re-reading config, so a lease loaded after a crash can act on it. Carries BOTH yaml (the persisted ledger) AND json (the Invoke/reverse-channel envelope) tags.

type HolderDescriptor

type HolderDescriptor struct {
	Name    string     `json:"name"`
	Holds   []string   `json:"holds"`
	Addr    HolderAddr `json:"addr"`
	Restore string     `json:"restore"`
}

HolderDescriptor is one candidate preemptible holder, pre-projected host-side.

type HooksConfig

type HooksConfig = CandyHook

--- candy sub-types ---

type HostEnv added in v0.2026186.239

type HostEnv struct {
	CharlyBin string `json:"charly_bin,omitempty"`
	Home      string `json:"home,omitempty"`
	// Version is the host charly's CalVer (CharlyVersion()) — the delivery-decision authority for
	// EnsureCharlyInGuest, so the plugin need not exec `<CharlyBin> version` to learn it.
	Version string `json:"version,omitempty"`
}

HostEnv is the generic host identity a lifecycle plugin (running ON the host) needs but cannot derive: the host charly binary path (os.Args[0] of the CHARLY process — NOT the plugin's own binary) and the host home. Rides op.Env for every lifecycle Op so the plugin's cli legs run `<CharlyBin> …`.

type HostProbeDevice added in v0.2026186.1117

type HostProbeDevice struct {
	Pattern     string `json:"pattern"`
	Path        string `json:"path"`
	Present     bool   `json:"present"`
	Description string `json:"description"`
}

HostProbeDevice is one host device-pattern probe result (the doctor "devices" section).

type HostProbeDistro added in v0.2026186.1117

type HostProbeDistro struct {
	ID      string `json:"id"`
	Name    string `json:"name"`
	Manager string `json:"manager"`
}

HostProbeDistro is the host distro identity + package manager (drives install-hint rendering).

type HostProbeReply added in v0.2026186.1117

type HostProbeReply struct {
	GPU              bool                         `json:"gpu"`
	AMDGPU           bool                         `json:"amd_gpu"`
	AMDGFXVersion    string                       `json:"amd_gfx_version,omitempty"`
	GPUFlags         []string                     `json:"gpu_flags,omitempty"`
	Vfio             *VFIOReport                  `json:"vfio,omitempty"`
	MemlockSoft      uint64                       `json:"memlock_soft"`
	MemlockHard      uint64                       `json:"memlock_hard"`
	VfioPciAvailable bool                         `json:"vfio_pci_available"`
	GroupAccessible  map[int]bool                 `json:"group_accessible,omitempty"`
	Devices          []HostProbeDevice            `json:"devices,omitempty"`
	Distro           HostProbeDistro              `json:"distro"`
	InstallHints     map[string]map[string]string `json:"install_hints,omitempty"`
	DistroFamilyMap  map[string]string            `json:"distro_family_map,omitempty"`
	ConfigPath       string                       `json:"config_path,omitempty"`
	Credential       *CredentialHealth            `json:"credential,omitempty"`
	CredentialErr    string                       `json:"credential_err,omitempty"`
	Error            string                       `json:"error,omitempty"`
}

HostProbeReply is the "hostprobe" HostBuild kind reply — RAW host facts the plugin renders into the report. All fields are best-effort (a probe failure leaves its field zero/empty, mirroring the shims).

type HostProbeRequest added in v0.2026186.1117

type HostProbeRequest struct {
	Engine string `json:"engine,omitempty"`
}

HostProbeRequest is the "hostprobe" HostBuild kind request. Engine hints which container engine's GPU run-flags to compute (empty → the host resolves it).

type Init

type Init struct {
	CandyFields []string `yaml:"candy_field,omitempty" json:"candy_field,omitempty"`

	CandyFiles []string `yaml:"candy_file,omitempty" json:"candy_file,omitempty"`

	DependsCandy string `yaml:"depends_candy,omitempty" json:"depends_candy,omitempty"`

	RequiresCapability []string `yaml:"requires_capability,omitempty" json:"requires_capability,omitempty"`

	// The one mandatory field: the build model.
	Model string `yaml:"model,omitempty" json:"model"`

	HeaderFile string `yaml:"header_file,omitempty" json:"header_file,omitempty"`

	FragmentDir string `yaml:"fragment_dir,omitempty" json:"fragment_dir,omitempty"`

	RelayTemplate string `yaml:"relay_template,omitempty" json:"relay_template,omitempty"`

	StageName string `yaml:"stage_name,omitempty" json:"stage_name,omitempty"`

	StageHeaderCopy string `yaml:"stage_header_copy,omitempty" json:"stage_header_copy,omitempty"`

	StageFragmentCopy string `yaml:"stage_fragment_copy,omitempty" json:"stage_fragment_copy,omitempty"`

	AssemblyTemplate string `yaml:"assembly_template,omitempty" json:"assembly_template,omitempty"`

	SystemEnableTemplate string `yaml:"system_enable_template,omitempty" json:"system_enable_template,omitempty"`

	PostAssemblyTemplate string `yaml:"post_assembly_template,omitempty" json:"post_assembly_template,omitempty"`

	Entrypoint []string `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty"`

	FallbackEntrypoint []string `yaml:"fallback_entrypoint,omitempty" json:"fallback_entrypoint,omitempty"`

	ManagementTool string `yaml:"management_tool,omitempty" json:"management_tool,omitempty"`

	ManagementCommands map[string]string `yaml:"management_command,omitempty" json:"management_command,omitempty"`

	LabelKey string `yaml:"label_key,omitempty" json:"label_key,omitempty"`

	ServiceSchema *InitServiceSchema `yaml:"service_schema,omitempty" json:"service_schema,omitempty"`
}

type InitDef

type InitDef = Init

--- top-level entity types ---

type InitServiceSchema

type InitServiceSchema struct {
	ServiceTemplate string `yaml:"service_template,omitempty" json:"service_template,omitempty"`

	UnitPathTemplate string `yaml:"unit_path_template,omitempty" json:"unit_path_template,omitempty"`

	DropinTemplate string `yaml:"dropin_template,omitempty" json:"dropin_template,omitempty"`

	DropinPathTemplate string `yaml:"dropin_path_template,omitempty" json:"dropin_path_template,omitempty"`

	SupportsPackaged bool `yaml:"supports_packaged,omitempty" json:"supports_packaged,omitempty"`
}

type InstallOpts

type InstallOpts struct {
	WithServices bool `yaml:"with_service,omitempty" json:"with_service,omitempty"`

	AllowRepoChanges bool `yaml:"allow_repo_changes,omitempty" json:"allow_repo_changes,omitempty"`

	AllowRootTasks bool `yaml:"allow_root_tasks,omitempty" json:"allow_root_tasks,omitempty"`

	SkipIncompatible bool `yaml:"skip_incompatible,omitempty" json:"skip_incompatible,omitempty"`

	Verify bool `yaml:"verify,omitempty" json:"verify,omitempty"`

	BuilderImage string `yaml:"builder_image,omitempty" json:"builder_image,omitempty"`
}

install_opts gates (deploy.go InstallOptsConfig) — shared by local + deploy.

type InstallOptsConfig

type InstallOptsConfig = InstallOpts

--- box / deploy sub-types ---

type InstallPlanView

type InstallPlanView struct {
	DeployID        string            `json:"deploy_id,omitempty"`
	Box             string            `json:"box,omitempty"`
	Version         string            `json:"version,omitempty"`
	Distro          string            `json:"distro,omitempty"`
	Candy           string            `json:"candy,omitempty"`
	CandiesIncluded []string          `json:"candies_included,omitempty"`
	AddCandies      []string          `json:"add_candies,omitempty"`
	BuilderImage    string            `json:"builder_image,omitempty"`
	Meta            map[string]string `json:"meta,omitempty"`

	// Steps is the serializable per-step IR — the ordered InstallStep sequence the
	// in-core InstallPlan carries, projected onto the wire union below. An external
	// deploy/step plugin walks it and EXECUTES each step on the venue (the out-of-proc
	// twin of the local deploy target's in-proc walk), pushing files via the ExecutorService
	// PutFile leg and running shell via RunSystem/RunUser.
	Steps []InstallStepView `json:"steps,omitempty"`
}

InstallPlanView is the JSON-roundtrippable wire VIEW of an InstallPlan. The rich in-core InstallPlan (package main) carries a `Steps []InstallStep` interface slice; the host serializes it into the Steps field below via the SINGLE stepToView / stepFromView converter (package main charly/step_view.go), so an EXTERNAL deploy/step plugin that EXECUTES the plan walks the same ordered step IR the in-proc DeployTargets walk — the wire path and the in-proc path carry identical data (R3; proven by the step-IR round-trip test). The remaining fields are the plan's identity + provenance.

type InstallStepView

type InstallStepView struct {
	Kind string `json:"kind"` // the StepKind discriminator ("File","ShellHook","Op",…)

	// Derived ADVISORY fields — the step's Scope()/Venue()/RequiresGate() method
	// results, populated by stepToView so an executing plugin knows where the effect
	// lands (system → sudo / root-owned PutFile, user → no sudo), where it runs, and
	// which opt-in gate it needs, WITHOUT recomputing the rule (R3 — the rule lives on
	// the concrete step's methods, called once in stepToView). stepFromView IGNORES
	// them: the reconstructed concrete step recomputes the same values from its stored
	// fields, so these never participate in step round-trip identity.
	Scope Scope  `json:"scope,omitempty"`
	Venue int    `json:"venue,omitempty"`
	Gate  string `json:"gate,omitempty"`

	// Payload is the OPAQUE per-kind input for an EXTERNAL (plugin-contributed) step kind
	// (F3 — Kind == "external:<word>"): the bytes the host forwards verbatim as the serving
	// class:step plugin's OpExecute params. For an external kind the Scope/Venue/Gate fields
	// above are AUTHORITATIVE, not advisory — the host carries the plugin-DECLARED contract
	// (it cannot recompute it from a concrete step, there being no compiled-in case), so
	// stepFromView rebuilds the external step from these carried values + this Payload. nil
	// for every builtin (compiled-in) step kind.
	Payload json.RawMessage `json:"payload,omitempty"`

	// ReverseOps is the step's host-computed teardown ops — step.Reverse() called ONCE
	// host-side in stepToView. An OUT-OF-PROCESS plugin that EXECUTES a plugin-renderable
	// step itself (via RunSystem/RunUser/PutFile) cannot call the package-main Reverse()
	// method, so it ECHOES these into its DeployReply for record-and-replay teardown — the
	// Reverse() rule stays ONCE in package main (R3); the plugin gains zero reverse logic.
	// For the two deploy-time-stateful kinds (ServicePackaged.PriorEnabled,
	// ShellHook.EnvFile), the host captures that state on the live venue BEFORE projecting
	// the view, so the recorded ops are faithful. The HOST-ENGINE kinds (Builder /
	// LocalPkgInstall / SystemPackages / act-Op / ExternalPlugin) ride RunHostStep, which
	// returns their reverse ops separately, so the plugin ignores this field for the kinds
	// it routes to RunHostStep. Like the Scope/Venue/Gate advisory fields, stepFromView
	// IGNORES it (round-trip identity is unaffected).
	ReverseOps []ReverseOp `json:"reverse_ops,omitempty"`

	// Shared identity / provenance.
	CandyName string `json:"candy_name,omitempty"` // every kind
	CandyDir  string `json:"candy_dir,omitempty"`  // Builder / Op / ApkInstall / LocalPkgInstall

	// SystemPackagesStep + RepoChangeStep + LocalPkgInstallStep.
	Format string `json:"format,omitempty"`
	// SystemPackagesStep + BuilderStep three-phase tag (int Phase: prepare/install/cleanup).
	Phase int `json:"phase,omitempty"`

	// SystemPackagesStep.
	Packages          []string         `json:"packages,omitempty"`
	Repos             []map[string]any `json:"repos,omitempty"` // each = a RepoSpec.Raw map
	Options           []string         `json:"options,omitempty"`
	Copr              []string         `json:"copr,omitempty"`
	Modules           []string         `json:"modules,omitempty"`
	Exclude           []string         `json:"exclude,omitempty"`
	Keys              []string         `json:"keys,omitempty"`
	CacheMount        []CacheMountView `json:"cache_mount,omitempty"`
	RawInstallContext map[string]any   `json:"raw_install_context,omitempty"`

	// BuilderStep.
	Builder         string         `json:"builder,omitempty"`
	BuilderImage    string         `json:"builder_image,omitempty"`
	Artifacts       []ArtifactView `json:"artifacts,omitempty"`
	RawStageContext map[string]any `json:"raw_stage_context,omitempty"`
	BuilderDef      *BuilderDef    `json:"builder_def,omitempty"`
	// LocalPkg is shared by BuilderStep (aur) + LocalPkgInstallStep.
	LocalPkg *LocalPkg `json:"local_pkg,omitempty"`

	// OpStep + ExternalPluginStep (Op + the shared user/ctx/distro fields).
	Op           *Op               `json:"op,omitempty"`
	CtxPath      string            `json:"ctx_path,omitempty"`
	ResolvedUser string            `json:"resolved_user,omitempty"`
	To           string            `json:"to,omitempty"`
	CandyVars    map[string]string `json:"candy_vars,omitempty"`
	Distros      []string          `json:"distros,omitempty"`

	// FileStep.
	Source string `json:"source,omitempty"`
	Dest   string `json:"dest,omitempty"`
	Mode   uint32 `json:"mode,omitempty"` // os.FileMode underlying value
	Owner  string `json:"owner,omitempty"`

	// ServicePackagedStep + ServiceCustomStep.
	Unit          string `json:"unit,omitempty"`           // packaged unit name
	Name          string `json:"name,omitempty"`           // custom service name
	TargetScope   Scope  `json:"target_scope,omitempty"`   // both
	Enable        bool   `json:"enable,omitempty"`         // both
	OverridesText string `json:"overrides_text,omitempty"` // packaged drop-in
	OverridesPath string `json:"overrides_path,omitempty"` // packaged drop-in
	PriorEnabled  bool   `json:"prior_enabled,omitempty"`  // packaged
	UnitText      string `json:"unit_text,omitempty"`      // custom unit body
	UnitPath      string `json:"unit_path,omitempty"`      // custom unit path

	// ShellHookStep.
	EnvVars map[string]string `json:"env_vars,omitempty"`
	PathAdd []string          `json:"path_add,omitempty"`
	EnvFile string            `json:"env_file,omitempty"`

	// ShellSnippetStep.
	Origin      string   `json:"origin,omitempty"`
	Shell       string   `json:"shell,omitempty"`
	Snippet     string   `json:"snippet,omitempty"`
	PathAppend  []string `json:"path_append,omitempty"`
	Destination string   `json:"destination,omitempty"`
	Marker      string   `json:"marker,omitempty"`
	UseDropin   bool     `json:"use_dropin,omitempty"`
	Priority    int      `json:"priority,omitempty"`

	// RepoChangeStep.
	File     string `json:"file,omitempty"`
	Content  string `json:"content,omitempty"`
	Checksum string `json:"checksum,omitempty"`

	// ApkInstallStep.
	ApkPackages []ApkPackageSpec `json:"apk_packages,omitempty"`

	// LocalPkgInstallStep.
	PkgbuildRef string `json:"pkgbuild_ref,omitempty"`
	ProjectDir  string `json:"project_dir,omitempty"`
}

InstallStepView is the JSON-roundtrippable wire form of ONE InstallStep. The in-core IR (package main install_plan.go) has 13 concrete step types behind the InstallStep interface; this is their tagged-union projection, discriminated by Kind (the StepKind string). It is a SUPERSET struct: each kind populates the subset of fields it carries, all `omitempty` so the wire stays compact.

What it deliberately does NOT carry: Scope() / Venue() / RequiresGate() / Reverse(). Those are METHODS computed from the stored fields on the concrete step, not stored data — so stepFromView reconstructs the SAME concrete Go step type and all four methods (and therefore the step's execute semantics + ordering) are byte-identical to the in-proc path. The wire view carries only the stored data; the behaviour rides along for free. That is the structural guarantee the in-proc and wire paths cannot diverge (R3).

The package-main↔view mapping lives in ONE place — stepToView / stepFromView (charly/step_view.go) — and the step-IR round-trip test proves every kind survives stepFromView(stepToView(s)) DeepEqual-intact.

type Iterate

type Iterate struct {
	Agent []string `yaml:"agent,omitempty" json:"agent,omitempty"`

	Sandbox string `yaml:"sandbox,omitempty" json:"sandbox"`

	PlateauIteration int `yaml:"plateau_iteration,omitempty" json:"plateau_iteration,omitempty"`

	Prompt string `yaml:"prompt,omitempty" json:"prompt,omitempty"`

	Note *bool `yaml:"note,omitempty" json:"note,omitempty"`

	Env StrMap `yaml:"env,omitempty" json:"env,omitempty"`

	MCPEndpoint *string `yaml:"mcp_endpoint,omitempty" json:"mcp_endpoint,omitempty"`
}

func (*Iterate) NotesEnabled

func (i *Iterate) NotesEnabled() bool

NotesEnabled reports whether the iteration NOTES.md memory is on (default on).

type IterateConfig

type IterateConfig = Iterate

--- box / deploy sub-types ---

type K8s

type K8s struct {
	// May be empty (a cluster-policy-only template runs no workload itself).
	Box string `yaml:"box,omitempty" json:"box"`

	Replica *int `yaml:"replica,omitempty" json:"replica,omitempty"`

	Resources *K8sResources `yaml:"resources,omitempty" json:"resources,omitempty"`

	Hostnames []K8sHostname `yaml:"hostnames,omitempty" json:"hostnames,omitempty"`

	KubeconfigContext string `yaml:"kubeconfig_context,omitempty" json:"kubeconfig_context,omitempty"`

	AdmissionPolicy string `yaml:"admission_policy,omitempty" json:"admission_policy,omitempty"`

	DefaultNamespace string `yaml:"default_namespace,omitempty" json:"default_namespace,omitempty"`

	Storage K8sStorage `yaml:"storage,omitempty" json:"storage,omitempty"`

	Ingress K8sIngressDefaults `yaml:"ingress,omitempty" json:"ingress,omitempty"`

	GatewayAPI K8sGatewayAPI `yaml:"gateway_api,omitempty" json:"gateway_api,omitempty"`

	Secret K8sSecretsBackend `yaml:"secret,omitempty" json:"secret,omitempty"`

	ImageDefault K8sImagesDefaults `yaml:"image_default,omitempty" json:"image_default,omitempty"`

	PodDefault K8sPodDefaults `yaml:"pod_default,omitempty" json:"pod_default,omitempty"`

	Observability K8sObservability `yaml:"observability,omitempty" json:"observability,omitempty"`

	NetworkPolicy string `yaml:"network_policy,omitempty" json:"network_policy,omitempty"`

	Defaults K8sResourceDefaults `yaml:"defaults,omitempty" json:"defaults,omitempty"`

	Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`
}

type K8sDeploy

type K8sDeploy struct {
	Namespace EntityRef `yaml:"namespace,omitempty" json:"namespace,omitempty"`

	Workload string `yaml:"workload,omitempty" json:"workload,omitempty"`

	Patches []K8sPatch `yaml:"patches,omitempty" json:"patches,omitempty"`

	Raw []string `yaml:"raw,omitempty" json:"raw,omitempty"`
}

type K8sDeployVenue

type K8sDeployVenue struct {
	OverlayPath string `json:"overlay_path"`           // <root>/overlays/<inst> — the `kubectl apply -k` argument
	TreeRoot    string `json:"tree_root,omitempty"`    // <root> = .opencharly/k8s/<name> — removed at teardown
	KubeContext string `json:"kube_context,omitempty"` // kind:k8s template's kubeconfig_context → `kubectl --context` (empty → current-context)
	DeployName  string `json:"deploy_name,omitempty"`  // for plugin-side log messages
}

K8sDeployVenue is the preresolved deploy:k8s substrate payload the host's k8s deploy preresolver (charly/k8s_deploy_preresolve.go) produces in DeployVenue.Substrate and the candy/plugin-kube deploy:k8s provider decodes.

Unlike deploy:android — where the plugin DRIVES the device and the host only resolves the endpoint + apk specs — the k8s Kustomize GENERATOR moved into the compiled-in candy/plugin-k8sgen (verb:k8sgen, C8/M13), fronted by the in-core GenerateK8sKustomize shim: the shim lifts the image Capabilities (read from the OCI labels via ExtractMetadata) to ports/uid/gid, Invokes the generator's OpEmit, then applies the host-side egress gate (#K8sObject / #Kustomization) + disk I/O. It has a SECOND in-core consumer (`charly bundle from-box --target k8s`, k8s_deploy_from_box.go). So the host generates the egress-validated Kustomize tree under .opencharly/k8s/<name>/ and ships only the resolved overlay path + tree root. The plugin owns the LIVE cluster I/O — `kubectl apply -k <OverlayPath>` at deploy, and the recorded teardown (`kubectl delete -k` + remove the tree) replayed at `charly bundle del` — the k8s analogue of plugin-adb installing apps after the host resolves the specs. The host generates what needs core machinery; the plugin does the live external-system I/O it owns.

type K8sGatewayAPI

type K8sGatewayAPI struct {
	Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`

	GatewayClass string `yaml:"gateway_class,omitempty" json:"gateway_class,omitempty"`
}

type K8sGenFile

type K8sGenFile struct {
	RelPath    string          `json:"rel_path"`
	Doc        json.RawMessage `json:"doc"`
	EgressKind string          `json:"egress_kind"`
}

K8sGenFile is one generated manifest the plugin returns: its RELATIVE path (under OutputDir/DeploymentName, e.g. "base/deployment.yaml"), the manifest as JSON (the host unmarshals it back to a value, egress-validates, and writes it as YAML), and the egress kind that gates it ("k8s_object" or "kustomization").

type K8sGenInput

type K8sGenInput struct {
	DeploymentName string   `json:"deployment_name"`
	Instance       string   `json:"instance"`
	ImageRef       string   `json:"image_ref"`
	Deploy         Deploy   `json:"deploy"`     // = the former BundleNode
	Cluster        K8s      `json:"cluster"`    // = the former K8sSpec
	Ports          []string `json:"ports"`      // from BoxMetadata.Port
	UID            int      `json:"uid"`        // from BoxMetadata.UID
	GID            int      `json:"gid"`        // from BoxMetadata.GID
	OutputDir      string   `json:"output_dir"` // provenance; the host owns disk paths
}

K8sGenInput is the pure-generation input the host ships to plugin-k8sgen over OpEmit. Deploy is the deployment node (the former BundleNode = spec.Deploy); Cluster is the kind:k8s cluster template (the former K8sSpec = spec.K8s); Ports / UID / GID are lifted from the image's OCI-label Capabilities host-side so the plugin needs no access to the package-main BoxMetadata type.

type K8sGenReply

type K8sGenReply struct {
	OverlayRelPath string       `json:"overlay_rel_path"`
	Files          []K8sGenFile `json:"files"`
}

K8sGenReply is the pure-generation output: the RELATIVE overlay path the host joins onto OutputDir/DeploymentName to form the `kubectl apply -k` argument, and the collected manifest files (base resources + base/overlay kustomizations).

type K8sHostname

type K8sHostname struct {
	Host string `yaml:"host,omitempty" json:"host"`

	TLS bool `yaml:"tls,omitempty" json:"tls,omitempty"`

	Path string `yaml:"path,omitempty" json:"path,omitempty"`
}

type K8sImagesDefaults

type K8sImagesDefaults struct {
	PullPolicy string `yaml:"pull_policy,omitempty" json:"pull_policy,omitempty"`

	PullSecrets []string `yaml:"pull_secrets,omitempty" json:"pull_secrets,omitempty"`
}

type K8sIngressDefaults

type K8sIngressDefaults struct {
	Enabled bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`

	Class string `yaml:"class,omitempty" json:"class,omitempty"`

	CertIssuer string `yaml:"cert_issuer,omitempty" json:"cert_issuer,omitempty"`

	PathTypeDefault string `yaml:"path_type_default,omitempty" json:"path_type_default,omitempty"`
}

type K8sObservability

type K8sObservability struct {
	ServiceMonitor bool `yaml:"service_monitor,omitempty" json:"service_monitor,omitempty"`

	ServiceMonitorInterval string `yaml:"service_monitor_interval,omitempty" json:"service_monitor_interval,omitempty"`
}

type K8sPatch

type K8sPatch struct {
	Target struct {
		Kind string `yaml:"kind,omitempty" json:"kind,omitempty"`

		Name string `yaml:"name,omitempty" json:"name,omitempty"`

		Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"`
	} `yaml:"target,omitempty" json:"target"`

	Patch string `yaml:"patch,omitempty" json:"patch"`
}

type K8sPodDefaults

type K8sPodDefaults struct {
	PriorityClass string `yaml:"priority_class,omitempty" json:"priority_class,omitempty"`

	// Raw Kubernetes Toleration objects (Go []map[string]any) — genuine
	// passthrough, so each element stays OPEN.
	Tolerations []map[string]any `yaml:"tolerations,omitempty" json:"tolerations,omitempty"`

	NodeSelector map[string]string `yaml:"node_selector,omitempty" json:"node_selector,omitempty"`
}

type K8sResourceDefaults

type K8sResourceDefaults struct {
	Labels map[string]string `yaml:"labels,omitempty" json:"labels,omitempty"`

	Annotations map[string]string `yaml:"annotations,omitempty" json:"annotations,omitempty"`
}

type K8sResourceValues

type K8sResourceValues struct {
	CPU string `yaml:"cpu,omitempty" json:"cpu,omitempty"`

	Memory string `yaml:"memory,omitempty" json:"memory,omitempty"`
}

type K8sResources

type K8sResources struct {
	Requests K8sResourceValues `yaml:"requests,omitempty" json:"requests,omitempty"`

	Limits K8sResourceValues `yaml:"limits,omitempty" json:"limits,omitempty"`
}

type K8sSecretsBackend

type K8sSecretsBackend struct {
	Backend string `yaml:"backend,omitempty" json:"backend,omitempty"`

	Store string `yaml:"store,omitempty" json:"store,omitempty"`

	Prefix string `yaml:"prefix,omitempty" json:"prefix,omitempty"`
}

type K8sSpec

type K8sSpec = K8s

--- top-level entity types ---

type K8sStorage

type K8sStorage struct {
	ClassDefault string `yaml:"class_default,omitempty" json:"class_default,omitempty"`

	ClassCheap string `yaml:"class_cheap,omitempty" json:"class_cheap,omitempty"`

	ClassEncrypted string `yaml:"class_encrypted,omitempty" json:"class_encrypted,omitempty"`

	ClassFast string `yaml:"class_fast,omitempty" json:"class_fast,omitempty"`

	AccessModeDefault string `yaml:"access_mode_default,omitempty" json:"access_mode_default,omitempty"`
}

type LibvirtAudio

type LibvirtAudio struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	ID int `yaml:"id,omitempty" json:"id,omitempty"`
}

type LibvirtCPU

type LibvirtCPU struct {
	// mode is REQUIRED-with-default (renderer default host-passthrough) so the
	// custom⇒model if-guard below can reference it (optional fields error when
	// absent). #LibvirtCPU only instantiates when `cpu:` is present.
	Mode string `yaml:"mode,omitempty" json:"mode"`

	Model string `yaml:"model,omitempty" json:"model,omitempty"`

	Check string `yaml:"check,omitempty" json:"check,omitempty"`

	Migratable string `yaml:"migratable,omitempty" json:"migratable,omitempty"`

	Topology *LibvirtCPUTopology `yaml:"topology,omitempty" json:"topology,omitempty"`

	Features []LibvirtCPUFeature `yaml:"features,omitempty" json:"features,omitempty"`

	Cache *LibvirtCPUCache `yaml:"cache,omitempty" json:"cache,omitempty"`

	NUMA []LibvirtNUMACell `yaml:"numa,omitempty" json:"numa,omitempty"`
}

type LibvirtCPUCache

type LibvirtCPUCache struct {
	Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`

	Level int `yaml:"level,omitempty" json:"level,omitempty"`
}

type LibvirtCPUFeature

type LibvirtCPUFeature struct {
	Policy string `yaml:"policy,omitempty" json:"policy,omitempty"`

	Name string `yaml:"name,omitempty" json:"name"`
}

type LibvirtCPUTopology

type LibvirtCPUTopology struct {
	Sockets int `yaml:"sockets,omitempty" json:"sockets,omitempty"`

	Dies int `yaml:"dies,omitempty" json:"dies,omitempty"`

	Cores int `yaml:"cores,omitempty" json:"cores,omitempty"`

	Threads int `yaml:"threads,omitempty" json:"threads,omitempty"`
}

type LibvirtCPUTune

type LibvirtCPUTune struct {
	Shares int `yaml:"shares,omitempty" json:"shares,omitempty"`

	Period int `yaml:"period,omitempty" json:"period,omitempty"`

	Quota int `yaml:"quota,omitempty" json:"quota,omitempty"`

	GlobalPeriod int `yaml:"global_period,omitempty" json:"global_period,omitempty"`

	GlobalQuota int `yaml:"global_quota,omitempty" json:"global_quota,omitempty"`

	EmulatorPeriod int `yaml:"emulator_period,omitempty" json:"emulator_period,omitempty"`

	EmulatorQuota int `yaml:"emulator_quota,omitempty" json:"emulator_quota,omitempty"`

	IOThreadPeriod int `yaml:"iothread_period,omitempty" json:"iothread_period,omitempty"`

	IOThreadQuota int `yaml:"iothread_quota,omitempty" json:"iothread_quota,omitempty"`

	VCPUPin []LibvirtVCPUPin `yaml:"vcpupin,omitempty" json:"vcpupin,omitempty"`

	EmulatorPin *LibvirtEmulatorPin `yaml:"emulatorpin,omitempty" json:"emulatorpin,omitempty"`

	IOThreadPin []LibvirtIOThreadPin `yaml:"iothreadpin,omitempty" json:"iothreadpin,omitempty"`
}

type LibvirtChannel

type LibvirtChannel struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Name string `yaml:"name,omitempty" json:"name,omitempty"`

	Path string `yaml:"path,omitempty" json:"path,omitempty"`

	Source string `yaml:"source,omitempty" json:"source,omitempty"`
}

type LibvirtClock

type LibvirtClock struct {
	Offset string `yaml:"offset,omitempty" json:"offset,omitempty"`

	Timezone string `yaml:"timezone,omitempty" json:"timezone,omitempty"`

	Adjustment string `yaml:"adjustment,omitempty" json:"adjustment,omitempty"`

	Basis string `yaml:"basis,omitempty" json:"basis,omitempty"`

	Timers []LibvirtTimer `yaml:"timers,omitempty" json:"timers,omitempty"`
}

type LibvirtConsole

type LibvirtConsole struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Target map[string]string `yaml:"target,omitempty" json:"target,omitempty"`
}

type LibvirtDevices

type LibvirtDevices struct {
	Emulator string `yaml:"emulator,omitempty" json:"emulator,omitempty"`

	Disks []LibvirtDisk `yaml:"disks,omitempty" json:"disks,omitempty"`

	Interfaces []LibvirtInterface `yaml:"interfaces,omitempty" json:"interfaces,omitempty"`

	Channels []LibvirtChannel `yaml:"channels,omitempty" json:"channels,omitempty"`

	Serial []LibvirtSerial `yaml:"serial,omitempty" json:"serial,omitempty"`

	Console []LibvirtConsole `yaml:"console,omitempty" json:"console,omitempty"`

	Parallel []LibvirtParallel `yaml:"parallel,omitempty" json:"parallel,omitempty"`

	Graphics []LibvirtGraphics `yaml:"graphics,omitempty" json:"graphics,omitempty"`

	Video []LibvirtVideo `yaml:"video,omitempty" json:"video,omitempty"`

	Audio []LibvirtAudio `yaml:"audio,omitempty" json:"audio,omitempty"`

	Sound []LibvirtSound `yaml:"sound,omitempty" json:"sound,omitempty"`

	Inputs []LibvirtInput `yaml:"inputs,omitempty" json:"inputs,omitempty"`

	USB []LibvirtUSB `yaml:"usb,omitempty" json:"usb,omitempty"`

	RedirDev []LibvirtRedirDev `yaml:"redirdev,omitempty" json:"redirdev,omitempty"`

	Hostdevs []LibvirtHostdev `yaml:"hostdevs,omitempty" json:"hostdevs,omitempty"`

	Filesystems []LibvirtFilesystem `yaml:"filesystems,omitempty" json:"filesystems,omitempty"`

	RNG []LibvirtRNG `yaml:"rng,omitempty" json:"rng,omitempty"`

	TPM []LibvirtTPM `yaml:"tpm,omitempty" json:"tpm,omitempty"`

	Watchdog []LibvirtWatchdog `yaml:"watchdog,omitempty" json:"watchdog,omitempty"`

	MemBalloon *LibvirtMemBalloon `yaml:"memballoon,omitempty" json:"memballoon,omitempty"`

	Shmem []LibvirtShmem `yaml:"shmem,omitempty" json:"shmem,omitempty"`

	IOMMU *LibvirtIOMMU `yaml:"iommu,omitempty" json:"iommu,omitempty"`

	Vsock *LibvirtVsock `yaml:"vsock,omitempty" json:"vsock,omitempty"`

	Panic []LibvirtPanic `yaml:"panic,omitempty" json:"panic,omitempty"`

	Smartcard []LibvirtSmartcard `yaml:"smartcard,omitempty" json:"smartcard,omitempty"`

	Hub []LibvirtHub `yaml:"hub,omitempty" json:"hub,omitempty"`
}

type LibvirtDisk

type LibvirtDisk struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Device string `yaml:"device,omitempty" json:"device,omitempty"`

	Source map[string]string `yaml:"source,omitempty" json:"source,omitempty"`

	Target map[string]string `yaml:"target,omitempty" json:"target,omitempty"`

	Driver map[string]string `yaml:"driver,omitempty" json:"driver,omitempty"`

	Readonly *bool `yaml:"readonly,omitempty" json:"readonly,omitempty"`

	Serial string `yaml:"serial,omitempty" json:"serial,omitempty"`

	WWN string `yaml:"wwn,omitempty" json:"wwn,omitempty"`

	Boot int `yaml:"boot,omitempty" json:"boot,omitempty"`
}

type LibvirtDomain

type LibvirtDomain struct {
	Snippets []string `yaml:"snippets,omitempty" json:"snippets,omitempty"`

	XMLPassthrough string `yaml:"xml_passthrough,omitempty" json:"xml_passthrough,omitempty"`

	Features *LibvirtFeatures `yaml:"features,omitempty" json:"features,omitempty"`

	CPU *LibvirtCPU `yaml:"cpu,omitempty" json:"cpu,omitempty"`

	Clock *LibvirtClock `yaml:"clock,omitempty" json:"clock,omitempty"`

	MemoryBacking *LibvirtMemoryBacking `yaml:"memory_backing,omitempty" json:"memory_backing,omitempty"`

	MemTune *LibvirtMemTune `yaml:"memtune,omitempty" json:"memtune,omitempty"`

	NUMATune *LibvirtNUMATune `yaml:"numatune,omitempty" json:"numatune,omitempty"`

	CPUTune *LibvirtCPUTune `yaml:"cputune,omitempty" json:"cputune,omitempty"`

	IOThreads int `yaml:"iothreads,omitempty" json:"iothreads,omitempty"`

	Devices *LibvirtDevices `yaml:"devices,omitempty" json:"devices,omitempty"`

	SecLabel *LibvirtSecLabel `yaml:"seclabel,omitempty" json:"seclabel,omitempty"`

	LaunchSecurity *LibvirtLaunchSecurity `yaml:"launch_security,omitempty" json:"launch_security,omitempty"`

	Resource *LibvirtResource `yaml:"resource,omitempty" json:"resource,omitempty"`

	SysInfo *LibvirtSysInfo `yaml:"sysinfo,omitempty" json:"sysinfo,omitempty"`
}

--------------------------------------------------------------------------- libvirt: LibvirtDomain (libvirt_yaml.go). CLOSED, every sub-type modeled as a #Libvirt<Name> def. Genuine passthroughs stay typed (NOT blanket `{...}`): snippets/xml_passthrough (raw XML) and every map[string]string Go field as `{[string]: string}`. ---------------------------------------------------------------------------

type LibvirtEmulatorPin

type LibvirtEmulatorPin struct {
	CPUSet string `yaml:"cpuset,omitempty" json:"cpuset"`
}

type LibvirtFeatures

type LibvirtFeatures struct {
	ACPI *bool `yaml:"acpi,omitempty" json:"acpi,omitempty"`

	APIC *bool `yaml:"apic,omitempty" json:"apic,omitempty"`

	PAE *bool `yaml:"pae,omitempty" json:"pae,omitempty"`

	SMM *bool `yaml:"smm,omitempty" json:"smm,omitempty"`

	HAP *bool `yaml:"hap,omitempty" json:"hap,omitempty"`

	VMPort *bool `yaml:"vmport,omitempty" json:"vmport,omitempty"`

	PMU *bool `yaml:"pmu,omitempty" json:"pmu,omitempty"`

	HyperV *LibvirtHyperV `yaml:"hyperv,omitempty" json:"hyperv,omitempty"`

	KVM *LibvirtKVM `yaml:"kvm,omitempty" json:"kvm,omitempty"`

	IBS string `yaml:"ibs,omitempty" json:"ibs,omitempty"`
}

type LibvirtFilesystem

type LibvirtFilesystem struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Driver string `yaml:"driver,omitempty" json:"driver,omitempty"`

	AccessMode string `yaml:"accessmode,omitempty" json:"accessmode,omitempty"`

	Source string `yaml:"source,omitempty" json:"source"`

	Target string `yaml:"target,omitempty" json:"target"`

	Readonly *bool `yaml:"readonly,omitempty" json:"readonly,omitempty"`

	Binary map[string]string `yaml:"binary,omitempty" json:"binary,omitempty"`
}

type LibvirtGraphics

type LibvirtGraphics struct {
	Type string `yaml:"type,omitempty" json:"type"`

	Port int `yaml:"port,omitempty" json:"port,omitempty"`

	AutoPort string `yaml:"autoport,omitempty" json:"autoport,omitempty"`

	Listen LibvirtGraphicsListeners `yaml:"listen,omitempty" json:"listen,omitempty"`

	Passwd string `yaml:"passwd,omitempty" json:"passwd,omitempty"`

	Keymap string `yaml:"keymap,omitempty" json:"keymap,omitempty"`

	GL string `yaml:"gl,omitempty" json:"gl,omitempty"`
}

type LibvirtGraphicsListen

type LibvirtGraphicsListen struct {
	Type    string `yaml:"type,omitempty" json:"type,omitempty"`
	Address string `yaml:"address,omitempty" json:"address,omitempty"`
	Network string `yaml:"network,omitempty" json:"network,omitempty"`
	Socket  string `yaml:"socket,omitempty" json:"socket,omitempty"`
}

--------------------------------------------------------------------------- LibvirtGraphicsListen / LibvirtGraphicsListeners — #LibvirtListen (libvirt_yaml_listen.go). scalar | map | list of listeners. ---------------------------------------------------------------------------

type LibvirtGraphicsListeners

type LibvirtGraphicsListeners []LibvirtGraphicsListen

LibvirtGraphicsListeners is the YAML-shaped list of listeners for one <graphics> element.

func (LibvirtGraphicsListeners) First

First returns the first listener, or the zero value if empty.

type LibvirtHostdev

type LibvirtHostdev struct {
	Type    string            `yaml:"type" json:"type"`
	Mode    string            `yaml:"mode,omitempty" json:"mode,omitempty"`
	Managed string            `yaml:"managed,omitempty" json:"managed,omitempty"`
	Source  map[string]string `yaml:"source" json:"source"`
	ROM     map[string]string `yaml:"rom,omitempty" json:"rom,omitempty"`
	Driver  map[string]string `yaml:"driver,omitempty" json:"driver,omitempty"`
}

--------------------------------------------------------------------------- LibvirtHostdev — #LibvirtHostdev (libvirt_yaml.go). The `if type=="pci"` source-redefine degrades the generated def to `any`. ---------------------------------------------------------------------------

type LibvirtHub

type LibvirtHub struct {
	Type string `yaml:"type,omitempty" json:"type"`
}

type LibvirtHugepages

type LibvirtHugepages struct {
	Size string `yaml:"size,omitempty" json:"size,omitempty"`

	NodeSet string `yaml:"nodeset,omitempty" json:"nodeset,omitempty"`
}

type LibvirtHyperV

type LibvirtHyperV struct {
	Relaxed string `yaml:"relaxed,omitempty" json:"relaxed,omitempty"`

	VAPIC string `yaml:"vapic,omitempty" json:"vapic,omitempty"`

	Spinlocks *LibvirtSpinlocks `yaml:"spinlocks,omitempty" json:"spinlocks,omitempty"`

	VPIndex string `yaml:"vpindex,omitempty" json:"vpindex,omitempty"`

	Runtime string `yaml:"runtime,omitempty" json:"runtime,omitempty"`

	Synic string `yaml:"synic,omitempty" json:"synic,omitempty"`

	STimer string `yaml:"stimer,omitempty" json:"stimer,omitempty"`

	Reset string `yaml:"reset,omitempty" json:"reset,omitempty"`

	VendorID *LibvirtVendorID `yaml:"vendor_id,omitempty" json:"vendor_id,omitempty"`

	Frequencies string `yaml:"frequencies,omitempty" json:"frequencies,omitempty"`

	Reenlightenment string `yaml:"reenlightenment,omitempty" json:"reenlightenment,omitempty"`

	TLBFlush string `yaml:"tlbflush,omitempty" json:"tlbflush,omitempty"`

	IPI string `yaml:"ipi,omitempty" json:"ipi,omitempty"`

	EVMCS string `yaml:"evmcs,omitempty" json:"evmcs,omitempty"`
}

HyperV enlightenment toggles — all "on"/"off"-ish strings; no Go validator, kept plain string to avoid false-rejecting valid libvirt values.

type LibvirtIOMMU

type LibvirtIOMMU struct {
	Model string `yaml:"model,omitempty" json:"model"`

	Driver map[string]string `yaml:"driver,omitempty" json:"driver,omitempty"`
}

type LibvirtIOThreadPin

type LibvirtIOThreadPin struct {
	IOThread int `yaml:"iothread,omitempty" json:"iothread"`

	CPUSet string `yaml:"cpuset,omitempty" json:"cpuset"`
}

type LibvirtInput

type LibvirtInput struct {
	Type string `yaml:"type,omitempty" json:"type"`

	Bus string `yaml:"bus,omitempty" json:"bus,omitempty"`
}

type LibvirtInterface

type LibvirtInterface struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Source map[string]string `yaml:"source,omitempty" json:"source,omitempty"`

	Model string `yaml:"model,omitempty" json:"model,omitempty"`

	MAC string `yaml:"mac,omitempty" json:"mac,omitempty"`

	MTU int `yaml:"mtu,omitempty" json:"mtu,omitempty"`

	Driver map[string]string `yaml:"driver,omitempty" json:"driver,omitempty"`

	Boot int `yaml:"boot,omitempty" json:"boot,omitempty"`

	PortForwards []LibvirtPortForward `yaml:"port_forwards,omitempty" json:"port_forwards,omitempty"`
}

type LibvirtKVM

type LibvirtKVM struct {
	Hidden string `yaml:"hidden,omitempty" json:"hidden,omitempty"`

	HintDedicated string `yaml:"hint_dedicated,omitempty" json:"hint_dedicated,omitempty"`

	PollControl string `yaml:"poll_control,omitempty" json:"poll_control,omitempty"`

	PVIPI string `yaml:"pv_ipi,omitempty" json:"pv_ipi,omitempty"`

	DirtyRingSize int `yaml:"dirty_ring_size,omitempty" json:"dirty_ring_size,omitempty"`
}

type LibvirtLaunchSecurity

type LibvirtLaunchSecurity struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	CBitPos int `yaml:"cbitpos,omitempty" json:"cbitpos,omitempty"`

	ReducedPhysBits int `yaml:"reduced_phys_bits,omitempty" json:"reduced_phys_bits,omitempty"`

	Policy string `yaml:"policy,omitempty" json:"policy,omitempty"`

	DhCert string `yaml:"dh_cert,omitempty" json:"dh_cert,omitempty"`

	Session string `yaml:"session,omitempty" json:"session,omitempty"`

	KernelHashes string `yaml:"kernel_hashes,omitempty" json:"kernel_hashes,omitempty"`
}

type LibvirtListenOne

type LibvirtListenOne struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Address string `yaml:"address,omitempty" json:"address,omitempty"`

	Network string `yaml:"network,omitempty" json:"network,omitempty"`

	Socket string `yaml:"socket,omitempty" json:"socket,omitempty"`
}

type LibvirtMemBalloon

type LibvirtMemBalloon struct {
	Model string `yaml:"model,omitempty" json:"model"`

	Autodeflate string `yaml:"autodeflate,omitempty" json:"autodeflate,omitempty"`

	Stats map[string]int64 `yaml:"stats,omitempty" json:"stats,omitempty"`
}

type LibvirtMemTune

type LibvirtMemTune struct {
	HardLimit string `yaml:"hard_limit,omitempty" json:"hard_limit,omitempty"`

	SoftLimit string `yaml:"soft_limit,omitempty" json:"soft_limit,omitempty"`

	SwapHardLimit string `yaml:"swap_hard_limit,omitempty" json:"swap_hard_limit,omitempty"`

	MinGuarantee string `yaml:"min_guarantee,omitempty" json:"min_guarantee,omitempty"`
}

type LibvirtMemnode

type LibvirtMemnode struct {
	CellID int `yaml:"cellid,omitempty" json:"cellid,omitempty"`

	Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`

	Nodeset string `yaml:"nodeset,omitempty" json:"nodeset,omitempty"`
}

type LibvirtMemoryBacking

type LibvirtMemoryBacking struct {
	Hugepages *LibvirtHugepages `yaml:"hugepages,omitempty" json:"hugepages,omitempty"`

	NoSharepages *bool `yaml:"nosharepages,omitempty" json:"nosharepages,omitempty"`

	Locked *bool `yaml:"locked,omitempty" json:"locked,omitempty"`

	Source string `yaml:"source,omitempty" json:"source,omitempty"`

	Access string `yaml:"access,omitempty" json:"access,omitempty"`

	Allocation string `yaml:"allocation,omitempty" json:"allocation,omitempty"`

	Discard *bool `yaml:"discard,omitempty" json:"discard,omitempty"`
}

type LibvirtNUMACell

type LibvirtNUMACell struct {
	ID int `yaml:"id,omitempty" json:"id,omitempty"`

	CPUs string `yaml:"cpus,omitempty" json:"cpus,omitempty"`

	Memory string `yaml:"memory,omitempty" json:"memory,omitempty"`

	Unit string `yaml:"unit,omitempty" json:"unit,omitempty"`

	MemAccess string `yaml:"memaccess,omitempty" json:"memaccess,omitempty"`
}

type LibvirtNUMAMemory

type LibvirtNUMAMemory struct {
	Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`

	Nodeset string `yaml:"nodeset,omitempty" json:"nodeset,omitempty"`

	Placement string `yaml:"placement,omitempty" json:"placement,omitempty"`
}

type LibvirtNUMATune

type LibvirtNUMATune struct {
	Memory *LibvirtNUMAMemory `yaml:"memory,omitempty" json:"memory,omitempty"`

	MemNodes []LibvirtMemnode `yaml:"memnodes,omitempty" json:"memnodes,omitempty"`
}

type LibvirtPCIHex

type LibvirtPCIHex string

PCI source address component: 0x-hex OR bare decimal (hexUintPtr accepts both).

type LibvirtPanic

type LibvirtPanic struct {
	Model string `yaml:"model,omitempty" json:"model,omitempty"`

	Address map[string]string `yaml:"address,omitempty" json:"address,omitempty"`
}

type LibvirtParallel

type LibvirtParallel struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Source map[string]string `yaml:"source,omitempty" json:"source,omitempty"`

	Target map[string]string `yaml:"target,omitempty" json:"target,omitempty"`
}

type LibvirtPortForward

type LibvirtPortForward struct {
	Proto string `yaml:"proto,omitempty" json:"proto,omitempty"`

	Start int `yaml:"start,omitempty" json:"start"`

	To int `yaml:"to,omitempty" json:"to,omitempty"`
}

type LibvirtRNG

type LibvirtRNG struct {
	Model string `yaml:"model,omitempty" json:"model,omitempty"`

	Backend string `yaml:"backend,omitempty" json:"backend,omitempty"`

	Rate map[string]string `yaml:"rate,omitempty" json:"rate,omitempty"`
}

type LibvirtRedirDev

type LibvirtRedirDev struct {
	Bus string `yaml:"bus,omitempty" json:"bus,omitempty"`

	Type string `yaml:"type,omitempty" json:"type,omitempty"`
}

type LibvirtResource

type LibvirtResource struct {
	Partition string `yaml:"partition,omitempty" json:"partition,omitempty"`

	FibreChannel map[string]string `yaml:"fibrechannel,omitempty" json:"fibrechannel,omitempty"`
}

type LibvirtSecLabel

type LibvirtSecLabel struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Model string `yaml:"model,omitempty" json:"model,omitempty"`

	Relabel string `yaml:"relabel,omitempty" json:"relabel,omitempty"`

	Label string `yaml:"label,omitempty" json:"label,omitempty"`

	BaseLabel string `yaml:"baselabel,omitempty" json:"baselabel,omitempty"`

	ImageLabel string `yaml:"imagelabel,omitempty" json:"imagelabel,omitempty"`
}

type LibvirtSerial

type LibvirtSerial struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Source map[string]string `yaml:"source,omitempty" json:"source,omitempty"`

	Target map[string]string `yaml:"target,omitempty" json:"target,omitempty"`
}

type LibvirtShmem

type LibvirtShmem struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Role string `yaml:"role,omitempty" json:"role,omitempty"`

	Model map[string]string `yaml:"model,omitempty" json:"model,omitempty"`

	Size string `yaml:"size,omitempty" json:"size,omitempty"`

	Server map[string]string `yaml:"server,omitempty" json:"server,omitempty"`
}

type LibvirtSmartcard

type LibvirtSmartcard struct {
	Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`

	Type string `yaml:"type,omitempty" json:"type,omitempty"`
}

type LibvirtSound

type LibvirtSound struct {
	Model string `yaml:"model,omitempty" json:"model"`
}

type LibvirtSpinlocks

type LibvirtSpinlocks struct {
	State string `yaml:"state,omitempty" json:"state,omitempty"`

	Retries int `yaml:"retries,omitempty" json:"retries,omitempty"`
}

type LibvirtSysInfo

type LibvirtSysInfo struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	BIOS map[string]string `yaml:"bios,omitempty" json:"bios,omitempty"`

	System map[string]string `yaml:"system,omitempty" json:"system,omitempty"`

	BaseBoard []map[string]string `yaml:"baseboard,omitempty" json:"baseboard,omitempty"`

	Chassis map[string]string `yaml:"chassis,omitempty" json:"chassis,omitempty"`

	Processor []map[string]string `yaml:"processor,omitempty" json:"processor,omitempty"`

	OEMStrings []string `yaml:"oem_strings,omitempty" json:"oem_strings,omitempty"`
}

type LibvirtTPM

type LibvirtTPM struct {
	Model string `yaml:"model,omitempty" json:"model,omitempty"`

	Backend map[string]string `yaml:"backend,omitempty" json:"backend,omitempty"`
}

type LibvirtTimer

type LibvirtTimer struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Present string `yaml:"present,omitempty" json:"present,omitempty"`

	Track string `yaml:"track,omitempty" json:"track,omitempty"`

	TickPolicy string `yaml:"tickpolicy,omitempty" json:"tickpolicy,omitempty"`

	Frequency int `yaml:"frequency,omitempty" json:"frequency,omitempty"`

	Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
}

type LibvirtUSB

type LibvirtUSB struct {
	Model string `yaml:"model,omitempty" json:"model,omitempty"`

	Port int `yaml:"port,omitempty" json:"port,omitempty"`
}

type LibvirtVCPUPin

type LibvirtVCPUPin struct {
	VCPU int `yaml:"vcpu,omitempty" json:"vcpu"`

	CPUSet string `yaml:"cpuset,omitempty" json:"cpuset"`
}

type LibvirtVendorID

type LibvirtVendorID struct {
	State string `yaml:"state,omitempty" json:"state,omitempty"`

	Value string `yaml:"value,omitempty" json:"value,omitempty"`
}

type LibvirtVideo

type LibvirtVideo struct {
	Model string `yaml:"model,omitempty" json:"model"`

	VRAM int `yaml:"vram,omitempty" json:"vram,omitempty"`

	Heads int `yaml:"heads,omitempty" json:"heads,omitempty"`

	Accel3D *bool `yaml:"accel3d,omitempty" json:"accel3d,omitempty"`

	Primary *bool `yaml:"primary,omitempty" json:"primary,omitempty"`
}

type LibvirtVsock

type LibvirtVsock struct {
	Model string `yaml:"model,omitempty" json:"model,omitempty"`

	CID map[string]string `yaml:"cid,omitempty" json:"cid,omitempty"`
}

type LibvirtWatchdog

type LibvirtWatchdog struct {
	Model string `yaml:"model,omitempty" json:"model"`

	Action string `yaml:"action,omitempty" json:"action,omitempty"`
}

type LifecycleOpts added in v0.2026186.239

type LifecycleOpts struct {
	DryRun               bool   `json:"dry_run,omitempty"`
	AllowRepoChanges     bool   `json:"allow_repo_changes,omitempty"`
	AllowRootTasks       bool   `json:"allow_root_tasks,omitempty"`
	WithServices         bool   `json:"with_services,omitempty"`
	AssumeYes            bool   `json:"assume_yes,omitempty"`
	Verify               bool   `json:"verify,omitempty"`
	Pull                 bool   `json:"pull,omitempty"`
	SkipIncompatible     bool   `json:"skip_incompatible,omitempty"`
	BuilderImageOverride string `json:"builder_image_override,omitempty"`
}

LifecycleOpts is the serializable subset of the host's EmitOpts shipped in a lifecycle Op's params. The two LIVE EmitOpts fields (ParentExec, ParentNode) cannot cross the []byte wire — they re-attach host-side via the reverse channel's live host-build inputs, never serialized.

type LifecyclePrepareInput added in v0.2026186.739

type LifecyclePrepareInput struct {
	Entity         string         `json:"entity"`                // the kind:vm entity name (node.From-resolved)
	VM             *VmSpec        `json:"vm,omitempty"`          // the resolved VmSpec (uf.VM[entity])
	SSHUser        string         `json:"ssh_user"`              // resolveVmSshUser(spec)
	SSHPort        int            `json:"ssh_port"`              // resolveVmSshPort(spec, entity) — auto-alloc + persisted-port idempotency
	Alias          string         `json:"alias"`                 // VmSshAlias(entity)
	SSHKeyPath     string         `json:"ssh_key_path"`          // <stateDir>/id_ed25519
	KnownHostsPath string         `json:"known_hosts_path"`      // <stateDir>/known_hosts
	StateDir       string         `json:"state_dir"`             // ~/.local/share/charly/vm/charly-<entity>
	PriorState     *VmDeployState `json:"prior_state,omitempty"` // the persisted VmDeployState (nil on first apply)
}

LifecyclePrepareInput is the host-resolved DATA a vm substrate's OpPrepareVenue needs but cannot derive itself (the plugin cannot LoadUnified — it has no project): the resolved kind:vm entity, its ssh coordinates, and the prior runtime state. The host ships it on the lifecycle Op's params under the "prepare" key (the same generic threading the proxy uses for image/version), so PrepareVenue is NOT a hollow forward — the plugin does the full venue lifecycle (ssh-config, auto-boot, the guest waits, charly delivery, nested pods) itself over generic seams, consuming only this resolved data.

type Local

type Local struct {
	// Ordered candy stack applied to the host. Required (empty list permitted —
	// a staged name-reservation stub; the loader warns, not errors).
	Candy []CandyRef `yaml:"candy,omitempty" json:"candy"`

	InstallOpts *InstallOpts `yaml:"install_opts,omitempty" json:"install_opts,omitempty"`

	Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`

	Description string `yaml:"description,omitempty" json:"description,omitempty"`

	Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`
}

type LocalPkg

type LocalPkg struct {
	PkgGlob string `yaml:"pkg_glob,omitempty" json:"pkg_glob"`

	SourceSentinel string `yaml:"source_sentinel,omitempty" json:"source_sentinel"`

	BuildTemplate string `yaml:"build_template,omitempty" json:"build_template"`

	InstallTemplate string `yaml:"install_template,omitempty" json:"install_template"`

	Probe string `yaml:"probe,omitempty" json:"probe"`

	DepBuilder string `yaml:"dep_builder,omitempty" json:"dep_builder,omitempty"`

	DownloadTemplate string `yaml:"download_template,omitempty" json:"download_template,omitempty"`
}

type LocalSpec

type LocalSpec = Local

--- top-level entity types ---

type MCPServerYAML

type MCPServerYAML = CandyMCPProvide

--- candy sub-types ---

type MatchOpMap

type MatchOpMap map[string]any

Matcher operators (validMatcherOps, validate_check.go). A matcher is a scalar (implicit equals), or a single-operator map; #MatcherList accepts a single matcher OR a list. (The base no longer carries a contains-default list def — that shape left with the `file` verb's `contains` field and is now reproduced standalone in the file plugin's #FileContains, decoded with the substring default via decodeContainsList; no base #Op field uses it anymore.)

type Matcher

type Matcher struct {
	Op    string `json:"op"`
	Value any    `json:"value,omitempty"`
}

Matcher is a goss-style matcher: a scalar (implicit equality) or an operator map ({equals: X}, {contains: […]}, {matches: R}, {lt: N}, …).

func (Matcher) MarshalJSON

func (m Matcher) MarshalJSON() ([]byte, error)

MarshalJSON makes the JSON write path the exact inverse of UnmarshalJSON. A ZERO (absent) matcher — Op=="" — marshals to `null`, which UnmarshalJSON maps back to the zero value; without this, the default struct marshal emitted `{"op":""}`, which UnmarshalJSON's single-key-map branch misread as Op="op" (an asymmetry that corrupted every zero matcher round-tripped through JSON — e.g. the all-zero check-matcher fields of an install Op carried in the per-step IR, or a baked-label Plan step). A NON-zero matcher marshals to the SAME canonical `{"op":...,"value":...}` form the default marshaler produced, so real matchers (and existing baked labels) are byte-unchanged.

func (Matcher) MarshalYAML

func (m Matcher) MarshalYAML() (any, error)

MarshalYAML emits the canonical operator-map form so round-tripping is stable.

func (*Matcher) UnmarshalJSON

func (m *Matcher) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts the canonical `{"op":"...","value":...}` form, an operator-map (`{"equals": X}`), a bare scalar (`"PONG"` → equals), and `null` / empty (→ the zero matcher, the inverse of MarshalJSON's zero case).

type MatcherList

type MatcherList []Matcher

MatcherList lets users write scalar/single-map shorthand where a list of Matchers is expected. `stdout: PONG` and `stdout: [PONG]` decode identically.

func (*MatcherList) UnmarshalJSON

func (ml *MatcherList) UnmarshalJSON(data []byte) error

UnmarshalJSON mirrors the scalar-shorthand behavior for the JSON read path.

type MergeConfig

type MergeConfig = BoxMerge

--- box / deploy sub-types ---

type Op

type Op struct {
	// --- verb discriminators (exactly one set; Go Kind() enforces) ---
	Mkdir string `yaml:"mkdir,omitempty" json:"mkdir,omitempty"`

	Copy string `yaml:"copy,omitempty" json:"copy,omitempty"`

	Write string `yaml:"write,omitempty" json:"write,omitempty"`

	Link string `yaml:"link,omitempty" json:"link,omitempty"`

	Download string `yaml:"download,omitempty" json:"download,omitempty"`

	Setcap string `yaml:"setcap,omitempty" json:"setcap,omitempty"`

	Build string `yaml:"build,omitempty" json:"build,omitempty"`

	// plugin — the generic PLUGIN-VERB discriminator, INTERNAL-ONLY: the
	// parse-time desugar rewrites every authored `<word>: <input>` sugar key
	// into this plugin/plugin_input pair, and authoring plugin:/plugin_input:
	// directly in a step is a hard load error (run: charly migrate). The pair
	// stays on #Op because it is the wire/label form (the desugared tree this
	// def validates, the ai.opencharly.description JSON) and the Go dispatch
	// surface (providerRegistry.ResolveVerb). plugin_input is validated by the
	// PLUGIN's own served CUE schema (not base #Op).
	Plugin string `yaml:"plugin,omitempty" json:"plugin,omitempty"`

	// --- shared modifiers ---
	ID string `yaml:"id,omitempty" json:"id,omitempty"`

	Description string `yaml:"description,omitempty" json:"description,omitempty"`

	Skip bool `yaml:"skip,omitempty" json:"skip,omitempty"`

	Timeout Duration `yaml:"timeout,omitempty" json:"timeout,omitempty"`

	// command — INTERNAL-ONLY rehydration target (never authored): the `command`
	// plugin verb's INSTALL-EMIT copies plugin_input.command here for emitCmd
	// (build) / renderOpCommand (deploy). Authored `command:` on a step IS the
	// command plugin's sugar key (scalar = the primary shorthand), consumed by
	// the parse-time desugar before this def validates; the wl/libvirt argv
	// moved into those plugins' own input defs.
	Command string `yaml:"command,omitempty" json:"command,omitempty"`

	Context []Context `yaml:"context,omitempty" json:"context,omitempty"`

	// `pod:` (per-step container venue) is RETIRED — a step's execution venue is
	// derived ENTIRELY from its position in the bundle tree (flattenBundleVenues
	// → Op.venue, yaml:"-"). Authoring it is a closed-schema rejection (run:
	// charly migrate).
	DependsOn []string `yaml:"depends_on,omitempty" json:"depends_on,omitempty"`

	// plugin_input — generic params for a `plugin:` verb. Opaque to base #Op
	// (accepts any shape); the plugin's own spliced CUE schema validates it.
	PluginInput map[string]any `yaml:"plugin_input,omitempty" json:"plugin_input,omitempty"`

	// --- install/build modifiers ---
	RunAs string `yaml:"run_as,omitempty" json:"run_as,omitempty"`

	To string `yaml:"to,omitempty" json:"to,omitempty"`

	Target string `yaml:"target,omitempty" json:"target,omitempty"`

	Caps string `yaml:"caps,omitempty" json:"caps,omitempty"`

	Content string `yaml:"content,omitempty" json:"content,omitempty"`

	Extract string `yaml:"extract,omitempty" json:"extract,omitempty"`

	ExtractInclude []string `yaml:"extract_include,omitempty" json:"extract_include,omitempty"`

	StripComponents int `yaml:"strip_components,omitempty" json:"strip_components,omitempty"`

	Uninstall []string `yaml:"uninstall,omitempty" json:"uninstall,omitempty"`

	Comment string `yaml:"comment,omitempty" json:"comment,omitempty"`

	Cache []string `yaml:"cache,omitempty" json:"cache,omitempty"`

	Env StrMap `yaml:"env,omitempty" json:"env,omitempty"`

	// --- step modifiers ---
	Eventually Duration `yaml:"eventually,omitempty" json:"eventually,omitempty"`

	RetryInterval Duration `yaml:"retry_interval,omitempty" json:"retry_interval,omitempty"`

	// `on:` (cross-member driver dispatch) is RETIRED — a step that drives a
	// peer/driver is authored as a step CHILD of that member node; its venue is
	// derived from position (flattenBundleVenues → Op.venue). Authoring `on:` is
	// a closed-schema rejection (run: charly migrate).
	Tag []string `yaml:"tag,omitempty" json:"tag,omitempty"`

	// Origin is populated at collection time (candy:<name>/box:<name>/deploy-*),
	// NEVER authored (Go yaml:"-"), but it TRAVELS in the ai.opencharly.description
	// OCI-label JSON (Go json:"origin") — so the generated spec.Op MUST carry it
	// for a faithful drop-in (do NOT @go(-) it). gengotypes emits
	// `yaml:"origin,omitempty" json:"origin,omitempty"`, matching the hand tag.
	Origin string `yaml:"origin,omitempty" json:"origin,omitempty"`

	// venue + intent_do are RUNTIME-DERIVED, never authored: venue is stamped
	// from a step's bundle-tree position (flattenBundleVenues), intent_do from
	// the enclosing Step's keyword (run→act / check→assert / agent→instruct).
	// They are generated onto the Go struct (the check runner persists them on
	// the Op-by-value passed to runOne / EffectiveDo), but the #Step authoring
	// arms forbid them (`venue?: _|_, intent_do?: _|_`) so authoring either is a
	// closed-schema rejection — exactly the contract the retired `pod:`/`on:`
	// fields enforced. yaml:"-" (never read from YAML); json omits them when
	// empty (the bake-time state, so they never leak into the description label).
	Venue string `yaml:"venue,omitempty" json:"venue,omitempty"`

	IntentDo string `yaml:"intent_do,omitempty" json:"intent_do,omitempty"`

	// --- file/copy/write SHARED modifier ---
	// `mode` is the SHARED octal-permission modifier: the copy/write install verbs read
	// Op.Mode at deploy (the external local/vm deploy walk via kit.ParseTaskMode),
	// so it STAYS in #Op. The file-EXCLUSIVE fields (file/exists/owner/group_of/filetype/
	// contains/sha256) LEFT #Op — they are read ONLY by the `file` plugin verb and now live
	// in its #FileInput (candy/plugin-file, with the contains-default semantic
	// reproduced via the candy's decodeContainsList). The state-provision migrator MOVES `mode` into a
	// file step's plugin_input while LEAVING it here for copy/write (the shared-companion
	// pattern, like gid between unix_group and user).
	Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`

	// exclude_distro — a SHARED step-level skip filter read by the generic runOne for
	// EVERY verb (skip the step when any image distro tag intersects the list), NOT a
	// package-exclusive field, so it STAYS on #Op. The `package`-exclusive fields
	// (installed/version/package_map) MOVED into #PackageInput when `package` extracted.
	ExcludeDistros []string `yaml:"exclude_distro,omitempty" json:"exclude_distro,omitempty"`

	// --- shared assertion matchers (SHARED via matchAll: the command/http probe
	// plugins + every live-verb plugin assert exit_status/stdout/stderr off the
	// marshalled step Op, so they STAY in #Op; verb-exclusive request/input
	// fields live in each plugin's input def) ---
	ExitStatus *int `yaml:"exit_status,omitempty" json:"exit_status,omitempty"`

	Stdout MatcherList `yaml:"stdout,omitempty" json:"stdout,omitempty"`

	Stderr MatcherList `yaml:"stderr,omitempty" json:"stderr,omitempty"`
}

#Op is the unified operation vocabulary (Go: charly/checkspec.go Op) AFTER the parse-time desugar: the builtin install verbs + the genuinely SHARED step modifiers + the internal plugin/plugin_input pair every plugin-verb sugar key desugars into. CLOSED: an unknown key on the desugared step is a typo (a plugin word never reaches this def — the desugar consumed it). Per-verb fields live in each plugin's own input def, served over Describe.

func (*Op) Kind

func (c *Op) Kind() (string, error)

Kind returns the single verb set on the Op, or an error when zero or multiple are set (the exactly-one-verb rule).

func (*Op) PluginInputStr

func (c *Op) PluginInputStr(verb string) string

PluginInputStr returns the string value of plugin_input[verb] when this Op is a `plugin: <verb>` op carrying that key — "" otherwise. The ONE home (R3) for the host check runner (checkrun.go) AND the k8sgen plugin's probe builder (candy/plugin-k8sgen), both of which read a migrated `plugin: http`/`addr`/`file`/`command` check subject.

func (*Op) StringFields

func (c *Op) StringFields() []*string

StringFields returns pointers to every string-valued modifier field, for the in-place ${VAR} expansion driven by ExpandVars (free function in main).

func (*Op) VerbsSet

func (c *Op) VerbsSet() []string

type OverlayBuildReply

type OverlayBuildReply struct {
	OverlayRef string `json:"overlay_ref,omitempty"`
	BaseImage  string `json:"base_image,omitempty"`
	DeployName string `json:"deploy_name,omitempty"`
	Error      string `json:"error,omitempty"`
}

OverlayBuildReply is what the "overlay" host-builder returns: the built overlay image ref (== BaseImage when there was no add_candy overlay to synthesize), the resolved base image ref, and the flattened deploy name. The caller (PrepareVenue) uses these to print the start hint and persist the concrete overlay ref (saveDeployState) so config/start deploy exactly this overlay. A build FAILURE rides Error (the reply-error convention, like BuildReply).

type OverlayBuildRequest

type OverlayBuildRequest struct {
	Dir              string `json:"dir,omitempty"`                // project dir (build-context root) the host reconstructs config from
	DeployName       string `json:"deploy_name,omitempty"`        // the raw deploy name (dotted for a nested pod; flattened engine-side)
	Image            string `json:"image,omitempty"`              // the base box the overlay inherits FROM (node.Image; "" → DeployName)
	Version          string `json:"version,omitempty"`            // the base image CalVer pin (node.Version; "" → newest-local)
	DryRun           bool   `json:"dry_run,omitempty"`            //
	AssumeYes        bool   `json:"assume_yes,omitempty"`         //
	AllowRepoChanges bool   `json:"allow_repo_changes,omitempty"` //
	AllowRootTasks   bool   `json:"allow_root_tasks,omitempty"`   //
	WithServices     bool   `json:"with_services,omitempty"`      //
}

OverlayBuildRequest is the BUILD-ENGINE DISPATCH envelope for the pod-overlay build — the F10 "overlay" host-builder, the pod-substrate sibling of BuildRequest. It carries only the SERIALIZABLE scalars the host engine cannot reconstruct from Dir; everything heavy the engine reads (Config / ResolvedBox / Candy / DistroDef) is reconstructed HOST-SIDE from Dir via NewGenerator, exactly as runBoxBuild / the prior inline PrepareVenue body did.

The overlay build's LIVE inputs — the deployment's compiled InstallPlans and, for a nested pod-in-pod overlay, the parent venue executor + node — do NOT ride this envelope: a live DeployExecutor is not serializable. The overlay build is dispatched IN-PROCESS host-side by podSubstrateLifecycle.PrepareVenue (a direct hostBuilders lookup, no gRPC hop), so those live inputs ride the ctx instead (the SAME pattern sdk.ContextWithExecutor uses to thread a live executor across the placement-invisible reverse channel). See package main overlayBuildInputs.

type PackageItem

type PackageItem struct {
	Name        string `yaml:"name" json:"name"`
	Description string `yaml:"description,omitempty" json:"description,omitempty"`
}

--------------------------------------------------------------------------- PackageItem — #PackageItem (calamares_types.go). Bare scalar XOR object form; the bare-scalar shorthand is canonicalized to {name} by the loader normalizer. ---------------------------------------------------------------------------

type Pacstrap

type Pacstrap struct {
	BasePackages []string `yaml:"base_package,omitempty" json:"base_package,omitempty"`

	KeyringInitCmd string `yaml:"keyring_init_cmd,omitempty" json:"keyring_init_cmd,omitempty"`

	MirrorlistURL string `yaml:"mirrorlist_url,omitempty" json:"mirrorlist_url,omitempty"`

	ExtraRepos []PacstrapRepo `yaml:"extra_repo,omitempty" json:"extra_repo,omitempty"`

	RuntimePacmanConf string `yaml:"runtime_pacman_conf,omitempty" json:"runtime_pacman_conf,omitempty"`
}

type PacstrapRepo

type PacstrapRepo struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Server string `yaml:"server,omitempty" json:"server"`

	SigLevel string `yaml:"siglevel,omitempty" json:"siglevel,omitempty"`
}

type PhaseSet

type PhaseSet struct {
	Prepare *PhaseTemplates `yaml:"prepare,omitempty" json:"prepare,omitempty"`

	Install *PhaseTemplates `yaml:"install,omitempty" json:"install,omitempty"`

	Cleanup *PhaseTemplates `yaml:"cleanup,omitempty" json:"cleanup,omitempty"`
}

Three-phase template set: prepare → install → cleanup, each with a container (Containerfile) and host (shell) rendering. Template bodies are Go text/template — strings, never parsed here.

type PhaseTemplates

type PhaseTemplates struct {
	Container string `yaml:"container,omitempty" json:"container,omitempty"`

	Host string `yaml:"host,omitempty" json:"host,omitempty"`
}

type Plugin

type Plugin struct {
	// providers: the "<class>:<word>" reserved-word capabilities this plugin
	// serves (e.g. "verb:exampleprobe", "kind:my-thing"). Each is registered into
	// providerRegistry — built-in (init()) or out-of-tree (gRPC).
	Providers []PluginCapability `yaml:"providers,omitempty" json:"providers"`

	// source: "builtin" (Go compiled into the charly binary, init()-registered) OR
	// a git ref (github.com/org/repo[/sub][@tag]) fetched via the @github resolver +
	// built into a provider binary. Default builtin.
	Source string `yaml:"source,omitempty" json:"source"`

	// primary: verb word → the input field its scalar sugar shorthand targets
	// (`file: /x` → plugin_input: {<primary>: "/x"}). Declared in the MANIFEST so
	// the byte-gated prescan registers it BEFORE the out-of-process provider
	// connects (the parse-time desugar needs it pre-parse); the served
	// ProvidedCapability.Primary mirrors it for the compiled-in placement.
	Primary map[string]string `yaml:"primary,omitempty" json:"primary,omitempty"`
}

#Plugin — the candy's plugin declaration. Its presence makes the candy a plugin (Go: charly/checkspec.go via the generated Candy.Plugin field, consumed by plugin_loader.go). CLOSED.

type PluginCapability

type PluginCapability string

#PluginCapability — a "<class>:<word>" capability string. class ∈ the closed ProviderClass set; word is lowercase-hyphenated.

type Pod

type Pod struct {
	// References a kind:box (bare lowercase-hyphenated name or remote ref).
	// Optional: the Go field has no non-empty validator.
	Box CandyRef `yaml:"box,omitempty" json:"box,omitempty"`

	// Each entry is a SidecarConfig wrapper: a single-key map of named
	// sidecar templates (PodSpec.Sidecar []SidecarConfig).
	Sidecar []PodSidecar `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`

	Secret []DeploySecret `yaml:"secret,omitempty" json:"secret,omitempty"`

	EnvDefaults map[string]string `yaml:"env_default,omitempty" json:"env_default,omitempty"`

	Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`
}

type PodSidecar

type PodSidecar struct {
	Sidecar map[string]Sidecar `yaml:"sidecar,omitempty" json:"sidecar"`
}

#PodSidecar mirrors Go SidecarConfig: a single-key wrapper around a map of named sidecar templates. CLOSED (the struct has exactly one field).

type PodSpec

type PodSpec = Pod

--- top-level entity types ---

type PortPin

type PortPin = string

--- named-pattern scalars (string) ---

type PortScope

type PortScope struct {
	All     bool
	Ports   []int
	PortMap map[int]string // port → hostname (cloudflare only)
}

PortScope handles three YAML forms for public/private port specification: "all" → All; [443, 8443] → Ports; {18789: "host"} → PortMap.

func (PortScope) IsZero

func (p PortScope) IsZero() bool

func (PortScope) MarshalJSON

func (p PortScope) MarshalJSON() ([]byte, error)

func (PortScope) MarshalYAML

func (p PortScope) MarshalYAML() (any, error)

func (*PortScope) UnmarshalJSON

func (p *PortScope) UnmarshalJSON(data []byte) error

func (*PortScope) UnmarshalYAML

func (p *PortScope) UnmarshalYAML(value *yaml.Node) error

type PortSpec

type PortSpec struct {
	Port     int
	Protocol string
}

PortSpec is the canonical {port, protocol} form of a candy `port:` entry. Source: charly/layers.go PortSpec (no struct tags — yaml.v3 lowercases the field name).

type PostTeardownReply added in v0.2026186.239

type PostTeardownReply struct {
	RemoveEntries []string `json:"remove_entries,omitempty"`
}

PostTeardownReply is the OpPostTeardown reply: the host removes each named charly.yml deploy-entry key AFTER the plugin's teardown (the plugin cannot mutate the host config — vm ships its resolved entry keys here; pod ships none).

type PreemptLease

type PreemptLease struct {
	Claimant  string            `yaml:"claimant" json:"claimant"`
	Claim     HolderAddr        `yaml:"claim" json:"claim"` // the claimant DEPLOYMENT addr (persistent-lease liveness)
	Tokens    []string          `yaml:"tokens" json:"tokens"`
	Shared    bool              `yaml:"shared,omitempty" json:"shared,omitempty"` // true = refcounted SHARED (pods); false = EXCLUSIVE (VM)
	Mode      string            `yaml:"mode,omitempty" json:"mode,omitempty"`     // driver MODE: "nvidia" (shared) | "vfio" (exclusive); "" = legacy/none
	Transient bool              `yaml:"transient" json:"transient"`               // check-bed claims auto-release; persistent claims don't
	Preempted []PreemptedHolder `yaml:"preempted" json:"preempted"`               // holders/pods THIS claim stopped + must restore on release
	Created   string            `yaml:"created" json:"created"`                   // RFC3339 UTC
	// OwnerPID/OwnerStart identify the OUTERMOST process that created the lease —
	// the liveness signal a concurrent charly process's reconcile uses (leaseLive).
	OwnerPID   int    `yaml:"owner_pid,omitempty" json:"owner_pid,omitempty"`
	OwnerStart string `yaml:"owner_start,omitempty" json:"owner_start,omitempty"`
}

PreemptLease is one active resource claim — exclusive (a VM with sole use) OR shared (a refcounted pod claim; many coexist on one token).

type PreemptLedger

type PreemptLedger struct {
	Leases []PreemptLease `yaml:"leases" json:"leases"`
}

PreemptLedger is the on-disk lease set (~/.local/share/charly/preemption/leases.yml).

type PreemptedHolder

type PreemptedHolder struct {
	Addr    HolderAddr `yaml:"addr" json:"addr"`
	Holds   []string   `yaml:"holds" json:"holds"`
	Restore string     `yaml:"restore" json:"restore"` // always | on-success
}

PreemptedHolder records one holder a lease stopped, its declared exclusive tokens, and its restore policy — so ReleaseClaimant/reconcile restart exactly what was stopped.

type PreemptibleConfig

type PreemptibleConfig struct {
	Holds   []string `yaml:"holds,omitempty" json:"holds,omitempty"`
	Stop    string   `yaml:"stop,omitempty" json:"stop,omitempty"`
	Restore string   `yaml:"restore,omitempty" json:"restore,omitempty"`
}

PreemptibleConfig is the HOLDER side of the resource-arbitration axis.

type PrepareVenueReply added in v0.2026186.239

type PrepareVenueReply struct {
	Venue VenueDescriptor `json:"venue"`
	State json.RawMessage `json:"state,omitempty"`
	Notes []string        `json:"notes,omitempty"`
}

PrepareVenueReply is the OpPrepareVenue reply. Venue is re-materialized host-side into a live DeployExecutor (the live executor never crosses the wire); State is an opaque deploy-entry patch the host persists (pod: {"ResolvedImage": ref}; vm: VmDeployState JSON); Notes are human-facing lines the host prints. Replaces the bare VenueDescriptor OpPrepareVenue reply.

type Readiness

type Readiness struct {
	PollIntervalLocal Duration `yaml:"poll_interval_local,omitempty" json:"poll_interval_local,omitempty"`

	PollIntervalRemote Duration `yaml:"poll_interval_remote,omitempty" json:"poll_interval_remote,omitempty"`

	PollIntervalHeavy Duration `yaml:"poll_interval_heavy,omitempty" json:"poll_interval_heavy,omitempty"`

	PerAttempt Duration `yaml:"per_attempt,omitempty" json:"per_attempt,omitempty"`

	PerAttemptHeavy Duration `yaml:"per_attempt_heavy,omitempty" json:"per_attempt_heavy,omitempty"`

	NoProgress Duration `yaml:"no_progress,omitempty" json:"no_progress,omitempty"`

	AbsoluteCap Duration `yaml:"absolute_cap,omitempty" json:"absolute_cap,omitempty"`

	StopGrace Duration `yaml:"stop_grace,omitempty" json:"stop_grace,omitempty"`
}

ReadinessConfig (config.go / readiness_config.go) — the `defaults.readiness:` bounds for the unified pollUntil readiness primitive (poll.go). CLOSED. Every field is a Go time.ParseDuration string; absent → the named fallback const in poll.go. Defaults-only (reuses the BoxConfig type), but modeled here per the #Box completeness invariant in this file's header. The Resolve()-time ordering invariants (interval <= no_progress <= absolute_cap; poll_interval_local <= stop_grace <= absolute_cap) are cross-field and stay in Go (validateOrdering).

type ReadinessConfig

type ReadinessConfig = Readiness

--- box / deploy sub-types ---

type RepoBlock

type RepoBlock = map[string]any

--- open passthrough map (alias so []RepoBlock IS []map[string]any) ---

type Resource

type Resource struct {
	Gpu *GpuSelector `yaml:"gpu,omitempty" json:"gpu,omitempty"`
}

func (*Resource) HasSelector

func (r *Resource) HasSelector() bool

HasSelector reports whether the resource carries a GPU selector.

type ResourceDef

type ResourceDef = Resource

--- top-level entity types ---

type RetentionReply added in v0.2026186.928

type RetentionReply struct {
	ImageRefs     []string `json:"image_refs,omitempty"`
	BuildDirs     []string `json:"build_dirs,omitempty"`
	CheckPaths    []string `json:"check_paths,omitempty"`
	KeepImages    int      `json:"keep_images,omitempty"`
	KeepCheckRuns int      `json:"keep_check_runs,omitempty"`
	Error         string   `json:"error,omitempty"`
}

RetentionReply is the "retention" HostBuild kind reply: the removed (or would-remove, under DryRun) image refs, build-candy dirs, and check-run paths, plus the effective retention counts, for the plugin to present. Error is a human-facing message on a non-recoverable failure.

type RetentionRequest added in v0.2026186.928

type RetentionRequest struct {
	Dir        string `json:"dir"`
	DryRun     bool   `json:"dry_run,omitempty"`
	Images     bool   `json:"images,omitempty"`
	Check      bool   `json:"check,omitempty"`
	Keep       int    `json:"keep,omitempty"`
	Invalidate string `json:"invalidate,omitempty"`
}

RetentionRequest is the "retention" HostBuild kind request: the plugin asks the host to run the shared prune engine host-side (the engine needs the core image inventory + label parsing that stays in core). Dir is the project directory (os.Getwd() in the plugin). Keep=0 means "use the resolved defaults"; Invalidate (non-empty) runs ONLY the targeted image-tag invalidation.

type ReverseOp

type ReverseOp struct {
	Kind    ReverseOpKind     `json:"kind"`
	Format  string            `json:"format,omitempty"`  // package format for package-remove (rpm/deb/pac)
	Targets []string          `json:"targets,omitempty"` // package names, file paths, env names, …
	Scope   Scope             `json:"scope,omitempty"`   // system vs user for disambiguation
	Extra   map[string]string `json:"extra,omitempty"`   // op-specific details (e.g. unit name, layer name, plugin-script body)

	// UninstallCmd is the rendered host-venue package-removal command for a
	// ReverseOpPackageRemove op, filled at record time from the format's
	// uninstall_template (the embedded build vocabulary, charly/charly.yml) by
	// fillReverseUninstallCmds — the deploy target has the DistroConfig at
	// install time, the teardown (which reads the persisted ledger) does not, so
	// the command is rendered up front and persisted. reverse_ops.go runs it
	// verbatim, so there is NO hardcoded per-format removal switch in the
	// teardown path.
	UninstallCmd string `json:"uninstall_cmd,omitempty"`
}

ReverseOp is a single teardown action. Serialized into the ledger so uninstall can reverse a deploy without re-reading the candy manifest.

type ReverseOpKind

type ReverseOpKind string

ReverseOpKind discriminates the kinds of teardown actions Reverse() produces. Ledger entries serialize these verbatim so a later `charly bundle del` can walk them without re-compiling the plan.

const (
	ReverseOpPackageRemove  ReverseOpKind = "package-remove"
	ReverseOpCargoUninstall ReverseOpKind = "cargo-uninstall"
	ReverseOpNpmUninstallG  ReverseOpKind = "npm-uninstall-g"
	ReverseOpPixiEnvRemove  ReverseOpKind = "pixi-env-remove"
	ReverseOpRmFileSystem   ReverseOpKind = "rm-file-system"
	ReverseOpRmFileUser     ReverseOpKind = "rm-file-user"
	ReverseOpRmDirRecursive ReverseOpKind = "rm-dir-recursive"
	ReverseOpServiceDisable ReverseOpKind = "service-disable"
	ReverseOpServiceRemove  ReverseOpKind = "service-remove"
	ReverseOpRemoveDropin   ReverseOpKind = "remove-dropin"
	ReverseOpRestoreEnabled ReverseOpKind = "restore-enabled"
	ReverseOpRemoveManaged  ReverseOpKind = "remove-managed-block"
	ReverseOpRemoveEnvdFile ReverseOpKind = "remove-envd-file"
	ReverseOpRemoveRepoFile ReverseOpKind = "remove-repo-file"
	ReverseOpCoprDisable    ReverseOpKind = "copr-disable"

	// ReverseOpPluginScript is the GENERIC recordable reverse op an external
	// (out-of-process) deploy/step/builder plugin returns: a shell script + its
	// scope, run verbatim at teardown via the ReverseExecutor (system → sudo,
	// user → no sudo). The script lives in Extra["script"]; Scope picks the
	// privilege. It preserves the record-and-replay invariant — only RECORDED
	// ops are replayed, never recomputed — without any new struct shape.
	ReverseOpPluginScript ReverseOpKind = "plugin-script"
)

type RouteYAML

type RouteYAML = CandyRoute

--- candy sub-types ---

type Scope

type Scope int

Scope classifies what kind of filesystem mutation a step makes. Steps are grouped by scope (and venue) when the host target batches into sudo vs user heredocs — mixing scopes in one batch would need per-command sudo. It is the integer-valued enum the ledger serializes (omitempty omits ScopeSystem=0), so the wire form is UNCHANGED by the move into spec.

const (
	// ScopeSystem mutates global host state: /etc, /usr, /var, systemd system
	// units, package DB. Requires sudo on host; emitted as USER root in the
	// Containerfile.
	ScopeSystem Scope = iota

	// ScopeUser mutates the invoking user's home or user-owned paths:
	// $HOME/.pixi, $HOME/.cargo, $HOME/.npm-global, $HOME/.local, systemd
	// user units, etc. No sudo needed on host; emitted as USER ${UID} in the
	// Containerfile.
	ScopeUser

	// ScopeUserProfile writes to the user's shell init surface:
	// ~/.bashrc / ~/.zshenv / fish conf.d + ~/.config/opencharly/env.d/.
	// Separate from ScopeUser because the host target has special handling
	// (managed blocks, shell detection) and the OCI target renders these as
	// ENV directives + path additions rather than file writes.
	ScopeUserProfile
)

func (Scope) String

func (s Scope) String() string

type SecretYAML

type SecretYAML = CandySecret

--- candy sub-types ---

type Security

type Security struct {
	Privileged bool `yaml:"privileged,omitempty" json:"privileged,omitempty"`

	CgroupNS string `yaml:"cgroupns,omitempty" json:"cgroupns,omitempty"`

	CapAdd []string `yaml:"cap_add,omitempty" json:"cap_add,omitempty"`

	Devices []string `yaml:"devices,omitempty" json:"devices,omitempty"`

	SecurityOpt []string `yaml:"security_opt,omitempty" json:"security_opt,omitempty"`

	IpcMode string `yaml:"ipc_mode,omitempty" json:"ipc_mode,omitempty"`

	ShmSize Size `yaml:"shm_size,omitempty" json:"shm_size,omitempty"`

	GroupAdd []string `yaml:"group_add,omitempty" json:"group_add,omitempty"`

	Mounts []string `yaml:"mount,omitempty" json:"mount,omitempty"`

	MemoryMax Size `yaml:"memory_max,omitempty" json:"memory_max,omitempty"`

	MemoryHigh Size `yaml:"memory_high,omitempty" json:"memory_high,omitempty"`

	MemorySwapMax Size `yaml:"memory_swap_max,omitempty" json:"memory_swap_max,omitempty"`

	Cpus string `yaml:"cpus,omitempty" json:"cpus,omitempty"`
}

--------------------------------------------------------------------------- Container security (SecurityConfig) — shared by box / candy / deploy / sidecar. ---------------------------------------------------------------------------

type SecurityConfig

type SecurityConfig = Security

--- box / deploy sub-types ---

type ServiceEntry

type ServiceEntry = CandyService

--- candy sub-types ---

type SettingsEntry added in v0.2026186.948

type SettingsEntry struct {
	Key    string `json:"key"`
	Value  string `json:"value"`
	Source string `json:"source"` // "env" | "config" | "default"
}

SettingsEntry is one resolved config key (the `charly settings list` row).

type SettingsReply added in v0.2026186.948

type SettingsReply struct {
	Value   string          `json:"value,omitempty"`
	Entries []SettingsEntry `json:"entries,omitempty"`
	Error   string          `json:"error,omitempty"`
}

SettingsReply is the "settings" HostBuild kind reply: Value for get/path, Entries for list; set/reset return neither. Error is a human-facing message on failure (e.g. an unknown config key).

type SettingsRequest added in v0.2026186.948

type SettingsRequest struct {
	Op    string `json:"op"`
	Key   string `json:"key,omitempty"`
	Value string `json:"value,omitempty"`
}

SettingsRequest is the "settings" HostBuild kind request: one config-subsystem op. Op ∈ {get, set, list, reset, path}. Key/Value carry the op's arguments (get/reset: Key; set: Key+Value; list/path: neither; reset with empty Key resets all).

type Shell

type Shell struct {
	Init string `yaml:"init,omitempty" json:"init,omitempty"`

	PathAppend []string `yaml:"path_append,omitempty" json:"path_append,omitempty"`

	Path string `yaml:"path,omitempty" json:"path,omitempty"`

	Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`

	Bash *ShellSpec `yaml:"bash,omitempty" json:"bash,omitempty"`

	Zsh *ShellSpec `yaml:"zsh,omitempty" json:"zsh,omitempty"`

	Fish *ShellSpec `yaml:"fish,omitempty" json:"fish,omitempty"`

	Sh *ShellSpec `yaml:"sh,omitempty" json:"sh,omitempty"`
}

--------------------------------------------------------------------------- Shell-rc config (ShellConfig/ShellSpec) — shared by box + candy. CLOSED: the Go UnmarshalYAML rejects any key outside the 4 intrinsic + 4 shell names. ---------------------------------------------------------------------------

func (*Shell) ByShell

func (s *Shell) ByShell() map[string]*ShellSpec

ByShell returns the per-shell overlay map (nil when none authored).

type ShellSpec

type ShellSpec struct {
	Init string `yaml:"init,omitempty" json:"init,omitempty"`

	PathAppend []string `yaml:"path_append,omitempty" json:"path_append,omitempty"`

	Path string `yaml:"path,omitempty" json:"path,omitempty"`
}

type Sidecar

type Sidecar struct {
	Description string `yaml:"description,omitempty" json:"description,omitempty"`

	Image string `yaml:"image,omitempty" json:"image,omitempty"`

	// env / parameter are map[string]string — values MUST be strings (quote
	// YAML bools/numbers). parameter "" is the "deploy must supply" sentinel.
	Env StrMap `yaml:"env,omitempty" json:"env,omitempty"`

	Parameter StrMap `yaml:"parameter,omitempty" json:"parameter,omitempty"`

	Secret []SidecarSecret `yaml:"secret,omitempty" json:"secret,omitempty"`

	Volume []SidecarVolume `yaml:"volume,omitempty" json:"volume,omitempty"`

	Security *Security `yaml:"security,omitempty" json:"security,omitempty"`
}

type SidecarDef

type SidecarDef = Sidecar

--- top-level entity types ---

type SidecarSecret

type SidecarSecret struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Env string `yaml:"env,omitempty" json:"env"`

	EnvFrom string `yaml:"env_from,omitempty" json:"env_from,omitempty"`

	Description string `yaml:"description,omitempty" json:"description,omitempty"`
}

type SidecarVolume

type SidecarVolume struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Path string `yaml:"path,omitempty" json:"path"`
}

type Size

type Size = string

--- named-pattern scalars (string) ---

type StandaloneLoad

type StandaloneLoad struct {
	Shape    string          `json:"shape"`              // "deploy" | "template" | "candy-image" | "candy-layer"
	Deploy   *Deploy         `json:"deploy,omitempty"`   // Shape=="deploy": the full pre-decoded BundleNode
	Template json.RawMessage `json:"template,omitempty"` // Shape=="template": the pre-decoded typed template value's JSON
	Box      *Box            `json:"box,omitempty"`      // Shape=="candy-image": the pre-decoded IMAGE (spec.Box)
	Candy    *Candy          `json:"candy,omitempty"`    // Shape=="candy-layer": the pre-decoded LAYER (spec.Candy)
}

StandaloneLoad carries a structural kind's host-pre-decoded canonical node. Shape names which fold the host performs on the plugin's echo:

  • "deploy" → Deploy (the full BundleNode) folds into uf.Bundle (C2-substrate);
  • "template" → Template (the per-substrate typed value's JSON, e.g. a spec.Vm) folds into the typed template map by kind (C2-substrate);
  • "candy-image" → Box (a full IMAGE, base:/from:) folds into uf.Box (C2-candy);
  • "candy-layer" → Candy (a LAYER fragment) folds into uf.Candy (C2-candy).

Exactly one of Deploy / Template / Box / Candy is set, matching Shape. Both candy shapes are pre-decoded HOST-SIDE by the core candyIsImage + buildCandy (the bootstrap-critical box⊻layer routing that STAYS core — the discovered-candy pre-check calls it directly), so the plugin is a pure ECHO exactly like substrate. RDD proved a canonical spec.Box / spec.Candy round-trips through JSON byte-faithfully.

type Step

type Step struct {
	Run        string `yaml:"run,omitempty"         json:"run,omitempty"`
	Check      string `yaml:"check,omitempty"       json:"check,omitempty"`
	AgentRun   string `yaml:"agent-run,omitempty"   json:"agent-run,omitempty"`
	AgentCheck string `yaml:"agent-check,omitempty" json:"agent-check,omitempty"`
	Include    string `yaml:"include,omitempty"     json:"include,omitempty"`

	Op `yaml:",inline" json:",inline"`
}

Step is a single plan step. Exactly one of Run/Check/AgentRun/AgentCheck/Include is non-empty (validated by StepKind()). The embedded Op (generated) is inline-promoted.

func (*Step) IsAgent

func (s *Step) IsAgent() bool

IsAgent reports whether the step is agent-graded (agent-run / agent-check).

func (*Step) IsInclude

func (s *Step) IsInclude() bool

IsInclude reports whether the step is an include: composition directive.

func (*Step) KeywordText

func (s *Step) KeywordText() string

KeywordText returns the populated keyword's prose regardless of which discriminator holds it.

func (*Step) Mutates

func (s *Step) Mutates() bool

Mutates reports whether the step changes system state (run / agent-run). `charly check live` (verify-only mode) skips mutating steps.

func (*Step) StepKind

func (s *Step) StepKind() (StepKeyword, error)

StepKind returns the step's intent keyword and an error if zero or multiple keyword discriminators are set.

type StepEmitRequest

type StepEmitRequest struct {
	Word    string          `json:"word"`
	Payload json.RawMessage `json:"payload,omitempty"`
	Distros []string        `json:"distros,omitempty"`
}

StepEmitRequest is the F-STEP-EMIT HostBuild envelope for a HOST-COUPLED external step kind's build-context fragment. A class:step plugin whose OpEmit needs the host build ENGINE (the DistroDef format templates, the Generator's task/builder rendering — the machinery a []byte can't carry across the process boundary) calls back Executor.HostBuild("step-emit", StepEmitRequest{…}) during its OpEmit; the host's registered "step-emit" host-builder dispatches by Word to a per-word emitter that renders the fragment IN-CORE and returns it as an EmitReply (the reply reuses EmitReply — R3). Word is the step reserved word; Payload is the step's opaque per-kind input (the SAME bytes the plugin received in op.Params); Distros carries the image's distro tags (BuildEnv). A PURE step never sends this — it returns its EmitReply.Fragment directly from OpEmit. The host's per-word emitter registry holds one renderer per relocated host-coupled step kind (C1.2 registered system-packages).

type StepKeyword

type StepKeyword string

StepKeyword is the intent discriminator on a Step.

const (
	KwRun        StepKeyword = "run"
	KwCheck      StepKeyword = "check"
	KwAgentRun   StepKeyword = "agent-run"
	KwAgentCheck StepKeyword = "agent-check"
	KwInclude    StepKeyword = "include"
)

type StrMap

type StrMap = map[string]string

StrMap is the Go shape of #StrMap (`{[string]: #StrVal}`). charly decodes every env / var / parameter / oci_label map into map[string]string — yaml.v3 coerces an unquoted scalar (`PORT: 8080`) to its literal text, so the map stays string→string. Source: the StrMap usage across charly/*.go.

type StrVal

type StrVal any /* CUE disjunction: (bool|string|number) */

An env entry: KEY=VALUE (value may be empty / contain =). A YAML scalar Go decodes into a string (map[string]string / string field): yaml.v3 coerces an unquoted int/bool/float to its literal text, so an idiomatic `PORT: 8080` is a valid string value. #StrMap is the matching map.

type StructuralKindLoadEnv

type StructuralKindLoadEnv struct {
	Members map[string]*Deploy `json:"members,omitempty"`

	// Standalone is the host-pre-decoded CANONICAL node channel the host threads to a structural
	// kind plugin whose value is RICH + core-referencing: candy/plugin-substrate (C2-substrate,
	// serving pod/vm/k8s/local/android → deploy/template shapes) AND candy/plugin-candy (C2-candy,
	// serving candy → candy-image/candy-layer shapes, folding into uf.Box/uf.Candy). Unlike group
	// — whose small scalar value is decoded from
	// op.Params against a self-contained #GroupInput and whose members ride Members above —
	// a substrate value is RICH + core-referencing (#Vm/#Deploy/#LibvirtDomain/… with
	// host-canonicalized shorthand like tunnel:/port:), so it cannot be re-decoded soundly
	// from op.Params by a plugin nor validated by a self-contained plugin schema. The host
	// therefore decodes the WHOLE node via the core buildBundleNode (deploy shape) /
	// decodeNodeValue (template shape) — the SINGLE decode source of truth (R3) — validates
	// it host-side against the KEPT #<Kind>Value def, and threads the canonical result here.
	// The plugin ECHOES it in its reply; the host folds the echo into uf.Bundle (deploy) or
	// the typed template map uf.Pod/uf.VM/… (template — the C2-substrate TEMPLATE fold arm
	// that extends F5's deploy-only fold). nil for group (which uses Members). RDD proved a
	// canonical spec.Deploy / spec.Vm / spec.Pod / … round-trips through JSON byte-faithfully,
	// so this thread-echo-fold is byte-equivalent to the former in-proc standaloneKind decode.
	Standalone *StandaloneLoad `json:"standalone,omitempty"`
}

StructuralKindLoadEnv is the OpLoad invocation context (op.Env) the host threads to a STRUCTURAL class:kind plugin (F5 authored-member input-threading). A structural kind's authored RESOURCE-MEMBER children (pod/vm/k8s/local/android/group sub-entities) cannot ride op.Params — that JSON is unified against the plugin's CLOSED #<Kind>Input def, which the member subtree would violate. So the host PRE-DECODES the authored member children HOST-SIDE, via the SAME core buildBundleNode recursion the builtin path uses (buildResourceMemberChildren — one member-decode source of truth, R3), and threads the decoded subtree HERE. The plugin decodes only its KIND-SPECIFIC scalar config from op.Params and ATTACHES these members to its spec.Deploy reply — Members for a targetless kind (group), Children for a workload kind — so runPluginKind folds a COMPLETE Bundle (with members) into uf.Bundle, identical to the builtin decode. Cross-member `${HOST:…}` refs survive as literal strings resolved later by tree position (check_members.go), so host-side pre-decode is structure-preserving.

type TunnelYAML

type TunnelYAML struct {
	Provider string    `yaml:"provider" json:"provider"`
	Tunnel   string    `yaml:"tunnel,omitempty" json:"tunnel,omitempty"`
	Public   PortScope `yaml:"public,omitempty" json:"public"`
	Private  PortScope `yaml:"private,omitempty" json:"private"`
}

TunnelYAML supports both bare string and expanded form (the `tunnel:` field).

type VFIOGpu

type VFIOGpu struct {
	VFIOPCIDevice
	GroupMembers []VFIOPCIDevice `json:"group_members"` // includes the GPU function itself; sorted by Addr
}

VFIOGpu is a display-class device plus every other function sharing its IOMMU group. Passthrough must move the whole group together, so the renderer emits one <hostdev> per GroupMember.

func SelectGPUByVendor

func SelectGPUByVendor(rep VFIOReport, vendor string) (VFIOGpu, bool)

SelectGPUByVendor returns the first passthrough-capable GPU whose PCI vendor matches (case/prefix-insensitive). ok=false when no GPU matches.

type VFIOPCIDevice

type VFIOPCIDevice struct {
	Addr       string `json:"addr"`        // 0000:01:00.0 (sysfs device-directory name = libvirt domain:bus:slot.function)
	VendorID   string `json:"vendor_id"`   // 0x10de
	DeviceID   string `json:"device_id"`   // 0x2704
	Class      string `json:"class"`       // 0x0300 (high 16 bits of the PCI class code)
	ClassLabel string `json:"class_label"` // human label, e.g. "VGA controller"
	Driver     string `json:"driver"`      // nvidia | nouveau | vfio-pci | "" (unbound)
	IOMMUGroup int    `json:"iommu_group"` // -1 when the device has no iommu_group (IOMMU disabled)
}

VFIOPCIDevice is a single PCI function discovered under sysfs.

type VFIOReport

type VFIOReport struct {
	IOMMUEnabled bool      `json:"iommu_enabled"` // /sys/kernel/iommu_groups is populated
	IOMMUKind    string    `json:"iommu_kind"`    // intel | amd | "" (from kernel cmdline)
	GPUs         []VFIOGpu `json:"gpus"`
}

VFIOReport summarizes host readiness for VFIO GPU passthrough.

type VenueDescriptor

type VenueDescriptor struct {
	Kind           string   `json:"kind"` // "shell" | "ssh"
	User           string   `json:"user,omitempty"`
	Host           string   `json:"host,omitempty"`
	Port           int      `json:"port,omitempty"`
	Args           []string `json:"args,omitempty"`
	ConnectTimeout int      `json:"connect_timeout,omitempty"`
}

VenueDescriptor is the SELF-CONTAINED, serializable description of a deploy venue's executor that a substrate LIFECYCLE plugin's OpPrepareVenue / OpTeardownExecutor returns (F6). A live DeployExecutor (ShellExecutor / *SSHExecutor) cannot cross the wire, so the plugin returns this descriptor and the HOST re-materializes the real executor from it — independently, AFTER the lifecycle Invoke returns — then serves THAT over the existing ExecutorService to the deploy-walk plugin. Kind "shell" → a host-local ShellExecutor (the SSH fields are ignored); Kind "ssh" → an *SSHExecutor built from User/Host/Port/Args/ConnectTimeout (the guest venue). Empty → no venue.

type Vm

type Vm struct {
	Source VmSource `yaml:"source,omitempty" json:"source"`

	DiskSize string `yaml:"disk_size,omitempty" json:"disk_size,omitempty"`

	Ram string `yaml:"ram,omitempty" json:"ram,omitempty"`

	Cpus int `yaml:"cpu,omitempty" json:"cpu,omitempty"`

	Machine string `yaml:"machine,omitempty" json:"machine,omitempty"`

	// firmware is REQUIRED-with-default (not optional): an if-guard can only
	// reference a field that always resolves to a concrete value, and an
	// OPTIONAL field — even one carrying a default — errors with "cannot
	// reference optional field" when absent. Required-with-default materializes
	// "bios" on omission (matching the Go empty→bios behavior) AND stays
	// referenceable by the uefi-secure cross-rule below.
	Firmware string `yaml:"firmware,omitempty" json:"firmware"`

	Backend string `yaml:"backend,omitempty" json:"backend"`

	Autostart bool `yaml:"autostart,omitempty" json:"autostart"`

	Network *VmNetwork `yaml:"network,omitempty" json:"network,omitempty"`

	SSH *VmSSH `yaml:"ssh,omitempty" json:"ssh,omitempty"`

	CloudInit *VmCloudInit `yaml:"cloud_init,omitempty" json:"cloud_init,omitempty"`

	Libvirt *LibvirtDomain `yaml:"libvirt,omitempty" json:"libvirt,omitempty"`

	Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`

	Snapshots []VmSnapshot `yaml:"snapshot,omitempty" json:"snapshot,omitempty"`
}

type VmCharlyInstall

type VmCharlyInstall struct {
	// VmCharlyInstall has ONLY `strategy` (the vm-spec skill's url/checksum are
	// STALE — the Go struct dropped them). auto: scp host binary post-boot;
	// scp: explicit form; skip: user-managed.
	Strategy string `yaml:"strategy,omitempty" json:"strategy,omitempty"`
}

type VmChecksum

type VmChecksum struct {
	Type string `yaml:"type,omitempty" json:"type,omitempty"`

	Value string `yaml:"value,omitempty" json:"value,omitempty"`
}

type VmCloudInit

type VmCloudInit struct {
	Hostname string `yaml:"hostname,omitempty" json:"hostname,omitempty"`

	Timezone string `yaml:"timezone,omitempty" json:"timezone,omitempty"`

	Locale string `yaml:"locale,omitempty" json:"locale,omitempty"`

	Users []VmCloudInitUser `yaml:"users,omitempty" json:"users,omitempty"`

	Package []string `yaml:"package,omitempty" json:"package,omitempty"`

	RunCmd []string `yaml:"runcmd,omitempty" json:"runcmd,omitempty"`

	BootCmd []string `yaml:"bootcmd,omitempty" json:"bootcmd,omitempty"`

	WriteFiles []VmCloudInitFile `yaml:"write_files,omitempty" json:"write_files,omitempty"`

	Network *VmCloudInitNetwork `yaml:"network,omitempty" json:"network,omitempty"`

	Mirrors *VmCloudInitMirrors `yaml:"mirrors,omitempty" json:"mirrors,omitempty"`

	CharlyInstall *VmCharlyInstall `yaml:"charly_install,omitempty" json:"charly_install,omitempty"`

	Extra string `yaml:"extra,omitempty" json:"extra,omitempty"`
}

--------------------------------------------------------------------------- cloud_init: VmCloudInit. CLOSED. Genuine passthroughs: extra (raw cloud-config string) and network.ethernets (network-config v2, map[string]map[string]any → {[string]: {[string]: _}}). ---------------------------------------------------------------------------

type VmCloudInitFile

type VmCloudInitFile struct {
	Path string `yaml:"path,omitempty" json:"path"`

	Content string `yaml:"content,omitempty" json:"content,omitempty"`

	Owner string `yaml:"owner,omitempty" json:"owner,omitempty"`

	Perms string `yaml:"perms,omitempty" json:"perms,omitempty"`

	Encoding string `yaml:"encoding,omitempty" json:"encoding,omitempty"`
}

type VmCloudInitMirrors

type VmCloudInitMirrors struct {
	APT []string `yaml:"apt,omitempty" json:"apt,omitempty"`

	DNF []string `yaml:"dnf,omitempty" json:"dnf,omitempty"`

	Pacman []string `yaml:"pacman,omitempty" json:"pacman,omitempty"`
}

type VmCloudInitNetwork

type VmCloudInitNetwork struct {
	Version int `yaml:"version,omitempty" json:"version,omitempty"`

	// network-config v2 map[string]map[string]any — typed-open passthrough.
	Ethernets map[string]map[string]any `yaml:"ethernets,omitempty" json:"ethernets,omitempty"`
}

type VmCloudInitUser

type VmCloudInitUser struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Sudo bool `yaml:"sudo,omitempty" json:"sudo,omitempty"`

	Groups []string `yaml:"groups,omitempty" json:"groups,omitempty"`

	Shell string `yaml:"shell,omitempty" json:"shell,omitempty"`

	LockPasswd *bool `yaml:"lock_passwd,omitempty" json:"lock_passwd,omitempty"`
}

type VmDeployState

type VmDeployState struct {
	InstanceID              string                  `yaml:"instance_id,omitempty" json:"instance_id,omitempty"`
	DiskPath                string                  `yaml:"disk_path,omitempty" json:"disk_path,omitempty"`
	SeedIso                 string                  `yaml:"seed_iso,omitempty" json:"seed_iso,omitempty"`
	SshPort                 int                     `yaml:"ssh_port,omitempty" json:"ssh_port,omitempty"`
	SshUser                 string                  `yaml:"ssh_user,omitempty" json:"ssh_user,omitempty"`
	Backend                 string                  `yaml:"backend,omitempty" json:"backend,omitempty"`
	KeyInjectionResolved    *VmKeyInjectionResolved `yaml:"key_injection_resolved,omitempty" json:"key_injection_resolved,omitempty"`
	CharlyInstallStrategy   string                  `yaml:"charly_install_strategy,omitempty" json:"charly_install_strategy,omitempty"`
	CloudInitRenderedDigest string                  `yaml:"cloud_init_rendered_digest,omitempty" json:"cloud_init_rendered_digest,omitempty"`
	Snapshots               []VmSnapshotState       `yaml:"snapshot,omitempty" json:"snapshot,omitempty"`
	Ephemeral               *EphemeralRuntime       `yaml:"ephemeral,omitempty" json:"ephemeral,omitempty"`
}

VmDeployState is the runtime state the vm lifecycle hook's PrepareVenue writes on first apply. Source: charly/deploy.go VmDeployState.

type VmKeyInjection

type VmKeyInjection struct {
	SMBIOS string `yaml:"smbios,omitempty" json:"smbios,omitempty"`

	CloudInit string `yaml:"cloud_init,omitempty" json:"cloud_init,omitempty"`
}

type VmKeyInjectionResolved

type VmKeyInjectionResolved struct {
	SMBIOS    bool `yaml:"smbios" json:"smbios"`
	CloudInit bool `yaml:"cloud_init" json:"cloud_init"`
}

VmKeyInjectionResolved is the effective key-injection state. Source: charly/deploy.go VmKeyInjectionResolved.

type VmNetwork

type VmNetwork struct {
	Model string `yaml:"model,omitempty" json:"model,omitempty"`

	Mode string `yaml:"mode,omitempty" json:"mode"`

	Bridge string `yaml:"bridge,omitempty" json:"bridge,omitempty"`

	MAC string `yaml:"mac,omitempty" json:"mac,omitempty"`

	PortForwards []string `yaml:"port_forwards,omitempty" json:"port_forwards,omitempty"`
}

type VmSSH

type VmSSH struct {
	User         string          `yaml:"user,omitempty" json:"user,omitempty"`
	Port         int             `yaml:"port,omitempty" json:"port,omitempty"`
	PortAuto     bool            `yaml:"port_auto,omitempty" json:"port_auto,omitempty"`
	KeySource    string          `yaml:"key_source,omitempty" json:"key_source,omitempty"`
	KeyInjection *VmKeyInjection `yaml:"key_injection,omitempty" json:"key_injection,omitempty"`
}

VmSSH is the guest SSH access config (port XOR port_auto is a Go cross-field rule). KeyInjection references the generated VmKeyInjection.

type VmSize

type VmSize = string

--- named-pattern scalars (string) ---

type VmSnapshot

type VmSnapshot struct {
	Name string `yaml:"name,omitempty" json:"name"`

	Description string `yaml:"description,omitempty" json:"description,omitempty"`

	Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`

	Quiesce bool `yaml:"quiesce,omitempty" json:"quiesce,omitempty"`

	From string `yaml:"from,omitempty" json:"from,omitempty"`
}

type VmSnapshotDecl

type VmSnapshotDecl = VmSnapshot

--- candy sub-types ---

type VmSnapshotState

type VmSnapshotState struct {
	Name        string `yaml:"name" json:"name"`
	Mode        string `yaml:"mode" json:"mode"`
	LibvirtName string `yaml:"libvirt_name,omitempty" json:"libvirt_name,omitempty"`
	DiskPath    string `yaml:"disk_path,omitempty" json:"disk_path,omitempty"`
	Description string `yaml:"description,omitempty" json:"description,omitempty"`
	Created     string `yaml:"created,omitempty" json:"created,omitempty"`
	Parent      string `yaml:"parent,omitempty" json:"parent,omitempty"`
	Refcount    int    `yaml:"refcount" json:"refcount"`
}

VmSnapshotState mirrors one snapshot in the charly.yml vm_state record. Source: charly/deploy.go VmSnapshotState.

type VmSource

type VmSource struct {
	Kind             string     `yaml:"kind" json:"kind"`
	URL              string     `yaml:"url,omitempty" json:"url,omitempty"`
	Checksum         VmChecksum `yaml:"checksum,omitempty" json:"checksum,omitempty"`
	Cache            string     `yaml:"cache,omitempty" json:"cache,omitempty"`
	BaseUser         string     `yaml:"base_user,omitempty" json:"base_user,omitempty"`
	Box              string     `yaml:"box,omitempty" json:"box,omitempty"`
	Transport        string     `yaml:"transport,omitempty" json:"transport,omitempty"`
	Rootfs           string     `yaml:"rootfs,omitempty" json:"rootfs,omitempty"`
	RootSize         string     `yaml:"root_size,omitempty" json:"root_size,omitempty"`
	KernelArgs       string     `yaml:"kernel_args,omitempty" json:"kernel_args,omitempty"`
	FromVm           string     `yaml:"from_vm,omitempty" json:"from_vm,omitempty"`
	FromSnapshot     string     `yaml:"from_snapshot,omitempty" json:"from_snapshot,omitempty"`
	CloudInitClean   bool       `yaml:"cloud_init_clean,omitempty" json:"cloud_init_clean,omitempty"`
	LibvirtName      string     `yaml:"libvirt_name,omitempty" json:"libvirt_name,omitempty"`
	DiskPath         string     `yaml:"disk_path,omitempty" json:"disk_path,omitempty"`
	DiskFormat       string     `yaml:"disk_format,omitempty" json:"disk_format,omitempty"`
	AdoptedAt        string     `yaml:"adopted_at,omitempty" json:"adopted_at,omitempty"`
	LastSyncedAt     string     `yaml:"last_synced_at,omitempty" json:"last_synced_at,omitempty"`
	Builder          string     `yaml:"builder,omitempty" json:"builder,omitempty"`
	BuilderImage     string     `yaml:"builder_image,omitempty" json:"builder_image,omitempty"`
	Distro           string     `yaml:"distro,omitempty" json:"distro,omitempty"`
	Package          []string   `yaml:"package,omitempty" json:"package,omitempty"`
	BootstrapArch    string     `yaml:"bootstrap_arch,omitempty" json:"bootstrap_arch,omitempty"`
	BootstrapVariant string     `yaml:"bootstrap_variant,omitempty" json:"bootstrap_variant,omitempty"`
}

VmSource is the discriminated-union source for a VM disk image (Kind selects the active branch: cloud_image / bootc / clone / imported / bootstrap).

type VmSpec

type VmSpec = Vm

--- top-level entity types ---

type VolumeYAML

type VolumeYAML = CandyVolume

--- candy sub-types ---

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL