Skip to content

plugin-distro

Placement compiled-in (in-process)
Source github.com/opencharly/plugin-distro/candy/plugin-distro
Version 2026.176.3203
Candy plugin-distro

This plugin is listed in charly/charly.yml’s compiled_plugins:, so its providers are compiled into the charly binary and register in-process.

The reserved words this plugin serves:

  • distro — kind class

The distro plugin KIND relocated into a candy: a KIND provider dispatching via the pb Invoke(OpLoad) envelope (decode the authored distro: entity into its core spec type, re-marshal as canonical JSON). Serves itself in BOTH placements (compiled-in OR out-of-process) like the matching/exampleprobe verbs + the init kind — no kit contract needed (kinds are pb-shape).

The CUE schema below is the authoritative grammar for this plugin’s input. It is the same single source that generates the plugin’s Go parameter types and answers the runtime Describe RPC, so this page cannot disagree with either.

// The BUILT-IN `distro` plugin's OWN CUE schema — the typed input for the `distro`
// KIND (the per-distro build vocabulary, formerly a core `distro:` kind decoded into
// the typed core map uf.Distro). It is the SINGLE SOURCE for this plugin's params,
// used two ways (the same contract the reference exampleprobe/process, the
// package-group/agent/module/sidecar plugins, and core `spec` use):
//
// 1. GENERATE the Go param struct — `cue exp gengotypes` (driven by task cue:gen,
// which wraps this with `package params` + `@go(params)`) emits
// ../params/cue_types_gen.go.
// 2. VALIDATE authored input AT RUNTIME — the builtin serves this source over the
// Describe channel (InProcTransport) exactly like an external serves it over
// gRPC; the host splices it onto the base (base ++ plugin) and validates every
// authored `distro:` entity body against #DistroInput BEFORE runPluginKind
// dispatches (validateAuthoredPluginInput(ClassKind, "distro", …)) — the
// kind-class analogue of the verb plugin_input gate.
//
// SELF-CONTAINED: it references NO base def — every shared shape (#CacheMount /
// #PhaseSet / #PhaseTemplates from _common.cue) is reproduced standalone here (prefix
// #Ds), so it compiles standalone (gengotypes + the load-gate compile) AND splices
// onto the base (the base ++ plugin splice exists to detect a def-name collision with
// the base, not to resolve base refs). It is a faithful reproduction of the core
// #Distro (schema/distro.cue) — the same authored WIRE keys, so the host validates a
// real distro entity (incl. the binary-embedded build vocabulary), and the plugin's
// Invoke canonicalises the body back through the core spec.Distro type (which DistroDef
// aliases and the generator/format code consumes via the Distros() accessor).
//
// NAME: in node-form the entity name is the top-level node KEY, not a body field, so
// the assembled entity body NEVER carries `name`; #Distro has no name field either, so
// there is nothing to make optional here. @go() annotations are dropped (they steer the
// CORE spec codegen only; the validator ignores attributes, the plugin Invoke decodes
// into spec.Distro, so the generated params struct's Go names are cosmetic for a builtin).
#DistroInput: {
inherits?: string & =~"^[a-z0-9]+(-[a-z0-9]+)*$"
inherit_packages?: bool
version?: string & =~"^[0-9]+(\\.[0-9]+)*$"
bootstrap?: #DsBootstrap
workaround?: [...string]
format?: {[string]: #DsFormat}
base_user?: #DsBaseUser
pacstrap?: #DsPacstrap
debootstrap?: #DsDebootstrap
alpine_bootstrap?: #DsAlpineBootstrap
bootloader?: #DsBootloader
disk_layout?: #DsDiskLayout
dnf?: #DsDnf
installer?: #DsInstaller
}
// install_cmd is the bootstrap command; ubuntu sets it to "" (kept WITHOUT
// `& !=""` so the empty-string base case validates).
#DsBootstrap: {
install_cmd: string
package?: [...string]
cache_mount?: [...#DsCacheMount]
}
#DsFormat: {
cache_mount?: [...#DsCacheMount]
section_field?: {[string]: "list" | "list_of_maps"}
uninstall_template?: string
phase?: #DsPhaseSet
validate?: [...#DsFormatRule]
secondary?: bool
local_pkg?: #DsLocalPkg
}
#DsFormatRule: {
field: string & !=""
rule: string & !=""
}
// #DsLocalPkg — the local_pkg INSTALL machinery only (the source-build fields
// pkg_glob/source_sentinel/build_template/dep_builder were removed with the
// pkg/ source-build cutover: the `charly generate-packages` plugin builds the
// package now, so the deploy-time + image-build paths only INSTALL the published
// package via install_template/download_template). Mirrors spec #LocalPkg.
#DsLocalPkg: {
install_template: string & !=""
probe: string & !=""
download_template?: string
}
#DsPacstrap: {
base_package?: [...string]
extra_repo?: [...#DsPacstrapRepo]
runtime_pacman_conf?: string
}
#DsPacstrapRepo: {
name: string & !=""
server: string & =~"^https?://"
siglevel?: string
}
#DsDebootstrap: {
suite?: string
mirror?: string & =~"^https?://"
variant?: string
components?: string
include_package?: [...string]
base_package?: [...string]
extra_repo?: [...#DsDebootstrapRepo]
}
#DsDebootstrapRepo: {
name: string & !=""
url: string & =~"^https?://"
suite?: string
components?: string
}
#DsAlpineBootstrap: {
mirror_url?: string & =~"^https?://"
}
#DsBootloader: {
install_template?: string
initramfs_template?: string
fstab_template?: string
}
// #DsDiskLayout mirrors spec's #DiskLayout: how a bootstrap VM's disk is partitioned and
// mounted, for the distros whose on-disk shape is part of their identity. Both fields are
// optional and both default to charly's historical behaviour (a bare root filesystem with
// the ESP at /boot/efi), so a distro that omits the block is unaffected.
#DsDiskLayout: {
esp_mount_point?: string & =~"^/"
subvolume?: [...#DsSubvolume]
}
// #DsSubvolume is one btrfs subvolume. `subvol=<name>` is prepended by the emitter, so
// mount_options must not repeat it.
#DsSubvolume: {
name: string & !=""
mount_point: string & =~"^/"
mount_options?: string
}
// #DsInstaller mirrors spec's #DistroInstaller: the format of the answers volume an
// unattended installer reads (archinstall JSON, kickstart, preseed, autoinstall). The
// FORMAT is the distro's; the DATA is the VM entity's.
//
// Added alongside disk_layout because it had drifted: spec gained #Distro.installer and
// this input def did not, so an authored `installer:` block was rejected here with
// `#DistroInput.installer: field not allowed` even though the core schema accepted it.
#DsInstaller: {
volume_id: string & =~"^[A-Za-z0-9_-]{1,32}$"
fs?: *"iso9660" | "vfat"
file: [...#DsInstallerFile]
boot_arg?: string
done?: *"poweroff" | "marker"
marker_path?: string & =~"^/"
}
// #DsInstallerFile is ONE file placed on the answers volume. `when` is a Go-template
// guard: the file is emitted only when it renders non-empty and not "false".
#DsInstallerFile: {
path: string & =~"^[A-Za-z0-9_.-]+(/[A-Za-z0-9_.-]+)*$"
content: string
mode?: string & =~"^0[0-7]{3,4}$"
when?: string
}
#DsBaseUser: {
name: string & !=""
uid: int & >=0
gid: int & >=0
home: string & =~"^/"
}
#DsDnf: {
max_parallel_downloads?: int & >=1
fastestmirror?: bool
}
// reproduces #CacheMount (schema/_common.cue) standalone.
#DsCacheMount: {
dst: string & =~"^/"
sharing?: *"locked" | "shared" | "private"
owned?: bool
}
// reproduces #PhaseSet / #PhaseTemplates (schema/_common.cue) standalone.
#DsPhaseSet: {
prepare?: #DsPhaseTemplates
install?: #DsPhaseTemplates
cleanup?: #DsPhaseTemplates
}
#DsPhaseTemplates: {
// install is the ONE venue-agnostic install body (spec#50 — R3: never two
// hand-maintained host/container copies). The venue wrapper (RUN {{cacheMounts}}
// for container, plain shell for host) is applied by spec.FormatPhaseTemplate.
// host/container remain as venue-specific OVERRIDES.
install?: string
container?: string
host?: string
}

See also the candy reference for this candy’s install surface.