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. Kept in lockstep with the CUE def (schema/deploy.cue #VmDeployState) — every field here has a matching CUE field (e.g. PortForwards ⇄ port_forwards, the auto-forward record).
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
- Variables
- func BareCandyRef(ref string) string
- func BedCheckLiveRefs(name string, children map[string]*BundleNode) []string
- func BoxNamesOf(m BoxMap) []string
- func BuilderPhaseTemplate(b *Builder, phase Phase, venue Venue) string
- func BundleDelArgv(name string) []string
- func CandyArtifactRegisters(layers []CandyReader) map[string]bool
- func CandyCapabilitiesError(layers map[string]CandyReader, order []string, missing []string) error
- func CandyMapKey(layer CandyReader) string
- func CandyStageDirName(layer CandyReader) string
- func CheckLevelReaches(have, want string) bool
- func CheckRequiredCapabilities(layers map[string]CandyReader, order []string, agg *AggregatedCandyCaps) []string
- func ClassifyNodeTarget(node *BundleNode, path string) string
- func CompareCalVer(a, b string) int
- func ComputeCalVer() string
- func ComputeCalVerAt(t time.Time) string
- func ContainerName(boxName string) string
- func ContainerNameInstance(boxName, instance string) string
- func CurrentUsername() string
- func DecodePluginKindMap[T any](uf *UnifiedFile, kind string) map[string]*T
- func DefaultDeployConfigPath() (string, error)
- func DeployKey(boxName, instance string) string
- func DeployNestedLocalChildren(parent string, children map[string]*BundleNode, ...) error
- func DeriveDeploymentName(imageRef string) string
- func DescriptionInfo(d string) string
- func DirExists(path string) bool
- func DiscoverSkipDir(name string) bool
- func DistroTagChain(distro, version string) []string
- func EncodeBox(b BoxConfig) json.RawMessage
- func EncodeInlineCandy(il *InlineCandy) json.RawMessage
- func EnvMapToPairs(env map[string]string) []string
- func ExpandPath(path string, home string) string
- func ExternalInPlaceVenue(node *BundleNode) bool
- func FileExists(path string) bool
- func FinalizeCandyRefs(m *CandyModel, v *CandyView, refs CandyRefs)
- func FindEntityDirs(path, filename string, recursive bool) ([]string, error)
- func FirstYAMLVersionLine(data []byte) string
- func FlattenedEnvMap(base map[string]string, overrides *CandyServiceOverrides) map[string]string
- func FormatCacheMount(dst, sharing string, uid, gid int) string
- func FormatHTTPHeaders(h http.Header) string
- func FormatPhaseTemplate(f *Format, phase Phase, venue Venue) string
- func FormatPortMapping(p ParsedPortMapping) string
- func GateEnabled(g Gate, opts EmitOpts) bool
- func GitRemoteIdentity(dir string) string
- func HTTPClientFor(base *http.Client, req CheckHTTPRequest) (*http.Client, error)
- func HostRooted(node *BundleNode) bool
- func InputStr(c *Op, key string) string
- func IsContainerVenue(node *BundleNode) bool
- func IsExternalStepKind(k StepKind) bool
- func IsRemoteCandyRefString(ref string) bool
- func IsRemoteImageRef(ref string) bool
- func IsValidCheckLevel(level string) bool
- func IsVmVenue(node *BundleNode) bool
- func LeafName(ref string) string
- func MappingRoot(n *yaml.Node) *yaml.Node
- func MatchAll(value string, matchers []Matcher) error
- func MatchValueString(v any) string
- func MatchValueStrings(v any) []string
- func MergePluginKindsMap(dst *map[string]map[string]json.RawMessage, ...)
- func MergeUnified(dst, src *UnifiedFile, srcDir string)
- func NormalizeGitRemoteURL(raw string) string
- func NormalizePCIVendor(s string) string
- func NormalizeRepoIdentity(s string) string
- func NormalizeRepoSpec(spc string) (repoPath, version string)
- func PackageNames(items []PackageItem) []string
- func ParseDeployKey(key string) (boxName, instance string)
- func PathIsSystemScoped(p string) bool
- func PathLeaf(path string) string
- func PodName(boxName string) string
- func PodNameInstance(boxName, instance string) string
- func PodQuadletFilename(boxName string) string
- func PodQuadletFilenameInstance(boxName, instance string) string
- func PollUntil(ctx context.Context, cfg PollConfig, cond PollCondition) error
- func QuadletDir() (string, error)
- func QuadletExists(boxName string) (bool, error)
- func QuadletExistsInstance(boxName, instance string) (bool, error)
- func QuadletFilename(boxName string) string
- func QuadletFilenameInstance(boxName, instance string) string
- func RenderCacheMounts(mounts []CacheMount, uid, gid int, sep string, trailing bool) string
- func RenderCacheMountsAuto(mounts []CacheMount, uid, gid int, sep string, trailing bool) string
- func RepoIdentity(ref, baseDir string) string
- func ResolveBoxName(box string) string
- func ResolveCheckLevel(level string) string
- func ResolveHome(p *InstallPlan, home string)
- func ResolveNodePath(roots map[string]BundleNode, path string) (*BundleNode, []*BundleNode, error)
- func ResolvePluginKindViaPlugin[T any](uf *UnifiedFile, kind string, resolve func(json.RawMessage) (*T, error)) map[string]*T
- func RootRepoIdentity(dir string) string
- func ScalarNode(v string) *yaml.Node
- func ServiceName(boxName string) string
- func ServiceNameInstance(boxName, instance string) string
- func ShellQuote(s string) string
- func SidecarContainerName(boxName, sidecarName string) string
- func SidecarContainerNameInstance(boxName, instance, sidecarName string) string
- func SidecarQuadletFilename(boxName, sidecarName string) string
- func SidecarQuadletFilenameInstance(boxName, instance, sidecarName string) string
- func SortedDeployKeys(m map[string]BundleNode) []string
- func SortedMemberKeys(members map[string]*BundleNode) []string
- func SortedNestedKeys(children map[string]*BundleNode) []string
- func SplitDottedPath(path string) []string
- func SplitNamespaceRef(ref string) (ns, rest string, ok bool)
- func SplitRepoAndSubPath(ref string) (repoPath, subPath, name string)
- func SplitVmAddress(name string) (plain string, isVm bool)
- func StampDescent(node *Deploy, traitsFor func(word string) *DeployTraits)
- func StatusFromState(state string) string
- func StripCandyRefVersion(ref string) (string, string)
- func StripPortSuffix(s string) (string, string)
- func StripURLScheme(ref string) string
- func SystemdUserDir() (string, error)
- func TaskKnownNames(vars map[string]string) map[string]bool
- func TrimPreview(s string) string
- func ValidateDeployRequiresBox(deploy map[string]BundleNode) error
- func ValidateDeploymentChildren(path string, node *BundleNode) error
- func ValidateDeploymentName(name, parentPath string) error
- func ValidateDeploymentTree(deploy map[string]BundleNode) error
- func VenueIsAgentProvisioned(uf *UnifiedFile, venue string) bool
- func VmDomainIdentity(deployName string) string
- func VmNameFromDeployName(deployName string) (string, error)
- func VmSshAlias(vmName string) string
- func WrapContainerCommand(script string) string
- type AUR
- type AdbEndpoint
- type Agent
- type AgentAbortControl
- type AgentDelegationEdge
- type AgentEvent
- type AgentExecSpec
- type AgentFederationRecord
- type AgentResolveInput
- type AgentResolveReply
- type AgentRunRequest
- type AgentRuntimeCapability
- type AgentSession
- type AgentSessionBinding
- type AgentTeam
- type AgentTeamMember
- type AgentTeamRecord
- type AggregatedCandyCaps
- type AggregatedCandyCapsView
- type AliasConfig
- type AliasYAML
- type AlpineBootstrap
- type Android
- type AndroidDeployVenue
- type AndroidResolveInput
- type AndroidResolveReply
- type AndroidSpec
- type ApkInstallStep
- type ApkPackageSpec
- type ArbiterBracketAcquireReply
- type ArbiterBracketAcquireRequest
- type ArbiterBracketReleaseReply
- type ArbiterBracketReleaseRequest
- type ArbiterInvokeInput
- type ArbiterInvokeReply
- type ArtifactRef
- type ArtifactView
- type BakedLabelSet
- type BaseChainNode
- type BaseUser
- type Bootloader
- type Bootstrap
- type Box
- type BoxAlias
- type BoxConfig
- type BoxFetchResolveReply
- type BoxFetchResolveRequest
- type BoxMap
- type BoxMerge
- type BoxMetadata
- type BoxRefResolveReply
- type BoxRefResolveRequest
- type BuildEngineScanRemoteRequest
- type BuildEnsureReply
- type BuildEnsureRequest
- type BuildEnv
- type BuildFormat
- type BuildPkgReply
- type BuildPkgRequest
- type BuildReply
- type BuildRequest
- type BuildResolveBox
- type BuildResolveReply
- type BuildResolveRequest
- type BuildResolvedBox
- type BuildStageContext
- type BuildTarget
- type BuildType
- type Builder
- type BuilderCollectInput
- type BuilderCollectReply
- type BuilderConfig
- type BuilderDef
- type BuilderMap
- type BuilderPreresolved
- type BuilderResolveInput
- type BuilderResolveReply
- type BuilderReverseInput
- type BuilderReverseReply
- type BuilderRunOpts
- type BuilderStep
- type BundleConfig
- type BundleNode
- type CLIArg
- type CLILeaf
- type CLIModel
- type CacheMount
- type CacheMountSpec
- type CacheMountView
- type CalVer
- type Candy
- type CandyAlias
- type CandyArtifact
- type CandyArtifactRewrite
- type CandyCandidate
- type CandyCapabilities
- type CandyCapabilitiesView
- type CandyCapability
- type CandyData
- type CandyExtract
- type CandyHook
- type CandyMCPProvide
- type CandyModel
- type CandyReader
- type CandyRecord
- type CandyRef
- type CandyRefEntry
- type CandyRefs
- type CandyRoute
- type CandyScanner
- type CandySecret
- type CandyService
- type CandyServiceOverrides
- type CandyView
- type CandyVolume
- type CandyYAML
- type CapabilityInitDef
- type CapabilityService
- type Check
- type CheckBedGpuPrereqReply
- type CheckBedGpuPrereqRequest
- type CheckBedMember
- type CheckBedPrereqSkip
- type CheckBedReply
- type CheckContext
- type CheckDrainEndpointCleanupsRequest
- type CheckEndpointResolveReply
- type CheckEndpointResolveRequest
- type CheckEnv
- type CheckExecutor
- type CheckGraphicsEndpoint
- type CheckHTTPRequest
- type CheckHTTPResponse
- type CheckImageLabelResolveReply
- type CheckImageLabelResolveRequest
- type CheckLoadPluginsReply
- type CheckLoadPluginsRequest
- type CheckResult
- type CheckRunMode
- type CheckRunReply
- type CheckRunRequest
- type CheckRunResults
- type CheckVenueResolveReply
- type CheckVenueResolveRequest
- type CheckVerbProvider
- type CheckVerbResult
- type CliReply
- type CliRequest
- type CloudInitRuntimeParams
- type CollectedAlias
- type Config
- func (c *Config) AllBoxNames() []string
- func (c *Config) BoxConfig(name string) (BoxConfig, bool)
- func (c *Config) BoxNames() []string
- func (c *Config) EachBox(yield func(string, BoxConfig) bool)
- func (c *Config) FindBoxByLeaf(leaf string) (string, bool)
- func (c *Config) HasBox(name string) bool
- func (c *Config) ResolveBoxRef(ref string) (BoxConfig, *Config, bool)
- func (c *Config) SetBox(name string, b BoxConfig)
- func (c *Config) WalkBaseChain(boxName string) []BaseChainNode
- func (c *Config) WalkBaseChainBuild(baseName string) []string
- func (c *Config) WalkBaseChainDistro(baseName string) []string
- type ConfigResolveReply
- type ConfigResolveRequest
- type ConstructStepReply
- type ConstructStepRequest
- type Context
- type Copy
- type CredentialHealth
- type CredentialMount
- type CueSchema
- type DataYAML
- type Debootstrap
- type DebootstrapRepo
- type Deploy
- func (n *Deploy) HasChildren() bool
- func (c Deploy) IsDisposable() bool
- func (c Deploy) IsEphemeral() bool
- func (c Deploy) IsGroup() bool
- func (c Deploy) IsPreemptible() bool
- func (c Deploy) LifecycleTag() string
- func (c Deploy) PreemptionHolds() []string
- func (c Deploy) RequiredExclusive() []string
- func (c Deploy) RequiredShared() []string
- type DeployCompileReply
- type DeployCompileRequest
- type DeployDelResolveReply
- type DeployDelResolveRequest
- type DeployExecutor
- type DeployExpose
- type DeployFromBoxReply
- type DeployFromBoxRequest
- type DeployNodeDelDispatchReply
- type DeployNodeDelDispatchRequest
- type DeployOverlayReply
- type DeployOverlayRequest
- type DeployPluginsConnectReply
- type DeployPluginsConnectRequest
- type DeployProbes
- type DeployRecord
- type DeployReply
- type DeployReplyRecord
- type DeployResolveTargetAddReply
- type DeployResolveTargetAddRequest
- type DeployResources
- type DeploySecret
- type DeployStorage
- type DeployTarget
- type DeployTargetDelOpts
- type DeployTargetDispatchReply
- type DeployTargetDispatchRequest
- type DeployTargetLogsOpts
- type DeployTargetRebuildOpts
- type DeployTargetStatus
- type DeployTraits
- type DeployTraitsCarrier
- type DeployVenue
- type DeployVolume
- type DeploymentStatus
- type DescentDescriptor
- type DetectedDevices
- type Diagnostic
- type Diagnostics
- type DiscoverConfig
- type DiscoveredManifest
- type Distro
- type DistroBuilderCandidate
- type DistroConfig
- func (dc *DistroConfig) AllFormatNames() []string
- func (dc *DistroConfig) ExpandPackageInheritance(tags []string) []string
- func (dc *DistroConfig) FindFormat(name string) *Format
- func (dc *DistroConfig) ResolveDistro(distroTags []string) *ResolvedDistro
- func (dc *DistroConfig) ResolveInherits(def *ResolvedDistro, maxDepth int) *ResolvedDistro
- func (dc *DistroConfig) ValidFormat(name string) bool
- type DistroDef
- type DistroPackages
- type DistroResolveInput
- type DistroResolveReply
- type Dnf
- type DoMode
- type DocParser
- type DocShape
- type Duration
- type EmitOpts
- type EmitReply
- type EncExecInput
- type EncExecReply
- type EncVolumePlan
- type EnsureBuildersParams
- type EntityRef
- type EnvConfig
- type EnvDependency
- type EnvKV
- type EnvProvideEntry
- type EnvVar
- type EphemeralLifetime
- type EphemeralRegisterReply
- type EphemeralRegisterRequest
- type EphemeralRuntime
- type EphemeralTeardownReply
- type EphemeralTeardownRequest
- type ExecContext
- type ExternalPluginStep
- type ExternalStep
- type ExtractYAML
- type FeatureEntity
- type FeatureReply
- type FeatureRequest
- type FileStep
- type Format
- type FormatRule
- type Gate
- type GoogleAccount
- type GpuProbeInput
- type GpuProbeReply
- type GpuSelector
- type GpuSwitchInput
- type GpuSwitchReply
- type HolderAddr
- type HolderDescriptor
- type HooksConfig
- type HostContext
- type HostEnv
- type ImageUserInput
- type ImportEntry
- type ImportList
- type Incident
- type Init
- type InitConfig
- func (ic *InitConfig) ActiveInit(layers map[string]CandyReader, candyOrder []string) map[string]*ResolvedInit
- func (ic *InitConfig) DetectCandyInit(ly *CandyYAML, candyPath string) []string
- func (ic *InitConfig) InitNames() []string
- func (ic *InitConfig) ResolveInitSystem(layers map[string]CandyReader, candyOrder []string, explicit string) (string, *ResolvedInit)
- type InitDef
- type InitResolveInput
- type InitResolveReply
- type InitResolveRequest
- type InitServiceSchema
- type InlineBuilderParams
- type InlineBuilderResult
- type InlineCandy
- type InstallContext
- type InstallOpts
- type InstallOptsConfig
- type InstallPlan
- type InstallPlanView
- type InstallStep
- type InstallStepView
- type Iterate
- type IterateConfig
- type K8s
- type K8sDeploy
- type K8sDeployVenue
- type K8sGatewayAPI
- type K8sGenFile
- type K8sGenInput
- type K8sGenReply
- type K8sGenerateKustomizeReply
- type K8sGenerateKustomizeRequest
- type K8sHostname
- type K8sImagesDefaults
- type K8sIngressDefaults
- type K8sObservability
- type K8sPatch
- type K8sPodDefaults
- type K8sResolveInput
- type K8sResolveReply
- type K8sResourceDefaults
- type K8sResourceValues
- type K8sResources
- type K8sSecretsBackend
- type K8sSpec
- type K8sStorage
- type KeyValue
- type LabelDataEntry
- type LabelDescriptionSet
- type LabelRouteEntry
- type LabelSecretEntry
- type LabelShellSet
- type LabelVolumeEntry
- type LabeledDescription
- type LibvirtAudio
- type LibvirtCPU
- type LibvirtCPUCache
- type LibvirtCPUFeature
- type LibvirtCPUTopology
- type LibvirtCPUTune
- type LibvirtChannel
- type LibvirtClock
- type LibvirtConsole
- type LibvirtDevices
- type LibvirtDisk
- type LibvirtDomain
- type LibvirtEmulatorPin
- type LibvirtFeatures
- type LibvirtFilesystem
- type LibvirtGraphics
- type LibvirtGraphicsListen
- type LibvirtGraphicsListeners
- type LibvirtHostdev
- type LibvirtHub
- type LibvirtHugepages
- type LibvirtHyperV
- type LibvirtIOMMU
- type LibvirtIOThreadPin
- type LibvirtInput
- type LibvirtInterface
- type LibvirtKVM
- type LibvirtLaunchSecurity
- type LibvirtListenOne
- type LibvirtMemBalloon
- type LibvirtMemTune
- type LibvirtMemnode
- type LibvirtMemoryBacking
- type LibvirtNUMACell
- type LibvirtNUMAMemory
- type LibvirtNUMATune
- type LibvirtPCIHex
- type LibvirtPanic
- type LibvirtParallel
- type LibvirtPortForward
- type LibvirtRNG
- type LibvirtRedirDev
- type LibvirtResource
- type LibvirtSecLabel
- type LibvirtSerial
- type LibvirtShmem
- type LibvirtSmartcard
- type LibvirtSound
- type LibvirtSpinlocks
- type LibvirtSysInfo
- type LibvirtTPM
- type LibvirtTimer
- type LibvirtURI
- type LibvirtUSB
- type LibvirtVCPUPin
- type LibvirtVendorID
- type LibvirtVideo
- type LibvirtVsock
- type LibvirtWatchdog
- type LifecycleOpts
- type LifecyclePrepareInput
- type LoadedDoc
- type LoadedProject
- type LoaderExecutor
- type LoaderWalkRequest
- type Local
- type LocalPkg
- type LocalPkgInstallStep
- type LocalResolveInput
- type LocalResolveReply
- type LocalSpec
- type MCPProvideEntry
- type MCPServerYAML
- type MatchOpMap
- type Matcher
- type MatcherList
- type MaterializeProjectSeams
- type MaterializeSeams
- type MaterializedProject
- type Materializer
- type MergeConfig
- type MergeReply
- type MergeRequest
- type NamespaceMount
- type NamespaceScanEntry
- type NamespaceScanReply
- type OCIEmitStepParams
- type Op
- type OpStep
- type OverlayBuildReply
- type OverlayBuildRequest
- type PackageItem
- type PackageSection
- type Pacstrap
- type PacstrapRepo
- type ParsedCalVer
- type ParsedNode
- type ParsedPortMapping
- type ParsedProject
- type ParsedRef
- type Phase
- type PhaseCarrier
- type PhaseSet
- type PhaseTemplates
- type Plugin
- type PluginCapability
- type Pod
- type PodAttachOpts
- type PodCmdOpts
- type PodCmdPayload
- type PodConfigBoxEngineReply
- type PodConfigBoxEngineRequest
- type PodConfigCleanDeployEntryReply
- type PodConfigCleanDeployEntryRequest
- type PodConfigContainerTunnelReply
- type PodConfigContainerTunnelRequest
- type PodConfigDetectDevicesReply
- type PodConfigDetectDevicesRequest
- type PodConfigListSidecarsReply
- type PodConfigLoadDeployReply
- type PodConfigLoadDeployRequest
- type PodConfigMigrateSecretsReply
- type PodConfigMigrateSecretsRequest
- type PodConfigRemoveReply
- type PodConfigRemoveRequest
- type PodConfigSaveBundleReply
- type PodConfigSaveBundleRequest
- type PodConfigScrubCliEnvReply
- type PodConfigScrubCliEnvRequest
- type PodConfigSetupReply
- type PodConfigSetupRequest
- type PodConfigTunnelResolveReply
- type PodConfigTunnelResolveRequest
- type PodConfigWriteReply
- type PodConfigWriteRequest
- type PodDisposableReply
- type PodDisposableRequest
- type PodExecReply
- type PodLifecyclePlan
- type PodLifecycleReply
- type PodLifecycleRequest
- type PodLiveStdioPlan
- type PodLogsOpts
- type PodLogsPayload
- type PodRemovePayload
- type PodResolveInput
- type PodResolveReply
- type PodServicePayload
- type PodShellOpts
- type PodShellPayload
- type PodSidecar
- type PodSpec
- type PodStartOpts
- type PodStartPayload
- type PodStopOpts
- type PodStopPayload
- type PodUpdatePayload
- type PollClass
- type PollCondition
- type PollConfig
- type PortMapping
- type PortPin
- type PortScope
- type PortSpec
- type PostTeardownReply
- type PreemptLease
- type PreemptLedger
- type PreemptedHolder
- type PreemptibleConfig
- type PrepareVenueReply
- type Process
- type ProcessExecutor
- type ProcessLaunch
- type ProjectLoader
- type ProjectTemplates
- type ProjectWalker
- type ProvidesConfig
- type RCARecord
- type RawBody
- type Readiness
- type ReadinessConfig
- type RebootStep
- type RecoveryDecision
- type RecoveryParams
- type RefsCollectSeams
- type RefsDownloader
- type RemoteDownload
- type RemoteImageResolveReply
- type RemoteImageResolveRequest
- type RenderSeamReply
- type RenderSeamRequest
- type RenderedService
- type RepoBlock
- type RepoChangeStep
- type RepoSpec
- type ResolveOpts
- type ResolvedAndroid
- type ResolvedBox
- type ResolvedBoxView
- type ResolvedDistro
- type ResolvedGpuSelector
- type ResolvedInit
- type ResolvedK8s
- type ResolvedLocal
- type ResolvedPod
- type ResolvedProject
- type ResolvedProjectRequest
- type ResolvedReadiness
- func (rr ResolvedReadiness) PerAttemptFor(class PollClass) time.Duration
- func (rr ResolvedReadiness) PluginEnv() []string
- func (rr ResolvedReadiness) StopGate(name string) PollConfig
- func (rr ResolvedReadiness) ValidateOrdering() error
- func (rr ResolvedReadiness) Wait(name string, class PollClass) PollConfig
- func (rr ResolvedReadiness) WaitCapped(name string, class PollClass, cap time.Duration) PollConfig
- func (rr ResolvedReadiness) WatchProgress(name string, class PollClass, noProgress time.Duration) PollConfig
- type ResolvedResource
- type ResolvedSidecar
- type ResolvedSidecarSecret
- type ResolvedSidecarVolume
- type ResolvedVm
- type ResolvedVolumeMount
- type Resource
- type ResourceDef
- type ResourceResolveInput
- type ResourceResolveReply
- type RetentionReply
- type RetentionRequest
- type ReverseOp
- type ReverseOpKind
- type RouteConfig
- type RouteYAML
- type SSHTarget
- type SaveDeployStateInput
- type ScanSeams
- type ScanSpec
- type ScannedCandy
- type Scope
- type ScoreSummary
- type SecretYAML
- type Security
- type SecurityConfig
- type ServiceCustomStep
- type ServiceEntry
- type ServicePackagedStep
- type ServiceRenderContext
- type ServiceRenderInput
- type ServiceRenderReply
- type Shell
- type ShellEntry
- type ShellHookStep
- type ShellSnippetStep
- type ShellSpec
- type Sidecar
- type SidecarDef
- type SidecarResolveInput
- type SidecarResolveReply
- type SidecarSecret
- type SidecarVolume
- type Size
- type StandaloneLoad
- type Status
- type StatusNestedNode
- type StatusSubstrateReply
- type StatusSubstrateRequest
- type Step
- type StepBatch
- type StepContract
- type StepContractCarrier
- type StepEmitRequest
- type StepKeyword
- type StepKind
- type StepPass
- type StepRecord
- type StepResult
- type StepScore
- type StrMap
- type StrVal
- type StructuralKindCarrier
- type StructuralKindLoadEnv
- type SubstrateKind
- type SubstrateStatusReply
- type SubstrateStatusRequest
- type SubstrateTemplateResolveRequest
- type SystemPackagesStep
- type TagInfo
- type TagPkgConfig
- type TargetHop
- type TargetSpec
- type TerminalExit
- type TerminalFrame
- type TerminalInput
- type TerminalKey
- type TerminalProfile
- type TerminalResize
- type TerminalSnapshot
- type Threaded
- type ToolStatus
- type TunnelConfig
- type TunnelPort
- type TunnelYAML
- type UUIDv7
- type UnifiedFile
- func (uf *UnifiedFile) Android() map[string]json.RawMessage
- func (uf *UnifiedFile) BoxConfig(name string) (BoxConfig, bool)
- func (uf *UnifiedFile) BoxNames() []string
- func (uf *UnifiedFile) CheckBeds() map[string]BundleNode
- func (uf *UnifiedFile) HasBox(name string) bool
- func (uf *UnifiedFile) K8s() map[string]json.RawMessage
- func (uf *UnifiedFile) Local() map[string]json.RawMessage
- func (uf *UnifiedFile) Pod() map[string]json.RawMessage
- func (uf *UnifiedFile) ProjectConfig() *Config
- func (uf *UnifiedFile) ProjectTemplates() *ProjectTemplates
- func (uf *UnifiedFile) SetBox(name string, b BoxConfig)
- func (uf *UnifiedFile) SetCandy(name string, il *InlineCandy)
- func (uf *UnifiedFile) VM() map[string]json.RawMessage
- type UserInfo
- type VFIOGpu
- type VFIOPCIDevice
- type VFIOReport
- type ValidateProjectReply
- type ValidateProjectRequest
- type ValidatingKindCarrier
- type ValidationError
- type Venue
- type VenueDescriptor
- type VerbSpec
- type VerifyChecksRequest
- type Vm
- type VmBuildReply
- type VmBuildRequest
- type VmCharlyInstall
- type VmChecksum
- type VmCloudInit
- type VmCloudInitFile
- type VmCloudInitMirrors
- type VmCloudInitNetwork
- type VmCloudInitUser
- type VmDeployState
- type VmDisplayEndpoint
- type VmKeyInjection
- type VmKeyInjectionResolved
- type VmNetwork
- type VmPluginEnv
- type VmResolveInput
- type VmResolveReply
- type VmResolveResult
- type VmSSH
- type VmSize
- type VmSnapInternalReq
- type VmSnapshot
- type VmSnapshotCreateOpts
- type VmSnapshotDecl
- type VmSnapshotEntry
- type VmSnapshotState
- type VmSource
- type VmSpec
- type VolumeMount
- type VolumeYAML
- type WalkSeams
Constants ¶
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" )
const ( ArbiterActionAcquireExclusive = "acquire-exclusive" ArbiterActionRelease = "release-claimant" ArbiterActionStatus = "status" ArbiterActionReconcile = "reconcile-stranded" ArbiterActionClearPoison = "clear-poison" ArbiterActionResourcePoisoned = "resource-poisoned" )
const ( CheckLevelNone = "none" CheckLevelBuild = "build" CheckLevelNoAgent = "noagent" CheckLevelAgent = "agent" )
The per-box acceptance-depth rungs.
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.
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" )
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).
const ( LabelVersion = "ai.opencharly.version" LabelBox = "ai.opencharly.box" LabelRegistry = "ai.opencharly.registry" LabelBootc = "ai.opencharly.bootc" LabelUID = "ai.opencharly.uid" LabelGID = "ai.opencharly.gid" LabelUser = "ai.opencharly.user" LabelHome = "ai.opencharly.home" LabelPort = "ai.opencharly.port" LabelVolume = "ai.opencharly.volume" LabelAlias = "ai.opencharly.alias" LabelSecurity = "ai.opencharly.security" LabelNetwork = "ai.opencharly.network" LabelEnv = "ai.opencharly.env" LabelHook = "ai.opencharly.hook" LabelRoute = "ai.opencharly.route" LabelInit = "ai.opencharly.init" LabelInitDef = "ai.opencharly.init_def" LabelEnvCandy = "ai.opencharly.env_candy" LabelPathAppend = "ai.opencharly.path_append" LabelPortProto = "ai.opencharly.port_proto" LabelPortRelay = "ai.opencharly.port_relay" LabelSkill = "ai.opencharly.skill" LabelStatus = "ai.opencharly.status" LabelInfo = "ai.opencharly.info" LabelCandyVersion = "ai.opencharly.candy_version" LabelSecret = "ai.opencharly.secret" LabelPlatformDistro = "ai.opencharly.platform.distro" LabelPlatformFormat = "ai.opencharly.platform.format" LabelBuilderUse = "ai.opencharly.builder.use" LabelBuilderProvide = "ai.opencharly.builder.provide" LabelDataEntries = "ai.opencharly.data" LabelDataBox = "ai.opencharly.data_box" LabelEnvProvide = "ai.opencharly.env_provide" LabelEnvRequire = "ai.opencharly.env_require" LabelEnvAccept = "ai.opencharly.env_accept" LabelSecretAccept = "ai.opencharly.secret_accept" // credential-store-backed env vars this image can optionally use LabelSecretRequire = "ai.opencharly.secret_require" // credential-store-backed env vars this image must have LabelMCPProvide = "ai.opencharly.mcp_provide" LabelAgentProvide = "ai.opencharly.agent_provide" LabelTerminalProfiles = "ai.opencharly.terminal_profiles" LabelMCPRequire = "ai.opencharly.mcp_require" LabelMCPAccept = "ai.opencharly.mcp_accept" LabelDescription = "ai.opencharly.description" LabelService = "ai.opencharly.service" LabelShell = "ai.opencharly.shell" LabelCheckLevel = "ai.opencharly.check_level" )
const ( DefaultBoxDir = "box" // discovered box/<name>/ directory DefaultCandyDir = "candy" // discovered candy/<name>/ directory )
layout_paths.go — the stable per-directory discovery path constants (the discovered box/<name>/ and candy/<name>/ dirs). Relocated from sdk/kit/migrate_support.go (#55 import-purity cone-render): plain loader-result DATA the types-only spec module owns, mirroring spec.UnifiedFileName. kit re-exports them via const alias so existing kit.DefaultBoxDir / kit.DefaultCandyDir call sites (plugins + sdk) are untouched.
const ( ReadinessIntervalLocalFallback = readinessIntervalLocalFallback ReadinessIntervalRemoteFallback = readinessIntervalRemoteFallback ReadinessIntervalHeavyFallback = readinessIntervalHeavyFallback ReadinessPerAttemptFallback = readinessPerAttemptFallback ReadinessPerAttemptHeavyFallback = readinessPerAttemptHeavyFallback ReadinessNoProgressFallback = readinessNoProgressFallback ReadinessAbsoluteCapFallback = readinessAbsoluteCapFallback ReadinessStopGraceFallback = readinessStopGraceFallback )
Exported fallback aliases — the exported surface vmshared (and, through it, the VM plugin) re-export as the operator-visible fallback defaults. The unexported consts above stay the intra-spec single source; these expose them across the package boundary without a second literal.
const ( RenderSeamInlineBuilder = "inline-builder" RenderSeamEnsureBuilders = "ensure-builders" )
render_seam.go — the render-seam METHOD discriminators (the RenderSeamRequest.Method values) for the two render-seam methods that still need a host callback (inline-builder, ensure-builders). Relocated from sdk/deploykit/render_seam.go (#55 import-purity cone-render) so charly's host-builder + candy/plugin-build's producer share ONE source without a deploykit import. The per-method param/result structs (#InlineBuilderParams / #InlineBuilderResult / #EnsureBuildersParams) are CUE-sourced (schema/buildresolve.cue) like the outer #RenderSeamRequest itself.
const DefaultCheckLevel = CheckLevelNoAgent
DefaultCheckLevel is the rung applied when a box declares no check_level.
const DefaultProjectRepo = "github.com/opencharly/charly"
DefaultProjectRepo is the repo --repo defaults to when the spec is the literal string "default" (or when charly mcp serve auto-falls back).
const DeployConfigEnv = "CHARLY_DEPLOY_CONFIG"
DeployConfigEnv overrides the per-host deploy-config PATH. A check bed sets it so a disposable run never touches the operator's real ~/.config/charly/charly.yml.
const ExternalStepKindPrefix = "external:"
ExternalStepKindPrefix marks a StepKind string as an external (plugin-contributed) kind.
const HomeToken = "{{.Home}}"
HomeToken is the deferred-home placeholder resolved by ResolveHome at emit time.
const LedgerSchemaVersion = "2026.161.1649"
LedgerSchemaVersion is the install-ledger record format version (the ledger-candy-keys cutover's CalVer). It is INDEPENDENT of the project schema CalVer (LatestSchemaVersion) — a non-ledger schema cutover that bumps the project HEAD must NOT invalidate the ledger gate.
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).
const SchemaFloor = "2026.174.1100"
SchemaFloor is the oldest schema version `charly migrate` can migrate FROM (schema/version.cue #SchemaFloor).
const SchemaVersion = "2026.204.1223"
SchemaVersion is the HEAD schema CalVer (schema/version.cue #SchemaVersion).
const UnifiedFileName = "charly.yml"
UnifiedFileName is the ONE box/candy manifest filename. Canonical loader DATA; ScanSpec below defaults to it. (sdk/kit keeps the sibling layout constants DefaultBoxDir/DefaultCandyDir.)
Variables ¶
var ( // ErrPollStalled — the high-water mark did not advance within NoProgress. ErrPollStalled = errors.New("poll: no forward progress within the no-progress window") // ErrPollCapExceeded — the absolute wall-clock ceiling elapsed. ErrPollCapExceeded = errors.New("poll: absolute cap exceeded") // ErrPollFatal — wrap a cond error in this to abort the poll immediately. ErrPollFatal = errors.New("poll: fatal condition error") // ErrPollConfig — PollUntil's own entry validation rejected the config. ErrPollConfig = errors.New("poll: invalid configuration") )
var AllStepKinds = []StepKind{ StepKindSystemPackages, StepKindBuilder, StepKindOp, StepKindFile, StepKindServicePackaged, StepKindServiceCustom, StepKindShellHook, StepKindShellSnippet, StepKindRepoChange, StepKindApkInstall, StepKindLocalPkgInstall, StepKindReboot, StepKindExternalPlugin, }
AllStepKinds is the fixed InstallStep IR vocabulary — the 13 compiled-in concrete step kinds (external:<word> kinds are dynamic and not listed).
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).
var ContextWords = []string{
"build",
"deploy",
"runtime",
}
ContextWords is the plan-step execution contexts (#Context).
var DocDirectives = []string{
"compiled_plugins",
"context_ignore_baseline",
"defaults",
"discover",
"import",
"ovmf_distro_aliases",
"ovmf_paths",
"providers",
"provides",
"repo",
"version",
}
DocDirectives is the reserved document directives (#NodeDoc top-level keys).
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.
var ErrImageNotLocal = errors.New("image not found in local storage")
ErrImageNotLocal is returned (wrapped with the image ref) when an image is not present in local container storage — a host-side signal, never crossing the plugin wire.
var ErrNotSupported = errors.New("live-stdio (interactive/stream) not supported on this venue")
ErrNotSupported is returned by RunInteractive/RunStream on a venue that cannot host a LIVE-STDIO session (a capture-only / non-interactive executor). Callers surface it as "shell/logs not supported on this substrate" — an explicit, compile-forced failure, never a silent no-op (F12; the honest form of the executor-matrix ErrNotSupported path).
var ExternalizedBuilders = map[string]bool{ "cargo": true, "npm": true, "pixi": true, "aur": true, }
ExternalizedBuilders is THE single source of truth for which builder words are served by an EXTERNAL out-of-process plugin. A word here resolves through the registry to a *grpcProvider connected at plugin-load time.
var InstallVerbs = map[string]bool{ "mkdir": true, "copy": true, "write": true, "link": true, "download": true, "setcap": true, "build": true, }
InstallVerbs are the verbs that render directly to a generic OpStep install step (a Containerfile directive at build, a deploy shell command at deploy). The verbs that lowered into a TYPED install step (package/service) are now extracted plugin verbs whose TypedStepProvider owns the lowering — handled by charly core's opActsInBuildDeploy, not this map.
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.
var KindWords = []string{}
KindWords is the reserved kind keywords (the #Node disjunction discriminators).
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).
var OpInContext func(op *Op, ctx ExecContext) bool
OpInContext reports whether an op runs in the given exec context. Its fallback consults the kernel VerbCatalog (charly), so charly injects the impl at init.
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).
var PluginEmitStepWords = map[StepKind]string{
StepKindFile: "file",
StepKindShellHook: "shell-hook",
StepKindShellSnippet: "shell-snippet",
StepKindServicePackaged: "service-packaged",
StepKindServiceCustom: "service-custom",
StepKindRepoChange: "repo-change",
StepKindApkInstall: "apk-install",
StepKindReboot: "reboot",
StepKindSystemPackages: "system-packages",
StepKindBuilder: "builder",
StepKindLocalPkgInstall: "local-pkg-install",
StepKindOp: "op",
}
PluginEmitStepWords maps a builtin InstallStep kind to the lowercase-hyphenated class:step plugin word that serves its pod-overlay OpEmit (candy/plugin-installstep). These kinds have NO in-proc StepProvider — the OCI dispatch routes them here, serializing the step VIEW as the OpEmit payload. Their DEPLOY leg is unchanged (sdk/kit.WalkPlans renders them from the same view; reboot's is the host-side guest reboot over RunHostStep). apk-install's and reboot's plugin declares Emits=false (no build fragment); every other word Emits=true.
Shared cross-boundary (R3): BOTH the host (charly/provider_step.go's checkStepProviderBijection) and the plugin itself (candy/plugin-installstep's "oci-dispatch" word) consult the SAME map — it belongs with the fixed step-kind vocabulary (AllStepKinds) in the IR envelope.
var ReadinessProvider = func() ResolvedReadiness { rr, _ := ResolveReadiness(nil) return rr }
ReadinessProvider returns the resolved readiness bounds the executors' waits use. Defaults to the built-in bounds; charly overrides it at init with its project-aware loadedReadiness (charly/readiness_config.go).
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).
var StepKeywords = []string{
"agent-check",
"agent-run",
"check",
"include",
"run",
}
StepKeywords is the plan-step intent keywords (#Step arms minus #Op fields).
var TaskAutoExports = map[string]bool{ "USER": true, "UID": true, "GID": true, "HOME": true, "ARCH": true, "BUILD_ARCH": true, }
TaskAutoExports are the auto-exported variable names reserved for the generator; `vars:` entries may not shadow these, and every `${VAR}` reference resolves against (auto-exports ∪ candy.Vars).
var ValidateRecord = func(kind, label string, v any) error { return nil }
ValidateRecord is the egress-validation seam for install-ledger writes. The ledger validates each record against its egress schema before writing; charly injects its ValidateEgressValue here at init. Defaults to a no-op for standalone use.
var VerbCatalog = map[string]VerbSpec{ "mkdir": {ctxBuildDeploy, DoAct, false}, "copy": {ctxBuildDeploy, DoAct, true}, "write": {ctxBuildDeploy, DoAct, true}, "link": {ctxBuildDeploy, DoAct, true}, "download": {ctxBuildDeploy, DoAct, true}, "setcap": {ctxBuildDeploy, DoAct, false}, "build": {ctxBuildDeploy, DoAct, false}, "plugin": {ctxBuildDeployRuntime, DoAssert, false}, }
VerbCatalog is the single source of truth for every core install-verb's legality, default do-mode, and reversibility — one table driving validation, dispatch, and lowering. Keys match spec.OpVerbs (gated by the registry bijection in charly/reserved_registry.go).
Functions ¶
func BareCandyRef ¶
BareCandyRef returns the map-key form of a ref (no @ prefix, no :version).
func BedCheckLiveRefs ¶ added in v0.2026213.1726
func BedCheckLiveRefs(name string, children map[string]*BundleNode) []string
BedCheckLiveRefs returns the ordered `charly check live` targets for a bed: the substrate itself first, then each nested child as a sorted dotted path. A `target: android` child shares the parent pod's venue (Descent.Venue == "parent") and has no own image — its app-presence checks are baked into the parent ref, so it is skipped. Pure + unit-tested.
func BoxNamesOf ¶
BoxNamesOf returns the image names in a generic image map, sorted for determinism.
func BuilderPhaseTemplate ¶ added in v0.2026213.1726
BuilderPhaseTemplate is the Builder analog of FormatPhaseTemplate. Same fallback rules apply: (PhaseInstall, container) falls back to the legacy inline InstallTemplate when Phases is absent.
func BundleDelArgv ¶ added in v0.2026211.549
BundleDelArgv returns the argv (everything after the charly binary) for a non-interactive `charly bundle del <name>`: the verb, the name, and the ONE valid skip-confirmation flag. Every programmatic teardown builds its command through this single helper — in-process (proc.RunCharlySubcommand), out-of-process (exec.Command), and the systemd-run TTL timer — so the flag can never drift across call sites again (R3 hoist: this was byte-identically duplicated in charly core, candy/plugin-bundle, and candy/plugin-substrate).
The flag is `--assume-yes`, NOT `--yes`/`--force`: the command:bundle plugin's `charly bundle del` Kong grammar (candy/plugin-bundle) declares its AssumeYes field `name:"assume-yes"`, with `-y` as the short form. That tag used to read `long:"yes"` — go-flags syntax Kong ignores, so the flag was only ever --assume-yes by accident of Kong's field-name derivation; the whole tree was converted to `name:` so the declaration states what ships. A `--yes`/`--force` drift — neither of which Kong accepts — once aborted teardown at arg-parse and silently leaked the resource (see CHANGELOG/); the deploy-del-flag regression test guards this.
func CandyArtifactRegisters ¶ added in v0.2026213.1726
func CandyArtifactRegisters(layers []CandyReader) map[string]bool
CandyArtifactRegisters returns the DISTINCT `register:` hints declared across every candy's artifact list — name-blind (it reads each artifact's own declaration, never a candy name).
func CandyCapabilitiesError ¶ added in v0.2026213.1726
func CandyCapabilitiesError(layers map[string]CandyReader, order []string, missing []string) error
CandyCapabilitiesError formats a missing-capabilities error with a remediation hint pointing at which candies requested what.
func CandyMapKey ¶ added in v0.2026213.1726
func CandyMapKey(layer CandyReader) string
CandyMapKey returns the candy's map key: the full @github ref for a remote candy (RepoPath/SubPathPrefix+Name), else the bare Name.
func CandyStageDirName ¶ added in v0.2026213.1726
func CandyStageDirName(layer CandyReader) string
CandyStageDirName returns the content-addressed staging dir name for a remote candy's copied tree (name.version).
func CheckLevelReaches ¶ added in v0.2026213.1726
CheckLevelReaches reports whether a box resolved to `have` runs at least as deep as `want` — e.g. CheckLevelReaches(boxLevel, CheckLevelNoAgent) gates the runtime live pass, CheckLevelReaches(boxLevel, CheckLevelAgent) gates the agent grader. Both operands are normalized through ResolveCheckLevel first.
func CheckRequiredCapabilities ¶ added in v0.2026213.1726
func CheckRequiredCapabilities(layers map[string]CandyReader, order []string, agg *AggregatedCandyCaps) []string
CheckRequiredCapabilities returns a sorted list of capability names requested via `requires_capabilities:` on any candy in `order` but not provided by the aggregated capabilities. Empty slice on success.
func ClassifyNodeTarget ¶ added in v0.2026213.1726
func ClassifyNodeTarget(node *BundleNode, path string) string
ClassifyNodeTarget picks the target discriminator for a node. Uses node.Target when non-empty (canonical pod|vm|k8s|local|android, set from the node-form kind by the loader's bundleTargetForDisc). For ref-based deploys with no charly.yml entry, the deploy name itself is the hint: a literal `host`/`local` LEAF → local target; anything else → pod. A pure function of node+path with no LoadUnified/executor dependency.
func CompareCalVer ¶ added in v0.2026213.1726
CompareCalVer compares two CalVer strings numerically component-by-component, falling back to lexical comparison for any non-numeric component. Returns -1 if a < b, +1 if a > b, 0 if equal. Distinct from ParsedCalVer.Less, which requires strictly-canonical parsed CalVers; this is the lenient dotted-string comparator the build/tag paths use.
func ComputeCalVer ¶ added in v0.2026213.1726
func ComputeCalVer() string
ComputeCalVer returns the canonical build tag for the current UTC instant.
func ComputeCalVerAt ¶ added in v0.2026213.1726
ComputeCalVerAt formats t as the canonical CalVer: 4-digit year, 3-digit zero-padded day-of-year, 4-digit zero-padded HHMM. Every component is fixed-width, so a plain lexicographic sort of CalVer strings is chronological.
func ContainerName ¶ added in v0.2026213.1726
ContainerName returns the deterministic container name for an image or a `<base>/<instance>` deploy key (the `/` is canonicalized to `-`).
func ContainerNameInstance ¶ added in v0.2026213.1726
ContainerNameInstance returns the container name with an optional instance suffix.
func CurrentUsername ¶ added in v0.2026210.1121
func CurrentUsername() string
CurrentUsername returns $USER or falls back to "charly" if neither $USER nor $LOGNAME is set.
func DecodePluginKindMap ¶ added in v0.2026210.808
func DecodePluginKindMap[T any](uf *UnifiedFile, kind string) map[string]*T
DecodePluginKindMap reconstructs the typed name-keyed map[string]*T for a plugin kind from uf.PluginKinds[kind] (each body the canonical T JSON the kind plugin's Invoke produced) via a PLAIN json.Unmarshal — no plugin OpResolve round-trip, for a kind whose body is already self-contained (Builder). Compare ResolvePluginKindViaPlugin above, its sibling for kinds that DO need a plugin-side resolve leg. A bad entry is skipped rather than poisoning the whole vocabulary. Returns nil when the kind has no entities.
func DefaultDeployConfigPath ¶ added in v0.2026213.1726
DefaultDeployConfigPath returns the per-host deploy overlay file (~/.config/charly/charly.yml), honoring the DeployConfigEnv override.
func DeployKey ¶ added in v0.2026211.549
DeployKey builds the charly.yml deployment map key from a box name and an optional instance: "selkies-desktop" (no instance) or "selkies-desktop/foo" (instance "foo"). The inverse is ParseDeployKey.
func DeployNestedLocalChildren ¶ added in v0.2026213.1726
func DeployNestedLocalChildren(parent string, children map[string]*BundleNode, apply func(childKey, dotted string) error) error
DeployNestedLocalChildren deploys a parent venue's nested target:local children via the dotted-path dispatch — each host-rooted (local/SSH-shell) child applies its candies in place. Promoted from sdk/deploykit (#55 U4), now that HostRooted is a spec predicate; deploykit keeps a re-export forwarder so its charly callers compile unchanged.
plugin-deploy-vm's PostApply brings up nested target:pod children as in-guest quadlets, but it SKIPS target:local children — they carry no image, they apply candies in place. Without this loop a nested local child never deploys, and a deploy-scope check against it either fails or (worse) silently checks nothing.
Both sites that own a VM venue call it: the isVM bed ROOT and bringUpMembers' VM-member branch. They differ only in how a child deploy is executed (the root wraps it in a recorded step(); a member shells out directly), so that is the injected apply func.
func DeriveDeploymentName ¶ added in v0.2026211.549
DeriveDeploymentName turns "quay.io/myorg/openclaw:v1" → "openclaw" and "registry.example.com/path/foo" → "foo" — the shared default-name derivation for a source-less `charly bundle from-box` deploy (both the pod path, charly/bundle_from_box_cmd.go, and the k8s path, candy/plugin-bundle/deploy_from_box.go — R3, one function, two callers).
func DescriptionInfo ¶ added in v0.2026213.1726
DescriptionInfo returns the first non-empty line of a candy/box description, trimmed.
func DiscoverSkipDir ¶ added in v0.2026213.1726
DiscoverSkipDir reports whether a directory name is a VCS or build-artifact dir that never contains a discoverable charly.yml manifest — skipped by the discover walk both for speed and to avoid traversing concurrently-mutated build outputs (e.g. a candy's pkgbuild/{pkg,src} under a live makepkg).
func DistroTagChain ¶ added in v0.2026210.2052
DistroTagChain builds the most-specific-first distro tag chain used by the cascade resolver: [<distro>:<version>, <distro>] when a canonical version is known (e.g. ["ubuntu:24.04", "ubuntu"]), or just [<distro>] for a rolling distro with no version (arch/cachyos).
func EncodeBox ¶
func EncodeBox(b BoxConfig) json.RawMessage
EncodeBox marshals an authored BoxConfig into its opaque body.
func EncodeInlineCandy ¶ added in v0.2026210.808
func EncodeInlineCandy(il *InlineCandy) json.RawMessage
EncodeInlineCandy marshals a loader InlineCandy into its opaque body.
func EnvMapToPairs ¶ added in v0.2026213.1726
EnvMapToPairs converts the deploy schema's env map into sorted KEY=VALUE pairs (the OCI-label wire + env-resolution chain form).
func ExpandPath ¶ added in v0.2026213.1726
ExpandPath expands ~, ${HOME} and $HOME in a path string to the given home directory. ${HOME} is replaced before bare $HOME so the braced form is handled (a bare $HOME ReplaceAll would not match "${HOME}").
Relocated from sdk/kit/env.go (#55 import-purity cone-render): a pure fabric path helper with no host dependency. kit.ExpandPath now aliases spec.ExpandPath (R3, one source), and spec.BuildServiceRenderContext consumes it in-package.
func ExternalInPlaceVenue ¶ added in v0.2026217.301
func ExternalInPlaceVenue(node *BundleNode) bool
ExternalInPlaceVenue reports whether node's stamped venue is an EXTERNAL deploy substrate that applies its workload IN PLACE — local-like: no container image to build, no `charly config`/`charly start`, teardown via `charly bundle del` (replay the recorded reverse ops). local/android/k8s/exampledeploy are in-place (parent/none venues); pod is the one externalized substrate that is NOT in-place (excluded by requiring venue != container implicitly, since parent/none never equals container). Mirrors HostRooted's shape (#55 W3 B2-full) — the plugin-reachable equivalent of the former core-private bedExternalInPlace(target string), which queried isExternalDeploySubstrate against the live provider registry: every node this predicate sees comes from an already-loaded, Descent-stamped project, so the registry round-trip was redundant with data already on the wire (the SAME finding candy/plugin-bundle's externalInPlaceFromDescent already proved for a bed's sibling MEMBERS — this promotes that one shared predicate for the bed ROOT too, R3).
func FileExists ¶ added in v0.2026213.1726
FileExists reports whether path exists and is a regular (non-dir) file.
func FinalizeCandyRefs ¶
func FinalizeCandyRefs(m *CandyModel, v *CandyView, refs CandyRefs)
FinalizeCandyRefs projects the RICH pre-qualification refs (CandyRefs) down to their bare-string FINAL wire form — v.Require / v.IncludedCandy (CandyView) and m.BakePlugin (CandyModel) — run ONCE, after any remote-sibling qualification (loaderkit.QualifyRemoteSiblingDeps) has had the chance to set .Resolved. Mirrors the pre-move projectCandyView/projectCandyModel bare-ref projection (charly/resolved_project_host.go's bareRefs() calls, pre-W9), which ran AFTER qualifyRemoteSiblingDeps on the live *Candy. Lives in spec (not loaderkit, alongside its sibling CandyRefEntry/ToCandyRefEntries) — a pure, non-registry-coupled data-shape transform charly core must reach directly at the fix-point arbitration's final step (import-purity: core imports spec but never an sdk mechanism kit).
func FindEntityDirs ¶ added in v0.2026213.1726
FindEntityDirs walks a scan root and returns every directory that contains the given canonical filename. When recursive is false, only the immediate children of path are considered.
func FirstYAMLVersionLine ¶ added in v0.2026213.1726
FirstYAMLVersionLine extracts the value of the first top-level `version:` line.
func FlattenedEnvMap ¶ added in v0.2026213.1726
func FlattenedEnvMap(base map[string]string, overrides *CandyServiceOverrides) map[string]string
FlattenedEnvMap merges an entry's base env with its overrides' env (overrides win).
func FormatCacheMount ¶ added in v0.2026213.1726
FormatCacheMount renders ONE cache mount as a Containerfile `--mount=type=cache,…` flag. uid>=0 → the owned form (uid/gid baked into the id namespace so different-uid builds don't collide on file ownership inside the cache volume); uid<0 → the shared form (sharing mode, defaulting to "locked" when empty — the BuildKit default for root-installed system caches). The id is derived from dst (and uid for owned), keeping the cache stable across layer-hash churn during iterative builds — the entire reason a derived id exists.
func FormatHTTPHeaders ¶ added in v0.2026213.1726
FormatHTTPHeaders renders an http.Header into a "Key: value\n" blob (one line per value, multi-value preserved) — the matcher-ready response-header form.
func FormatPhaseTemplate ¶ added in v0.2026213.1726
FormatPhaseTemplate looks up the template string for a (phase, venue) lookup, with documented fallback behavior: if the new phase: block lacks the requested cell, fall back to the legacy InstallTemplate for (PhaseInstall, container) only — the combination covered by the legacy field. All other lookups return "" when the new path is absent.
func FormatPortMapping ¶ added in v0.2026213.1726
func FormatPortMapping(p ParsedPortMapping) string
FormatPortMapping is the inverse of ParsePortMapping. Empty bindAddr / proto are omitted; trailing-zero / equal ports collapse to canonical short forms that podman accepts.
func GateEnabled ¶ added in v0.2026210.2308
GateEnabled returns whether the given gate is permitted under opts. GateNone is always enabled; named gates require the corresponding opt-in flag.
func GitRemoteIdentity ¶ added in v0.2026213.1726
GitRemoteIdentity returns the normalized `host/owner/repo` identity of dir's git `origin` remote, or "" when dir is not a git repo / has no origin / git is unavailable.
func HTTPClientFor ¶ added in v0.2026213.1726
HTTPClientFor builds a per-request *http.Client honoring the CheckHTTPRequest policy (AllowInsecure, NoFollowRedirects, CAPEM, Timeout), derived from the engine's base client. The base supplies the default timeout; req.Timeout overrides it.
func HostRooted ¶ added in v0.2026213.1726
func HostRooted(node *BundleNode) bool
HostRooted reports whether node's stamped descent trait is host-rooted — the substrate's own ROOT executor runs directly on the host (a local/SSH-shell venue, not a container/VM venue). Reads the wire-stamped node.Descent directly (every node a LoadUnified'd project produces is Descent-stamped by StampDescent), so it needs no registry access. Promoted from sdk/deploykit's former private host-rooted predicate (#55 U4) so DeployNestedLocalChildren + the bed-session apply path (PersistBedDeployOverrides) share ONE predicate over the spec value type; deploykit callers repoint to spec.HostRooted directly.
func InputStr ¶ added in v0.2026213.1726
InputStr reads a string field from the step's desugared plugin input map.
func IsContainerVenue ¶ added in v0.2026217.301
func IsContainerVenue(node *BundleNode) bool
IsContainerVenue reports whether node's stamped venue is the container-exec (pod) substrate. Mirrors HostRooted's shape (#55 W3 A4) — see IsVmVenue.
func IsExternalStepKind ¶ added in v0.2026210.2308
IsExternalStepKind reports whether k is an external (plugin-contributed) step kind.
func IsRemoteCandyRefString ¶
IsRemoteCandyRefString reports whether a ref is an @-prefixed remote ref.
func IsRemoteImageRef ¶
IsRemoteImageRef returns true if a ref looks like a remote image reference (starts with @).
func IsValidCheckLevel ¶ added in v0.2026213.1726
IsValidCheckLevel reports whether level is one of the four canonical rungs.
func IsVmVenue ¶ added in v0.2026217.301
func IsVmVenue(node *BundleNode) bool
IsVmVenue reports whether node's stamped venue is the SSH-hop (vm) substrate. Mirrors HostRooted's shape (#55 W3 A4) — promoted so a plugin-side deploy-orchestration consumer (sdk/deploykit's BringUpMembers/TearDownMembers) and any future caller share ONE predicate over the wire-stamped node.Descent, instead of each re-deriving the venue check independently.
func LeafName ¶
LeafName strips every namespace prefix from a (possibly qualified) ref, returning the final member name — e.g. "charly.arch-builder" -> "arch-builder", "a.b.c" -> "c", bare "fedora" -> "fedora".
func MappingRoot ¶
MappingRoot unwraps a YAML document node to its top-level mapping node (or nil). Shared by charly core and the out-of-module candy/plugin-migrate engine (R3).
func MatchAll ¶ added in v0.2026215.1748
MatchAll returns nil if every matcher succeeds against the value. The first failure wins (reports the specific unmet expectation).
Takes []Matcher rather than MatcherList so callers can pass any named slice type whose underlying element is Matcher (e.g. ContainsList) without an explicit conversion at every call site.
Shared by the core check runner and out-of-tree verb plugins.
func MatchValueString ¶ added in v0.2026215.1748
MatchValueString coerces a matcher's stored Value (any) to a string. For numeric types it renders canonically; for everything else it falls back to fmt.Sprint.
Shared by the core check runner and out-of-tree verb plugins.
func MatchValueStrings ¶ added in v0.2026215.1748
MatchValueStrings handles list-valued matchers like {contains: [a, b]}. A scalar value becomes a singleton list.
Shared by the core check runner and out-of-tree verb plugins.
func MergePluginKindsMap ¶ added in v0.2026213.1726
func MergePluginKindsMap(dst *map[string]map[string]json.RawMessage, src map[string]map[string]json.RawMessage)
MergePluginKindsMap merges src's plugin-kind entities INTO *dst under project-wins semantics: an entity already present in dst (by kind, then name) is never overwritten. A pure map/json.RawMessage merge over the plugin-kind carrier shape, relocated to the dedicated spec module (#55 2b) so the binary-embedded-defaults merge (charly/embed_defaults.go) reaches it without importing loaderkit; loaderkit re-exports it as a forwarder for its own merge path.
func MergeUnified ¶ added in v0.2026213.1726
func MergeUnified(dst, src *UnifiedFile, srcDir string)
MergeUnified merges src into dst such that dst's existing values WIN on conflict at the same leaf (root-wins). This means when MaterializeLoadedProject replays the walk's documents in order (the root file first, then its flat imports), the root file's values are already present before any import's fields are considered, so root wins.
For included files: the same MergeUnified is called but dst already contains the root's values, so those fields stay untouched. src's fields that aren't present in dst get copied over. That's the desired semantics.
func NormalizeGitRemoteURL ¶ added in v0.2026213.1726
NormalizeGitRemoteURL strips the scheme / `git@` / `.git` decorations from a git remote URL, leaving `host/owner/repo`. scp-style (`git@host:owner/repo`) and scheme URLs (`https://`, `ssh://`, `git://`, with optional `user@`) are both handled. A value already in `host/owner/repo` (or bare `owner/repo`) form passes through unchanged.
func NormalizePCIVendor ¶
NormalizePCIVendor lowercases a PCI vendor id and ensures the "0x" prefix, so "10DE" / "0X10de" / "0x10de" all compare equal to the sysfs form ("0x10de").
func NormalizeRepoIdentity ¶ added in v0.2026213.1726
NormalizeRepoIdentity normalizes an explicit `repo:` value (which may be a full git URL, an scp-style ref, or a bare `owner/repo`) to the `host/owner/repo` form ParseRemoteRef produces — so an explicit declaration and a remote `@`-ref compare equal. Reuses NormalizeRepoSpec's bare-`owner/repo` → github.com rule.
func NormalizeRepoSpec ¶ added in v0.2026213.1726
NormalizeRepoSpec turns a user-supplied --repo spec into a (repoPath, version) pair suitable for a repo-cache download. Spec formats:
"default" → (DefaultProjectRepo, "")
"owner/repo" → ("github.com/owner/repo", "")
"owner/repo@ref" → ("github.com/owner/repo", "ref")
"host/owner/repo[@ref]" → used literally
An empty version means "resolve to default branch at lookup time".
func PackageNames ¶
func PackageNames(items []PackageItem) []string
PackageNames returns just the names from a PackageItem list, in order. Convenience for places that only need the install-target list.
func ParseDeployKey ¶ added in v0.2026211.549
ParseDeployKey splits a charly.yml map key back into image name and instance. "selkies-desktop" → ("selkies-desktop", "") "selkies-desktop/foo" → ("selkies-desktop", "foo")
func PathIsSystemScoped ¶ added in v0.2026210.2308
PathIsSystemScoped returns true for paths under /etc, /usr, /var, /opt, /root, /boot and similar system locations. Used to classify FileStep scope when the compiler hasn't explicitly tagged it.
func PathLeaf ¶ added in v0.2026213.1726
PathLeaf returns the last segment of a dotted deployment path. "foo.bar.baz" -> "baz"; "foo" -> "foo"; "" -> "". Unlike SplitDottedPath, a malformed path (leading/trailing/doubled dots) still yields its raw trailing segment rather than nil — callers that only care about the LEAF (e.g. a "host"/"local" literal-name check) want the tolerant form.
func PodName ¶ added in v0.2026213.1726
PodName returns the container name for a pod's primary container.
func PodNameInstance ¶ added in v0.2026213.1726
PodNameInstance returns the container name for a pod's primary container, instance-aware.
func PodQuadletFilename ¶ added in v0.2026213.1726
PodQuadletFilename returns the quadlet filename for a pod.
func PodQuadletFilenameInstance ¶ added in v0.2026213.1726
PodQuadletFilenameInstance returns the quadlet filename for a pod with optional instance.
func PollUntil ¶
func PollUntil(ctx context.Context, cfg PollConfig, cond PollCondition) error
PollUntil drives cond to readiness using the modes described on PollConfig. Returns nil on ready; a %w-wrapped ErrPollStalled / ErrPollCapExceeded / ErrPollFatal / ErrPollConfig, or ctx.Err(), otherwise. Context-cancellable; deterministic under an injected clock (zero real sleeping in tests).
func QuadletDir ¶ added in v0.2026213.1726
QuadletDir returns the user-level quadlet directory.
func QuadletExists ¶ added in v0.2026213.1726
QuadletExists checks whether a .container file exists for the given image.
func QuadletExistsInstance ¶ added in v0.2026213.1726
QuadletExistsInstance checks whether a .container file exists for the given image/instance.
func QuadletFilename ¶ added in v0.2026213.1726
QuadletFilename returns the quadlet filename for an image.
func QuadletFilenameInstance ¶ added in v0.2026213.1726
QuadletFilenameInstance returns the quadlet filename for an image with optional instance.
func RenderCacheMounts ¶ added in v0.2026213.1726
func RenderCacheMounts(mounts []CacheMount, uid, gid int, sep string, trailing bool) string
RenderCacheMounts joins a slice of spec.CacheMount into one Containerfile flag string. uid<0 → shared form (sharing-locked); uid>=0 → owned form. `trailing` appends the separator after the last entry — needed by cacheMountsOwned which feeds directly into a multi-line RUN body. Single source of truth for the slice-rendering pattern that previously lived inline at four call sites (two template helpers + the former generate.go + tasks.go cmd-emitter); every multi-mount site now flows through here, every single-mount site flows through FormatCacheMount (via buildkit's CacheMount.String()) directly.
func RenderCacheMountsAuto ¶ added in v0.2026213.1726
func RenderCacheMountsAuto(mounts []CacheMount, uid, gid int, sep string, trailing bool) string
RenderCacheMountsAuto renders a MIXED list where each entry is owned (uid/gid) or shared per its own `owned:` flag — letting one builder declare both root system caches (pacman → shared/locked) and user build caches (makepkg SRCDEST, yay AUR clones → uid/gid-owned) in a single cache_mount list. uid/gid apply only to the entries flagged owned; the rest render in the shared form.
func RepoIdentity ¶ added in v0.2026213.1726
RepoIdentity returns the canonical repo identity of an import ref, or "" when it can't be determined (in which case the loader degrades to version-keyed behavior). A remote `@host/org/repo[/sub]:ver` ref yields `host/org/repo` directly (no fetch, no git); a local path yields the git `origin` identity of the target directory.
func ResolveBoxName ¶ added in v0.2026213.1726
ResolveBoxName extracts the short box name from a ref that may be a local box name or a remote ref (github.com/org/repo/box[@version]).
func ResolveCheckLevel ¶ added in v0.2026213.1726
ResolveCheckLevel normalizes an authored check_level to a canonical rung, applying the default for the empty value. An unknown value is returned verbatim so the validator flags it — never silently defaulted.
func ResolveHome ¶ added in v0.2026210.2308
func ResolveHome(p *InstallPlan, home string)
ResolveHome substitutes the deferred HomeToken with a concrete home in every home-bearing step field, in place. Each DeployTarget calls this once at emit time with the home of its real destination: img.Home for the OCI/pod-overlay build, the host home for the external local deploy, the GUEST home (SSH executor ResolveHome) for the external vm deploy. Idempotent — fields without the token are left untouched, so a second call is a no-op.
Covered fields: ShellHookStep env values + PathAdd, ShellSnippetStep Snippet + Destination + PathAppend, FileStep.Dest. OpStep cmd/content bodies are intentionally NOT touched — `~`/`$HOME` there shell-expand at runtime on the destination as the deploy user, which is already correct on every venue. BuilderStep is also untouched — its home is resolved separately by renderBuilderScript against the builder/guest home (see execBuilder).
A free function (not a method on InstallPlan) because it type-switches the concrete step vocabulary.
func ResolveNodePath ¶ added in v0.2026213.1726
func ResolveNodePath(roots map[string]BundleNode, path string) (*BundleNode, []*BundleNode, error)
ResolveNodePath resolves a dotted deployment path against a root map, returning the leaf node, its ancestor chain, and any lookup error.
func ResolvePluginKindViaPlugin ¶ added in v0.2026210.808
func ResolvePluginKindViaPlugin[T any](uf *UnifiedFile, kind string, resolve func(json.RawMessage) (*T, error)) map[string]*T
ResolvePluginKindViaPlugin projects uf.PluginKinds[kind] (opaque bodies) into *T value envelopes via resolve, a per-body plugin OpResolve leg — the ONE shared loop every "resolve a plugin-kind catalog through its OpResolve leg" accessor uses (charly-core's Distros/Builders/ resolveResources/resolveAndroids/resolveInits each supply their own registry-coupled resolve callback; this loop itself touches no registry). A bad entry is skipped rather than poisoning the whole vocabulary.
func RootRepoIdentity ¶ added in v0.2026213.1726
RootRepoIdentity determines the local root project's own repo identity for cycle-break registration. An explicit `repo:` field in charly.yml is authoritative; otherwise it falls back to the `git remote origin` identity of the working tree. Returns "" when neither is available (the loader then behaves exactly as before — version-keyed, no self-identity short-circuit).
func ServiceName ¶ added in v0.2026213.1726
ServiceName returns the systemd service name for an image.
func ServiceNameInstance ¶ added in v0.2026213.1726
ServiceNameInstance returns the systemd service name for an image with optional instance.
func ShellQuote ¶ added in v0.2026210.1121
ShellQuote wraps s in single quotes for safe interpolation into a shell command line, escaping any embedded single quotes via the standard '\” idiom. It is a pure stdlib FABRIC primitive (POSIX single-quoting) with exactly one definition across the tree (#55 step1): the process-launch renderer (github.com/opencharly/spec/proc RemoteLaunchCommand), the sdk/kit deploy executors, the sdk/deploykit Containerfile emitter, and every plugin candy that quotes a shell token all call spec.ShellQuote — one source, no duplicate quoter (R3).
func SidecarContainerName ¶ added in v0.2026213.1726
SidecarContainerName returns the container name for a named sidecar.
func SidecarContainerNameInstance ¶ added in v0.2026213.1726
SidecarContainerNameInstance returns the container name for a named sidecar, instance-aware.
func SidecarQuadletFilename ¶ added in v0.2026213.1726
SidecarQuadletFilename returns the quadlet filename for a sidecar container.
func SidecarQuadletFilenameInstance ¶ added in v0.2026213.1726
SidecarQuadletFilenameInstance returns the quadlet filename for a sidecar with optional instance.
func SortedDeployKeys ¶ added in v0.2026213.1726
func SortedDeployKeys(m map[string]BundleNode) []string
SortedDeployKeys returns the deploy-map keys in deterministic (sorted) order.
func SortedMemberKeys ¶ added in v0.2026213.1726
func SortedMemberKeys(members map[string]*BundleNode) []string
SortedMemberKeys returns the member keys of a node in deterministic order.
func SortedNestedKeys ¶ added in v0.2026213.1726
func SortedNestedKeys(children map[string]*BundleNode) []string
SortedNestedKeys returns the keys of a children map in deterministic order so traversal produces stable output across runs.
func SplitDottedPath ¶ added in v0.2026213.1726
SplitDottedPath splits a dotted deployment path into segments. An empty input or a path with any empty segment (leading/trailing/doubled dots) yields nil so callers can flag the error at their layer with the original offending path string.
func SplitNamespaceRef ¶
SplitNamespaceRef splits a qualified ref on its FIRST `.` into (namespace, remainder). A bare ref (no dot, or a leading/trailing dot) returns ok=false. The remainder may itself be qualified (`a.b.c` → "a", "b.c").
func SplitRepoAndSubPath ¶
SplitRepoAndSubPath splits a ref into repo path (host/org/repo), sub-path, and name. e.g. "github.com/org/repo/candy/name" -> ("github.com/org/repo", "candy/name", "name") For short refs like "pixi", returns ("", "", "pixi").
func SplitVmAddress ¶ added in v0.2026213.1726
SplitVmAddress detects the "vm:"-prefixed CLI ADDRESSING form (`charly bundle add/del vm:<name>` / `vm:<parent.child>`) and returns the address with that prefix stripped, plus whether it was present. "vm:" here is an ADDRESSING HINT — "resolve this via the vm substrate" — NEVER an identity itself; a caller that needs the plain (tree-lookup / ledger-identity) form strips it via this helper, one which needs the sanitized dc.Bundle key form still applies "vm:"+VmDomainIdentity(...) separately (a DIFFERENT canonical form).
NOT the same job as VmNameFromDeployName (which extracts the VM ENTITY and errors when the prefix is ABSENT — a different, already-established, unchanged contract) or VmDomainIdentity (which sanitizes dots/slashes for a domain-identity STRING, unconditionally, prefix or not). Relocated from sdk/vmshared (#55 vmshared Bucket B); vmshared keeps a re-export forwarder.
func StampDescent ¶ added in v0.2026213.1726
func StampDescent(node *Deploy, traitsFor func(word string) *DeployTraits)
StampDescent stamps node.Descent from the substrate's DECLARED traits (resolved by `traitsFor(node.Target)`) and recurses into the whole nested (Children) + peer (Members) subtree, so every node the deploy chain can descend into carries a descriptor. Idempotent — re-stamping with the same traitsFor writes the same value. traitsFor must never be nil; it returns nil for a word with no declared substrate traits (a targetless group / empty target), which DescentFromTraits maps to the external-in-place default.
func StatusFromState ¶
StatusFromState normalises an engine/container runtime state vocabulary to charly's DeploymentStatus.Status vocabulary. Shared by the substrate plugins' status collectors (the pod live row builder in candy/plugin-substrate) AND the host-side vm collector (charly/status_collect_vm.go, deploy-cone-coupled → stays core until K5) — single-sourced here (R3) so the live/enrich split (P14a) and the K5 vm-collector move both read ONE mapper. Pure over its input.
func StripCandyRefVersion ¶
StripCandyRefVersion splits an @-prefixed ref into its bare form and pinned version. A non-@ ref returns (ref, "").
func StripPortSuffix ¶ added in v0.2026213.1726
StripPortSuffix removes /tcp or /udp protocol suffix from a port string. "47998/udp" -> "47998", "udp"; "8000" -> "8000", ""
func StripURLScheme ¶ added in v0.2026213.1726
StripURLScheme removes http:// or https:// from a remote ref if present.
func SystemdUserDir ¶ added in v0.2026213.1726
SystemdUserDir returns the user-level systemd unit directory (~/.config/systemd/user/).
func TaskKnownNames ¶ added in v0.2026213.1726
TaskKnownNames returns the ${NAME} references that resolve cleanly for this candy: auto-exports ∪ candy.Vars keys.
func TrimPreview ¶ added in v0.2026213.1726
TrimPreview truncates s to a 200-char preview (trailing "…") for compact check-output display. Homed in the fabric slice github.com/opencharly/spec/spec (the same fabric-primitive class as ShellQuote) so both the executor slice (spec/exec) and kit reach the ONE copy — kit re-exports it (kit.TrimPreview) so its existing callers compile unchanged (R3, single source).
func ValidateDeployRequiresBox ¶
func ValidateDeployRequiresBox(deploy map[string]BundleNode) error
ValidateDeployRequiresBox enforces the 2026-05-12 schema rule: every `target: pod` deploy entry MUST declare its `box:` field. Pre-cutover the check runner silently fell back to inspecting the running container's image ref via `containerImageRef`, which read stale OCI labels off volume-pinned containers and dropped any probes added after the seed image. The hard-required field forces operator intent to be explicit; the check runner now resolves the ref ONLY from this field.
Scope: target: pod (or empty — pod is the default). target: vm uses `vm:`, target: local is candy-driven, target: k8s CLUSTER definitions live in the `k8s:` section (not deploy:).
Remediation: `charly migrate` (idempotent) walks every affected deploy and injects the field, inferring the value from the deploy key (`<base>` for `<base>/<instance>` keys; the key itself otherwise).
func ValidateDeploymentChildren ¶
func ValidateDeploymentChildren(path string, node *BundleNode) error
ValidateDeploymentChildren recurses ValidateDeploymentName over node's nested-deployment children.
func ValidateDeploymentName ¶
ValidateDeploymentName rejects a deploy-tree key containing ".", reserved for dotted-path CLI addressing (`charly bundle add a.b.c`).
func ValidateDeploymentTree ¶
func ValidateDeploymentTree(deploy map[string]BundleNode) error
ValidateDeploymentTree enforces structural invariants on the deployments tree that can't be expressed in the YAML struct tags:
- Map keys at every level MUST NOT contain "." (dots are reserved for dotted-path CLI addressing like `charly bundle add a.b.c`).
- Every explicit pod deploy must declare `box:` (ValidateDeployRequiresBox).
Errors include the offending path so the user sees exactly which entry needs to be fixed.
func VenueIsAgentProvisioned ¶ added in v0.2026213.1726
func VenueIsAgentProvisioned(uf *UnifiedFile, venue string) bool
VenueIsAgentProvisioned reports whether the deploy node named venue (a child or member anywhere in the bundle tree) is flagged AgentProvisioned — the ONE genuinely bundle-tree-coupled predicate the check-run preflight needs to skip an agent-provisioned image's local-storage ensure.
func VmDomainIdentity ¶
VmDomainIdentity normalizes a deploy/bundle name into the per-deploy VM DOMAIN IDENTITY — the token that keys the libvirt domain (charly-<identity>), the per-domain state dir, the managed ssh-config alias, and the ssh-port ledger entry (vm:<identity>). It is DISTINCT from the kind:vm ENTITY (the disk/spec source, resolved via the deploy's `from:` cross-ref): several distinct beds may share one entity, so keying the domain by the ENTITY collided them on one libvirt domain + one disk + one host SSH port. Keying by the DEPLOY NAME instead makes distinct beds collision-free by construction (each gets its own domain, per-deploy disk overlay, and auto-allocated port).
The normalization strips a leading "vm:" prefix and flattens the instance ("/") and nested-path (".") separators to "-", so a bare bed name maps to itself (check-builder-vm → check-builder-vm), a bundle ref maps to its VM token (vm:arch → arch), and a direct `charly vm create <entity>` (whose domain identity IS the entity) is unchanged. Both the host preresolver and candy/plugin-deploy-vm call this on the SAME deploy name, so the domain they derive always agrees.
Home note (FLOOR-legal leaf): lives in package spec — the always-floor-legal wire/vocabulary leaf — so BOTH charly core and a plugin (candy/plugin-check's bed_session.go, #55 W3 B2-full) derive the domain identity without a vmshared import. stdlib-only (strings).
func VmNameFromDeployName ¶ added in v0.2026213.1726
VmNameFromDeployName extracts the VM entity name from a deploy-key in the legacy "vm:<name>[/<instance>]" form. Callers that hold a schema-v4 deploy key (whose entity comes from the node's `vm:` field) resolve the entity a different way (the node's own From field); this helper handles the prefixed form (legacy refs + the "vm:<entity>" key the del path builds for ledger/teardown keying). The `instance` suffix is preserved for future per-instance addressing but currently unused. Relocated from sdk/vmshared (#55 vmshared Bucket B) so kernel-floor charly files address VM deploys without a vmshared import; vmshared keeps a re-export forwarder.
func VmSshAlias ¶ added in v0.2026213.1726
VmSshAlias returns the canonical managed-ssh-config Host alias for a VM deployment name ("charly-" namespaced) — the token `charly vm create` / the vm deploy lifecycle writes the Host stanza under (~/.config/charly/ssh_config) and an SSHExecutor built with just `Host: VmSshAlias(id)` lets ssh(1) supply User/Port/IdentityFile. Pure naming leaf, a peer of VmDomainIdentity: lives in package spec — the always-floor-legal wire/vocabulary leaf — so the spec/exec executor-chain constructors (RootExecutorForDeployNode/ResolveDeployChain/ VmChildExecutor) derive the alias without an sdk/kit import (#55 K4). sdk/kit re-exports it for the host-coupled ssh-config-fragment writers (renderStanza/VmSshStanza) that STAY in kit.
func WrapContainerCommand ¶ added in v0.2026213.1726
WrapContainerCommand guards an in-container command-check script against stdin-consuming subcommands. The runner delivers in-container scripts to the pod shell over a stdin heredoc ("stdin-attached exec"); without this guard the FIRST subcommand that reads stdin — adb shell, ssh, read, cat — consumes the REST of the heredoc (the not-yet-executed script lines), silently truncating the check to its first command. Wrapping the whole script in a brace group with stdin redirected from /dev/null fixes it generically: the shell reads the entire group before executing it (so the heredoc is fully drained by parse time), then runs every subcommand with stdin tied to /dev/null. The host path (a plain `sh -c` argv) is unaffected.
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 AgentAbortControl ¶
type AgentDelegationEdge ¶
type AgentEvent ¶
type AgentEvent struct {
RunID UUIDv7 `yaml:"run_id,omitempty" json:"run_id"`
Sequence int64 `yaml:"sequence,omitempty" json:"sequence"`
Type string `yaml:"type,omitempty" json:"type"`
Time string `yaml:"time,omitempty" json:"time"`
Payload map[string]any `yaml:"payload,omitempty" json:"payload,omitempty"`
}
type AgentExecSpec ¶
type AgentExecSpec struct {
Command []string `yaml:"command,omitempty" json:"command,omitempty"`
PromptVia string `yaml:"prompt_via,omitempty" json:"prompt_via,omitempty"`
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,omitempty"`
}
#AgentExecSpec is a fully-resolved long-running-CLI invocation descriptor — the resolve-to-envelope form of an agent, with Go-level defaults applied by the plugin (Timeout, PromptVia). Kind-agnostic: it describes HOW to launch a CLI, capture its version, and poll it in a plateau-bounded loop — not "agent-kind" knowledge.
type AgentFederationRecord ¶
type AgentFederationRecord struct {
ID UUIDv7 `yaml:"id,omitempty" json:"id"`
Node string `yaml:"node,omitempty" json:"node"`
Owner string `yaml:"owner,omitempty" json:"owner"`
SessionID UUIDv7 `yaml:"session_id,omitempty" json:"session_id,omitempty"`
RunID UUIDv7 `yaml:"run_id,omitempty" json:"run_id,omitempty"`
State string `yaml:"state,omitempty" json:"state"`
UpdatedAt string `yaml:"updated_at,omitempty" json:"updated_at"`
Metadata StrMap `yaml:"metadata,omitempty" json:"metadata,omitempty"`
}
type AgentResolveInput ¶
type AgentResolveInput struct {
Agents map[string]RawBody `yaml:"agents,omitempty" json:"agents,omitempty"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
}
#AgentResolveInput is candy/plugin-agent's OpResolve input: the opaque agent bodies (the name-keyed AI-CLI catalog) + the selected agent name ("" picks the sole entry, or errors when several are configured).
type AgentResolveReply ¶
type AgentResolveReply struct {
Spec *AgentExecSpec `yaml:"spec,omitempty" json:"spec,omitempty"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
}
#AgentResolveReply is the OpResolve reply: the resolved exec spec + the chosen agent's catalog name.
type AgentRunRequest ¶
type AgentRunRequest struct {
ID UUIDv7 `yaml:"id,omitempty" json:"id"`
SessionID UUIDv7 `yaml:"session_id,omitempty" json:"session_id"`
RequestID UUIDv7 `yaml:"request_id,omitempty" json:"request_id"`
IdempotencyKey string `yaml:"idempotency_key,omitempty" json:"idempotency_key"`
Prompt string `yaml:"prompt,omitempty" json:"prompt,omitempty"`
Params map[string]any `yaml:"params,omitempty" json:"params,omitempty"`
Resume bool `yaml:"resume,omitempty" json:"resume,omitempty"`
}
type AgentRuntimeCapability ¶
type AgentRuntimeCapability struct {
Provider string `yaml:"provider,omitempty" json:"provider"`
ProtocolVersion int `yaml:"protocol_version,omitempty" json:"protocol_version"`
Entrypoint []string `yaml:"entrypoint,omitempty" json:"entrypoint,omitempty"`
Profiles []string `yaml:"profiles,omitempty" json:"profiles,omitempty"`
InteractionModes []string `yaml:"interaction_modes,omitempty" json:"interaction_modes,omitempty"`
Persistence string `yaml:"persistence,omitempty" json:"persistence,omitempty"`
SemanticCompletion bool `yaml:"semantic_completion,omitempty" json:"semantic_completion,omitempty"`
}
type AgentSession ¶
type AgentSession struct {
ID UUIDv7 `yaml:"id,omitempty" json:"id"`
Runtime string `yaml:"runtime,omitempty" json:"runtime"`
Target TargetSpec `yaml:"target,omitempty" json:"target"`
State string `yaml:"state,omitempty" json:"state"`
CreatedAt string `yaml:"created_at,omitempty" json:"created_at"`
UpdatedAt string `yaml:"updated_at,omitempty" json:"updated_at"`
StorageRef string `yaml:"storage_ref,omitempty" json:"storage_ref,omitempty"`
Metadata StrMap `yaml:"metadata,omitempty" json:"metadata,omitempty"`
TerminalProfile *TerminalProfile `yaml:"terminal_profile,omitempty" json:"terminal_profile,omitempty"`
}
type AgentSessionBinding ¶
type AgentSessionBinding struct {
StorageRef string `yaml:"storage_ref,omitempty" json:"storage_ref"`
}
Provider-reported durable session correlation. Runtime-specific session state remains on the target; this is the only binding replicated to the controller.
type AgentTeam ¶
type AgentTeam struct {
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Agents []AgentTeamMember `yaml:"agents,omitempty" json:"agents"`
Edges []AgentDelegationEdge `yaml:"edges,omitempty" json:"edges,omitempty"`
Coordinator string `yaml:"coordinator,omitempty" json:"coordinator,omitempty"`
Concurrency int64 `yaml:"concurrency,omitempty" json:"concurrency,omitempty"`
EvidencePolicy string `yaml:"evidence_policy,omitempty" json:"evidence_policy,omitempty"`
}
type AgentTeamMember ¶
type AgentTeamMember struct {
Name string `yaml:"name,omitempty" json:"name"`
Runtime string `yaml:"runtime,omitempty" json:"runtime"`
Role string `yaml:"role,omitempty" json:"role,omitempty"`
Target TargetSpec `yaml:"target,omitempty" json:"target,omitempty"`
TerminalProfile *TerminalProfile `yaml:"terminal_profile,omitempty" json:"terminal_profile,omitempty"`
}
type AgentTeamRecord ¶
type AgentTeamRecord struct {
ID UUIDv7 `yaml:"id,omitempty" json:"id"`
Team AgentTeam `yaml:"team,omitempty" json:"team"`
Sessions map[string]UUIDv7 `yaml:"sessions,omitempty" json:"sessions"`
CreatedAt string `yaml:"created_at,omitempty" json:"created_at"`
}
Durable control-plane projection of a declarative team. The authored graph remains #AgentTeam; this record binds each member name to the ephemeral session created for one dispatch so later delegation can re-check authority.
type AggregatedCandyCaps ¶ added in v0.2026210.1533
type AggregatedCandyCaps 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_labels,omitempty" json:"oci_labels,omitempty"`
// provided is the set of capability names declared by some candy in the composition.
// Used by CheckRequiredCapabilities to validate `requires_capabilities:` cross-candy
// requirements.
Provided map[string]bool `yaml:"provided,omitempty" json:"provided,omitempty"`
}
#AggregatedCandyCaps — the output of walking all candies in resolution order. Populated onto #ResolvedBox and consumed wherever code reads BoxConfig.Bootc/BoxConfig.DataImage/the init-system bootc parameter.
func AggregateCandyCapabilities ¶ added in v0.2026213.1726
func AggregateCandyCapabilities(layers map[string]CandyReader, order []string) (*AggregatedCandyCaps, error)
AggregateCandyCapabilities walks `order` (candy names in topological resolution order) and merges each candy's `capabilities:` contribution. Returns an error if two candies declare conflicting values for the same OCI label key — the conflict surfaces the bug rather than silently picking a winner.
type AggregatedCandyCapsView ¶
type AggregatedCandyCapsView 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"`
OCILabels map[string]string `yaml:"oci_labels,omitempty" json:"oci_labels,omitempty"`
}
#AggregatedCandyCapsView — the box-level aggregated candy capability surface the build RENDER reads (a wire projection of buildkit.AggregatedCandyCaps): the arbitrary candy-declared oci_labels + the two booleans that gate the render (preserve_user → the final-USER-reset skip + the bootc round-trip label; needs_root_after_init → the post-candy root reset). Carried so the plugin-side render (NewSpecResolvedBox) reattaches img.CandyCaps WITHOUT re-aggregating over the live *Candy graph.
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 ¶
EffectiveSerial returns the device serial, defaulting to the emulator serial.
func (*Android) IsEndpoint ¶
IsEndpoint reports whether the device targets a remote adb endpoint.
type AndroidDeployVenue ¶
type AndroidDeployVenue struct {
AdbAddr string `yaml:"adb_addr,omitempty" json:"adb_addr"`
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
Container string `yaml:"container,omitempty" json:"container,omitempty"`
Serial string `yaml:"serial,omitempty" json:"serial,omitempty"`
GoogleEmail string `yaml:"google_email,omitempty" json:"google_email,omitempty"`
GoogleToken string `yaml:"google_token,omitempty" json:"google_token,omitempty"`
Installs []ApkPackageSpec `yaml:"installs,omitempty" json:"installs,omitempty"`
// boot_timeout / install_deadline / install_interval are the readiness +
// install-retry windows the host ships (no magic numbers in the plugin).
BootTimeout string `yaml:"boot_timeout,omitempty" json:"boot_timeout,omitempty"`
InstallDeadline string `yaml:"install_deadline,omitempty" json:"install_deadline,omitempty"`
InstallInterval string `yaml:"install_interval,omitempty" 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.
type AndroidResolveInput ¶
type AndroidResolveInput struct {
Android RawBody `yaml:"android,omitempty" json:"android"`
}
type AndroidResolveReply ¶
type AndroidResolveReply struct {
Resolved *ResolvedAndroid `yaml:"resolved,omitempty" json:"resolved,omitempty"`
}
type ApkInstallStep ¶ added in v0.2026210.2308
type ApkInstallStep struct {
Packages []ApkPackageSpec
CandyName string
CandyDir string // candy source dir — anchors relative committed-APK paths
}
ApkInstallStep installs Android apps onto a `kind: android` device. It is the IR form of a candy's `apk:` package section — the apk "package format" (parallel to SystemPackagesStep for rpm/deb/pac, and BuilderStep for aur).
Unlike every other step, an apk install lands on a RUNNING Android device, not the build/host filesystem. `target: android` is an EXTERNAL deploy substrate (F1): NO in-proc DeployTarget executes this step — the host-side android deploy preresolver (collectAndroidInstalls, android_deploy_preresolve.go) READS it to collect the apk install specs and ships them to the deploy:android plugin (candy/plugin-adb: apkeep + adb), which drives the device. Every DeployTarget SKIPS it — OCITarget emits nothing (no device at image-build time), and Local/Vm/Pod targets record a skip (a host/VM/pod is not an Android device). This is the same "wrong venue → skip" shape `aur:` uses off-Arch, expressed as a clean recorded skip rather than an error (an image legitimately builds without installing apps).
func (*ApkInstallStep) Kind ¶ added in v0.2026210.2308
func (s *ApkInstallStep) Kind() StepKind
func (*ApkInstallStep) RequiresGate ¶ added in v0.2026210.2308
func (s *ApkInstallStep) RequiresGate() Gate
func (*ApkInstallStep) Reverse ¶ added in v0.2026210.2308
func (s *ApkInstallStep) Reverse() []ReverseOp
Reverse returns no STATIC ledger ops — Android teardown ops are DYNAMIC, recorded from the deploy:android plugin's OpExecute reply (the uninstall reverse ops) at deploy time, replayed at `charly bundle del` like any external deploy substrate.
func (*ApkInstallStep) Scope ¶ added in v0.2026210.2308
func (s *ApkInstallStep) Scope() Scope
Scope is system — installing an app mutates device-global package state.
func (*ApkInstallStep) Venue ¶ added in v0.2026210.2308
func (s *ApkInstallStep) Venue() Venue
Venue is host-native — the android deploy preresolver reads this step from the host (the install itself runs in the deploy:android plugin over the wire).
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 ArbiterBracketAcquireReply ¶
type ArbiterBracketAcquireReply struct {
}
type ArbiterBracketAcquireRequest ¶
type ArbiterBracketAcquireRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
Node Deploy `yaml:"node,omitempty" json:"node"`
}
#ArbiterBracketAcquireRequest / Reply — the K4-exit HostBuild seam for the acquire half of the Q1 resource-arbiter bracket (FLOOR-SLIM-proper Unit-8; the former core-resident charly/arbiter_bracket.go's arbiterBracketedStart, deleted — command:bundle's handleLifecycleSimple now calls this seam itself around its own Start dispatch, instead of core bracketing the dispatch call inline). `name` is the claimant identity; `node` carries the claim fields (requires_exclusive:/requires_shared:) acquireResourceForClaimant reads. The actual os.Setenv(CHARLY_PREEMPT_LEASE) STILL runs host-side (preserving the nested-`charly`-subprocess env-inheritance skip) — only OWNERSHIP of WHEN to call it moves plugin-side.
type ArbiterBracketReleaseReply ¶
type ArbiterBracketReleaseReply struct {
}
type ArbiterBracketReleaseRequest ¶
type ArbiterBracketReleaseRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
}
#ArbiterBracketReleaseRequest / Reply — the release half (arbiterBracketedStart's release-on-failure leg + arbiterBracketedStop's unconditional release-after-stop leg, both now reached from command:bundle's handleLifecycleSimple via this ONE seam).
type ArbiterInvokeInput ¶
type ArbiterInvokeInput struct {
Action string `yaml:"action,omitempty" json:"action"`
Claimant string `yaml:"claimant,omitempty" json:"claimant,omitempty"`
Tokens []string `yaml:"tokens,omitempty" json:"tokens,omitempty"`
ClaimAddr HolderAddr `yaml:"claim_addr,omitempty" json:"claim_addr"`
Transient bool `yaml:"transient,omitempty" json:"transient,omitempty"`
Success bool `yaml:"success,omitempty" json:"success,omitempty"`
Token string `yaml:"token,omitempty" json:"token,omitempty"`
// GPU-implied-consumer projection (acquire-shared only, K-wave W3a A2): the claimant node's
// GPU-relevant traits, so verb:arbiter can union its OWN implied-consumer token onto tokens
// before applying its early-return-on-empty acquire policy — arbiter policy belongs in the
// arbiter, not the in-core proxy (the former charly/gpu_imply.go, now
// candy/plugin-preempt/gpu_imply.go). is_group/is_pod_member are pre-derived core-side (the
// in-core nodeTraits/isPodMember trait resolution — not duplicated plugin-side);
// security_devices is the claimant's raw security.devices list.
IsGroup bool `yaml:"is_group,omitempty" json:"is_group,omitempty"`
IsPodMember bool `yaml:"is_pod_member,omitempty" json:"is_pod_member,omitempty"`
SecurityDevices []string `yaml:"security_devices,omitempty" json:"security_devices,omitempty"`
}
#ArbiterInvokeInput is the action-multiplexed input the in-core proxy ships to verb:arbiter. Each action populates only the field(s) it needs (the OTHERS stay Go zero-value); claim_addr is REQUIRED rather than optional because the original hand-written type's `omitempty` on a value-typed (non-pointer) HolderAddr field was already a no-op in encoding/json — Go's omitempty never elides a struct value — so marking it required here changes nothing about the actual wire bytes, only how honestly the schema describes them.
type ArbiterInvokeReply ¶
type ArbiterInvokeReply struct {
Active bool `yaml:"active,omitempty" json:"active,omitempty"`
Bool bool `yaml:"bool,omitempty" json:"bool,omitempty"`
Ledger *PreemptLedger `yaml:"ledger,omitempty" json:"ledger,omitempty"`
Stranded []string `yaml:"stranded,omitempty" json:"stranded,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
#ArbiterInvokeReply is the action-multiplexed reply from verb:arbiter.
type ArtifactRef ¶ added in v0.2026210.2308
type ArtifactRef struct {
ContainerPath string // path inside the builder stage/container
HostPath string // path on the deploy target (host fs or final-image fs)
Chown bool // whether to re-own to the target user
}
ArtifactRef describes an output path from a builder that needs to be extracted back to the host or copied into the final OCI image.
For user-scope pixi/npm/cargo on the host target: the host path is bind-mounted into the builder, so there's no "extraction" — the Artifacts list is empty because the bind-mount already put the files in place. For aur: the list contains /tmp/aur-pkgs/*.pkg.tar.zst, which the host target pulls out to a staging dir then hands to pacman.
type ArtifactView ¶
type ArtifactView struct {
ContainerPath string `yaml:"container_path,omitempty" json:"container_path,omitempty"`
HostPath string `yaml:"host_path,omitempty" json:"host_path,omitempty"`
Chown bool `yaml:"chown,omitempty" json:"chown,omitempty"`
}
#ArtifactView is the wire mirror of package main's ArtifactRef.
type BakedLabelSet ¶
type BakedLabelSet struct {
// always-present scalars (the formatter emits these unconditionally as LABEL %q).
Version string `yaml:"version,omitempty" json:"version"`
Box string `yaml:"box,omitempty" json:"box"`
UID int `yaml:"uid,omitempty" json:"uid"`
GID int `yaml:"gid,omitempty" json:"gid"`
User string `yaml:"user,omitempty" json:"user"`
Home string `yaml:"home,omitempty" json:"home"`
// conditional scalars (the formatter omits these when empty).
Registry string `yaml:"registry,omitempty" json:"registry,omitempty"`
Bootc bool `yaml:"bootc,omitempty" json:"bootc,omitempty"`
OCILabels map[string]string `yaml:"oci_labels,omitempty" json:"oci_labels,omitempty"`
Network string `yaml:"network,omitempty" json:"network,omitempty"`
// platform identity + builder-pool coordination (writeJSONLabel).
Distro []string `yaml:"distro,omitempty" json:"distro,omitempty"`
BuildFormat []string `yaml:"build_format,omitempty" json:"build_format,omitempty"`
Builder map[string]string `yaml:"builder,omitempty" json:"builder,omitempty"`
Build []string `yaml:"build,omitempty" json:"build,omitempty"`
// JSON labels (omitted when empty). env is the image-level env MAP (the label wire form).
Port []string `yaml:"port,omitempty" json:"port,omitempty"`
PortProto map[string]string `yaml:"port_proto,omitempty" json:"port_proto,omitempty"`
Volume []LabelVolumeEntry `yaml:"volume,omitempty" json:"volume,omitempty"`
Alias []CollectedAlias `yaml:"alias,omitempty" json:"alias,omitempty"`
Security Security `yaml:"security,omitempty" json:"security,omitempty"`
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
Hook *CandyHook `yaml:"hook,omitempty" json:"hook,omitempty"`
Description *LabelDescriptionSet `yaml:"description,omitempty" json:"description,omitempty"`
Shell *LabelShellSet `yaml:"shell,omitempty" json:"shell,omitempty"`
// init system: active name + the deploy-relevant init def + the DYNAMIC label key the
// per-init service-name list emits under (writeJSONLabel(b, labelInitDef.LabelKey, names)).
Init string `yaml:"init,omitempty" json:"init,omitempty"`
InitDef *CapabilityInitDef `yaml:"init_def,omitempty" json:"init_def,omitempty"`
InitLabelKey string `yaml:"init_label_key,omitempty" json:"init_label_key,omitempty"`
ServiceNames []string `yaml:"service_names,omitempty" json:"service_names,omitempty"`
Service []CapabilityService `yaml:"service,omitempty" json:"service,omitempty"`
PortRelay []int `yaml:"port_relay,omitempty" json:"port_relay,omitempty"`
Secret []LabelSecretEntry `yaml:"secret,omitempty" json:"secret,omitempty"`
EnvProvide map[string]string `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"`
SecretRequire []EnvDependency `yaml:"secret_require,omitempty" json:"secret_require,omitempty"`
SecretAccept []EnvDependency `yaml:"secret_accept,omitempty" json:"secret_accept,omitempty"`
MCPProvide []CandyMCPProvide `yaml:"mcp_provide,omitempty" json:"mcp_provide,omitempty"`
AgentProvide []AgentRuntimeCapability `yaml:"agent_provide,omitempty" json:"agent_provide,omitempty"`
TerminalProfiles map[string]TerminalProfile `yaml:"terminal_profiles,omitempty" json:"terminal_profiles,omitempty"`
MCPRequire []EnvDependency `yaml:"mcp_require,omitempty" json:"mcp_require,omitempty"`
MCPAccept []EnvDependency `yaml:"mcp_accept,omitempty" json:"mcp_accept,omitempty"`
Route []LabelRouteEntry `yaml:"route,omitempty" json:"route,omitempty"`
EnvCandy map[string]string `yaml:"env_candy,omitempty" json:"env_candy,omitempty"`
PathAppend []string `yaml:"path_append,omitempty" json:"path_append,omitempty"`
Skill string `yaml:"skill,omitempty" json:"skill,omitempty"`
Status string `yaml:"status,omitempty" json:"status,omitempty"`
CheckLevel string `yaml:"check_level,omitempty" json:"check_level,omitempty"`
Info string `yaml:"info,omitempty" json:"info,omitempty"`
CandyVersion map[string]string `yaml:"candy_version,omitempty" json:"candy_version,omitempty"`
DataEntries []LabelDataEntry `yaml:"data_entries,omitempty" json:"data_entries,omitempty"`
DataImage bool `yaml:"data_image,omitempty" json:"data_image,omitempty"`
}
#BakedLabelSet — the BUILD-side wire-form carrier for the render's OCI-label emission (#67, the build_resolve RENDER-leg death). The former charly/generate.go writeLabels body split at the data/format boundary: the HOST gather (buildBakedMetadata) reads the live *Candy/*Config graph + the Collect* aggregators and produces a *spec.BakedLabelSet holding EXACTLY the data writeLabels emits, in WIRE form; the deploykit FORMATTER (WriteLabels) emits the LABEL lines from it byte-for-byte WITHOUT the live graph. It is distinct from #BoxMetadata (the DEPLOY-side hub ExtractMetadata builds field-by-field from labels): a few fields differ in shape between the bake wire form and the deploy read form — env is a map here (the label bakes a JSON object) but []string KEY=VALUE pairs in #BoxMetadata, and volume is #LabelVolumeEntry here (name+path) but #VolumeMount in #BoxMetadata — so the build carrier is its own wire-faithful type, never the deploy hub. It rides the envelope on #ResolvedBoxView.baked_metadata (build-render projection only) and re-attaches via NewSpecResolvedBox. MARSHALED (crosses core→plugin), so its tags are wire-relevant; the sub-shapes are the same CUE-sourced label types writeLabels emitted.
type BaseChainNode ¶
BaseChainNode is one image visited while walking an internal base chain. Name is the ref as it was reached (bare for a root image, namespace-qualified for a base reached across an import boundary, e.g. `cachyos.cachyos`).
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 ¶
HasBaseFromConflict reports whether the box authored BOTH base: and from:.
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 ---
func BoxConfigFrom ¶
BoxConfigFrom decodes name's image config from a generic image map.
type BoxFetchResolveReply ¶
type BoxFetchResolveReply struct {
Path string `yaml:"path,omitempty" json:"path,omitempty"`
}
type BoxFetchResolveRequest ¶
type BoxFetchResolveRequest struct {
Spec string `yaml:"spec,omitempty" json:"spec"`
Refresh bool `yaml:"refresh,omitempty" json:"refresh,omitempty"`
}
#BoxFetchResolveRequest/#BoxFetchResolveReply — the "box-fetch-resolve" HostBuild seam behind candy/plugin-authoring's command:fetch/command:refresh (K3 build-tail tail, coneB-buildremnant): the former hidden core `__box-fetch`/`__box-refresh` reentries (charly/box_fetch_reentry.go, DELETED) are replaced by ONE generic host-builder wrapping the SAME host-coupled repo resolver (ResolveProjectRepo → EnsureRepoDownloaded: CHARLY_REPO_OVERRIDE + the registered refs-backend download dispatch + the command:migrate auto-migration) — none of which an sdk-only plugin can run itself. refresh=true additionally force-removes the spec's cache entry before resolving (the former BoxRefreshCmd body) so a stale cache re-clones.
type BoxMap ¶
type BoxMap = map[string]json.RawMessage
BoxMap is the generic image map type — name → opaque marshaled BoxConfig. A plain alias of map[string]json.RawMessage, so it round-trips byte-identically with charly's own (still separately declared, for the UnifiedFile side) `boxMap` alias — both resolve to the SAME underlying type.
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 BoxMetadata ¶
type BoxMetadata struct {
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
Registry string `yaml:"registry,omitempty" json:"registry,omitempty"`
Bootc bool `yaml:"bootc,omitempty" json:"bootc,omitempty"`
UID int `yaml:"uid,omitempty" json:"uid,omitempty"`
GID int `yaml:"gid,omitempty" json:"gid,omitempty"`
User string `yaml:"user,omitempty" json:"user,omitempty"`
Home string `yaml:"home,omitempty" json:"home,omitempty"`
Port []string `yaml:"port,omitempty" json:"port,omitempty"`
Volume []VolumeMount `yaml:"volume,omitempty" json:"volume,omitempty"`
Alias []CollectedAlias `yaml:"alias,omitempty" json:"alias,omitempty"`
Security Security `yaml:"security,omitempty" json:"security,omitempty"`
Network string `yaml:"network,omitempty" json:"network,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"`
Env []string `yaml:"env,omitempty" json:"env,omitempty"`
Hook *CandyHook `yaml:"hook,omitempty" json:"hook,omitempty"`
Route []LabelRouteEntry `yaml:"route,omitempty" json:"route,omitempty"`
Init string `yaml:"init,omitempty" json:"init,omitempty"`
InitDef *CapabilityInitDef `yaml:"init_def,omitempty" json:"init_def,omitempty"`
Service []CapabilityService `yaml:"service,omitempty" json:"service,omitempty"`
ServiceNames []string `yaml:"service_names,omitempty" json:"service_names,omitempty"`
EnvCandy map[string]string `yaml:"env_candy,omitempty" json:"env_candy,omitempty"`
PathAppend []string `yaml:"path_append,omitempty" json:"path_append,omitempty"`
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
PortProto map[string]string `yaml:"port_proto,omitempty" json:"port_proto,omitempty"`
PortRelay []int `yaml:"port_relay,omitempty" json:"port_relay,omitempty"`
Skill string `yaml:"skill,omitempty" json:"skill,omitempty"`
Status string `yaml:"status,omitempty" json:"status,omitempty"`
Info string `yaml:"info,omitempty" json:"info,omitempty"`
CandyVersion map[string]string `yaml:"candy_version,omitempty" json:"candy_version,omitempty"`
Secret []LabelSecretEntry `yaml:"secret,omitempty" json:"secret,omitempty"`
Distro []string `yaml:"distro,omitempty" json:"distro,omitempty"`
BuildFormat []string `yaml:"build_format,omitempty" json:"build_format,omitempty"`
Builder map[string]string `yaml:"builder,omitempty" json:"builder,omitempty"`
Build []string `yaml:"build,omitempty" json:"build,omitempty"`
DataEntries []LabelDataEntry `yaml:"data_entries,omitempty" json:"data_entries,omitempty"`
DataImage bool `yaml:"data_image,omitempty" json:"data_image,omitempty"`
EnvProvide map[string]string `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"`
AgentProvide []AgentRuntimeCapability `yaml:"agent_provide,omitempty" json:"agent_provide,omitempty"`
TerminalProfiles map[string]TerminalProfile `yaml:"terminal_profiles,omitempty" json:"terminal_profiles,omitempty"`
MCPRequire []EnvDependency `yaml:"mcp_require,omitempty" json:"mcp_require,omitempty"`
MCPAccept []EnvDependency `yaml:"mcp_accept,omitempty" json:"mcp_accept,omitempty"`
Description *LabelDescriptionSet `yaml:"description,omitempty" json:"description,omitempty"`
Shell *LabelShellSet `yaml:"shell,omitempty" json:"shell,omitempty"`
CheckLevel string `yaml:"check_level,omitempty" json:"check_level,omitempty"`
}
#BoxMetadata — the OCI-label metadata hub. NEVER whole-marshaled (ExtractMetadata builds it field-by-field), so its own tags are wire-irrelevant. Deploy-only fields (Tunnel/DNS/ AcmeEmail/Engine) are fed by MergeDeployOntoMetadata, never baked. PortProto RESHAPED map[int]string → map[string]string (the WIRE was always string-keyed; the int-key was an ExtractMetadata-internal convenience — resolveProto + ExtractMetadata rewired to match).
type BoxRefResolveReply ¶
type BoxRefResolveReply struct {
// exists_ref / pull_ref is the fully-qualified registry ref to check for local
// presence / hand to `podman pull`; "" when the identifier could not be
// resolved (no project reachable, or an unknown short name).
ExistsRef string `yaml:"exists_ref,omitempty" json:"exists_ref,omitempty"`
PullRef string `yaml:"pull_ref,omitempty" json:"pull_ref,omitempty"`
// build_fallback_short is the short name (bare or namespace-qualified, e.g.
// "fedora.fedora-builder") `charly box build` should target when the pull
// fails; "" when no local build-fallback is possible for this identifier.
BuildFallbackShort string `yaml:"build_fallback_short,omitempty" json:"build_fallback_short,omitempty"`
// produced_ref is the registry ref build_fallback_short resolves to today
// (registry+name, tag-less) — used to tag-alias a pinned-tag input ref onto
// the freshly built image.
ProducedRef string `yaml:"produced_ref,omitempty" json:"produced_ref,omitempty"`
}
type BoxRefResolveRequest ¶
type BoxRefResolveRequest struct {
Image string `yaml:"image,omitempty" json:"image"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
}
#BoxRefResolveRequest / #BoxRefResolveReply — the "box-ref-resolve" HostBuild seam: resolve a short-name or full-ref image identifier against charly.yml.
type BuildEngineScanRemoteRequest ¶
type BuildEngineScanRemoteRequest struct {
CacheDir string `yaml:"cache_dir,omitempty" json:"cache_dir"`
RepoPath string `yaml:"repo_path,omitempty" json:"repo_path"`
Refs []string `yaml:"refs,omitempty" json:"refs,omitempty"`
}
#BuildEngineScanRemoteRequest — the `buildengine-scan-remote` host-leg request (K3 build-engine, U6): scan the wanted bare refs out of a downloaded repo cache dir. The plugin's ScanSeams.ScanRemote closure fills it; the host runs requireCandyScanner().ScanRemoteCandy over the cache.
type BuildEnsureReply ¶
type BuildEnsureReply struct {
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
type BuildEnsureRequest ¶
type BuildEnsureRequest struct {
Image string `yaml:"image,omitempty" json:"image"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
BuildEngine string `yaml:"build_engine,omitempty" json:"build_engine,omitempty"`
RunEngine string `yaml:"run_engine,omitempty" json:"run_engine,omitempty"`
}
#BuildEnsureRequest / #BuildEnsureReply — the build:ensure word's Invoke envelope: ensure an image is present in local podman storage, falling back to a local (or remote-cached) build when the identifier maps to a project charly.yml entry.
type BuildEnv ¶
type BuildEnv struct {
Distros []string `yaml:"distros,omitempty" json:"distros,omitempty"`
Image string `yaml:"image,omitempty" json:"image,omitempty"`
DevLocalPkg bool `yaml:"dev_local_pkg,omitempty" json:"dev_local_pkg,omitempty"`
ImageBuildDir string `yaml:"image_build_dir,omitempty" json:"image_build_dir,omitempty"`
ContextRelPrefix string `yaml:"context_rel_prefix,omitempty" json:"context_rel_prefix,omitempty"`
// A pod-overlay deploy's add_candy: refs (if any) — threaded so a HOST-COUPLED word's OpEmit
// (candy/plugin-installstep's getGenerator) can widen ITS OWN "resolved-project" envelope
// re-fetch the same way ScanAllCandyWithConfigOpts' ResolveOpts.ExtraCandyRefs already widens
// the host's own overlay Generator (hostBuildOverlay). Without this, an add_candy candy the
// overlay build correctly resolved host-side is absent from this plugin's INDEPENDENT
// envelope fetch, and candyByName's REMOTE-candy fallback still misses (RCA'd K1-alpha
// regression: check-addcandy-pod's overlay-deploy path, "candy not found").
ExtraCandyRefs []string `yaml:"extra_candy_refs,omitempty" json:"extra_candy_refs,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.
type BuildPkgReply ¶
type BuildPkgRequest ¶
type BuildPkgRequest struct {
Format []string `yaml:"format,omitempty" json:"format,omitempty"`
Candy string `yaml:"candy,omitempty" json:"candy,omitempty"`
Out string `yaml:"out,omitempty" json:"out,omitempty"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
}
#BuildPkgRequest / #BuildPkgReply — the build:pkg word's Invoke envelope: `charly box pkg`'s CLI→DRIVE envelope (mirrors #BuildRequest's shape, K3 build-tail move — the former hidden core `__box-pkg` reentry is DELETED, its body now runs plugin-side in candy/plugin-build).
type BuildReply ¶
type BuildReply struct {
Written []string `yaml:"written,omitempty" json:"written,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
#BuildReply is what a build:box / build:generate plugin echoes back from its HostBuild call.
type BuildRequest ¶
type BuildRequest struct {
Boxes []string `yaml:"boxes,omitempty" json:"boxes,omitempty"`
ExtraCandyRefs []string `yaml:"extra_candy_refs,omitempty" json:"extra_candy_refs,omitempty"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
IncludeDisabled bool `yaml:"include_disabled,omitempty" json:"include_disabled,omitempty"`
DevLocalPkg bool `yaml:"dev_local_pkg,omitempty" json:"dev_local_pkg,omitempty"`
Push bool `yaml:"push,omitempty" json:"push,omitempty"`
Platform string `yaml:"platform,omitempty" json:"platform,omitempty"`
Cache string `yaml:"cache,omitempty" json:"cache,omitempty"`
NoCache bool `yaml:"no_cache,omitempty" json:"no_cache,omitempty"`
Jobs int `yaml:"jobs,omitempty" json:"jobs,omitempty"`
PodmanJobs int `yaml:"podman_jobs,omitempty" json:"podman_jobs,omitempty"`
}
#BuildRequest is the CLI→DRIVE envelope: what `charly box build` / `charly box generate` marshal into the compiled-in candy/plugin-build's Invoke (op.Params).
type BuildResolveBox ¶
type BuildResolveBox struct {
Name string `yaml:"name,omitempty" json:"name"`
FullTag string `yaml:"full_tag,omitempty" json:"full_tag,omitempty"`
Registry string `yaml:"registry,omitempty" json:"registry,omitempty"`
Platforms []string `yaml:"platforms,omitempty" json:"platforms,omitempty"`
MergeAuto bool `yaml:"merge_auto,omitempty" json:"merge_auto,omitempty"`
MergeMaxMB int64 `yaml:"merge_max_mb,omitempty" json:"merge_max_mb,omitempty"`
MergeMaxTotalMB int64 `yaml:"merge_max_total_mb,omitempty" json:"merge_max_total_mb,omitempty"`
// from/bootstrap_builder_image/distro_def/bootstrap_builder are the privileged-bootstrap
// inputs (a `from: builder:<name>` image) the candy needs to run
// buildkit.RunPrivileged itself instead of the host doing it in the buildengine-prep seam.
// bootstrap_builder is the SPECIFIC resolved #Builder for `from`'s builder name (not the
// whole per-image builder map) — the minimal slice runPrivilegedBootstrap actually reads.
// Both nil/empty for a non-bootstrap image (the common case).
From string `yaml:"from,omitempty" json:"from,omitempty"`
BootstrapBuilderImage string `yaml:"bootstrap_builder_image,omitempty" json:"bootstrap_builder_image,omitempty"`
DistroDef *ResolvedDistro `yaml:"distro_def,omitempty" json:"distro_def,omitempty"`
BootstrapBuilder *Builder `yaml:"bootstrap_builder,omitempty" json:"bootstrap_builder,omitempty"`
}
#BuildResolveBox is one image's drive descriptor: the tag to build and whether merge.auto fires for it (the candy gates the verb:oci layer-merge call on this bool; the host merge seam resolves the size knobs from config). The rendered Containerfile CONTENT is NO LONGER shipped in the reply (#67 render-DRIVE move): plugin-build renders Containerfiles itself from the resolved-project envelope (BuildResolveReply.resolved_project) via deploykit.Generator, so the candy pipes dg.Containerfiles[name] to podman — not a reply field.
type BuildResolveReply ¶
type BuildResolveReply struct {
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
EngineName string `yaml:"engine_name,omitempty" json:"engine_name,omitempty"`
Platform string `yaml:"platform,omitempty" json:"platform,omitempty"`
Order []string `yaml:"order,omitempty" json:"order,omitempty"`
Levels [][]string `yaml:"levels,omitempty" json:"levels,omitempty"`
Boxes []BuildResolveBox `yaml:"boxes,omitempty" json:"boxes,omitempty"`
Jobs int64 `yaml:"jobs,omitempty" json:"jobs,omitempty"`
PodmanJobs int64 `yaml:"podman_jobs,omitempty" json:"podman_jobs,omitempty"`
Cache string `yaml:"cache,omitempty" json:"cache,omitempty"`
KeepImages int64 `yaml:"keep_images,omitempty" json:"keep_images,omitempty"`
Written []string `yaml:"written,omitempty" json:"written,omitempty"`
// resolved_project — the full resolved-project envelope with build-render
// caches (BakedMetadata/RenderCandyOrder/InitSystem/InitDef/ActiveInits/Caps
// on each ResolvedBoxView + GlobalOrder/ExternalizedBuilders on the project),
// so plugin-build renders Containerfiles via deploykit.Generator WITHOUT the
// live *Candy/*Config graph (#67 render-DRIVE move). Filled by the buildengine-prep
// seam (render-prep → projectResolvedProject with caches). Empty for the
// generate-only path that writes Containerfiles host-side (transitional).
ResolvedProject *ResolvedProject `yaml:"resolved_project,omitempty" json:"resolved_project,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
type BuildResolveRequest ¶
type BuildResolveRequest struct {
Boxes []string `yaml:"boxes,omitempty" json:"boxes,omitempty"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
IncludeDisabled bool `yaml:"include_disabled,omitempty" json:"include_disabled,omitempty"`
DevLocalPkg bool `yaml:"dev_local_pkg,omitempty" json:"dev_local_pkg,omitempty"`
Push bool `yaml:"push,omitempty" json:"push,omitempty"`
Platform string `yaml:"platform,omitempty" json:"platform,omitempty"`
Cache string `yaml:"cache,omitempty" json:"cache,omitempty"`
NoCache bool `yaml:"no_cache,omitempty" json:"no_cache,omitempty"`
Jobs int64 `yaml:"jobs,omitempty" json:"jobs,omitempty"`
PodmanJobs int64 `yaml:"podman_jobs,omitempty" json:"podman_jobs,omitempty"`
GenerateOnly bool `yaml:"generate_only,omitempty" json:"generate_only,omitempty"`
}
#BuildResolveRequest carries the CLI-supplied build inputs (the former BuildRequest fields) the host cannot reconstruct from Dir alone. The host runs NewGenerator (loader) + Generate (render → .build/), the privileged builder-bootstrap, and the builder-image ensure host-side, then returns the drive-model. GenerateOnly (the `charly box generate` path) renders + returns the written Containerfile paths WITHOUT bootstrap/ensure/buildengine-prep.
type BuildResolvedBox ¶ added in v0.2026215.1748
type BuildResolvedBox struct {
ResolvedBox
// Build config (resolved per-image via charly.yml import: + the binary-embedded build vocabulary)
DistroConfig *DistroConfig `json:"-"`
DistroDef *ResolvedDistro `json:"-"`
BuilderConfig *BuilderConfig `json:"-"`
InitSystem string `json:"-"`
InitDef *ResolvedInit `json:"-"`
CandyCaps *AggregatedCandyCaps `json:"-"`
BakedMetadata *BakedLabelSet `json:"-"`
RenderCandyOrder []string `json:"-"`
ActiveInits map[string]*ResolvedInit `json:"-"`
}
build_resolved_box.go — SPIKE: the render-time embedding wrapper relocated from sdk/buildkit/resolved_box.go (#55 value-type relocation spike). Every field type here already resolves to a spec.* type (DistroConfig/BuilderConfig/ResolvedDistro/ ResolvedInit/AggregatedCandyCaps/BakedLabelSet are all CUE-generated in this same package) so the wrapper carries ZERO buildkit-only content — it exists purely because gengotypes has no json:"-" construct (the kit.CheckResult/DeadlineExceeded pattern — see /charly-internals:go "Generation coverage"). buildkit.ResolvedBox becomes a type alias onto this type so every existing buildkit/deploykit/candy call site compiles unchanged.
type BuildStageContext ¶
type BuildStageContext struct {
BuilderRef string `yaml:"builder_ref,omitempty" json:"builder_ref,omitempty"`
StageName string `yaml:"stage_name,omitempty" json:"stage_name,omitempty"`
LayerStage string `yaml:"layer_stage,omitempty" json:"layer_stage,omitempty"`
CopySrc string `yaml:"copy_src,omitempty" json:"copy_src,omitempty"`
UID int `yaml:"uid,omitempty" json:"uid,omitempty"`
GID int `yaml:"gid,omitempty" json:"gid,omitempty"`
Home string `yaml:"home,omitempty" json:"home,omitempty"`
User string `yaml:"user,omitempty" json:"user,omitempty"`
Manifest string `yaml:"manifest,omitempty" json:"manifest,omitempty"`
HasLockFile bool `yaml:"has_lock_file,omitempty" json:"has_lock_file,omitempty"`
InstallCmd string `yaml:"install_cmd,omitempty" json:"install_cmd,omitempty"`
ManylinuxFix string `yaml:"manylinux_fix,omitempty" json:"manylinux_fix,omitempty"`
CacheMounts []CacheMount `yaml:"cache_mounts,omitempty" json:"cache_mounts,omitempty"`
Packages []string `yaml:"packages,omitempty" json:"packages,omitempty"`
Options []string `yaml:"options,omitempty" json:"options,omitempty"`
HasBuildScript bool `yaml:"has_build_script,omitempty" json:"has_build_script,omitempty"`
BuildScript string `yaml:"build_script,omitempty" json:"build_script,omitempty"`
}
#BuildStageContext — data a builder's multi-stage `stage_template` renders against: builder/stage identity, scratch-stage + copy-src wiring, detected manifest/lockfile/build-script, install command, cache mounts, and (for config-detected builders like aur) packages/options.
type BuildTarget ¶
type BuildTarget struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Auto bool `yaml:"auto,omitempty" json:"auto,omitempty"`
}
#BuildTarget — a build-order entry (charly box list targets): the box/intermediate name + whether it is an auto-computed intermediate (the `name [auto]` output).
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 ¶
IsBootstrap reports whether the builder is a bootstrap builder.
type BuilderCollectInput ¶
type BuilderCollectInput struct {
Candy string `yaml:"candy,omitempty" json:"candy"`
Builder string `yaml:"builder,omitempty" json:"builder"`
Home string `yaml:"home,omitempty" json:"home,omitempty"`
Packages []string `yaml:"packages,omitempty" json:"packages,omitempty"`
Replaces []string `yaml:"replaces,omitempty" json:"replaces,omitempty"`
}
#BuilderCollectInput is the OpCollectContext params: the host-supplied candy descriptor an external builder plugin reads to produce its per-candy stage context.
type BuilderCollectReply ¶
type BuilderCollectReply struct {
Context map[string]any `yaml:"context,omitempty" json:"context,omitempty"`
}
#BuilderCollectReply is the OpCollectContext reply: the builder-specific stage-context keys the host merges onto the base context.
type BuilderConfig ¶ added in v0.2026210.2052
#BuilderConfig is the `builder:` build-vocabulary container — the whole builder map keyed by builder name (charly/charly.yml's `builder:` section). #55 step 3-III: relocated from sdk/buildkit for the import-purity lever (see #DistroConfig). Its methods (ValidBuilderType / BuilderNames) are pure Go METHODS and stay hand-written in spec/builder_config_methods.go.
func ProjectBuilderConfig ¶ added in v0.2026213.1726
func ProjectBuilderConfig(uf *UnifiedFile) *BuilderConfig
ProjectBuilderConfig reconstructs the *BuilderConfig (builder: section) from uf. The builder bodies decode purely (DecodePluginKindMap) — no OpResolve callback needed. Nil when no builders are configured.
func (*BuilderConfig) BuilderNames ¶ added in v0.2026210.2052
func (bc *BuilderConfig) BuilderNames() []string
BuilderNames returns sorted list of defined builder names.
func (*BuilderConfig) ValidBuilderType ¶ added in v0.2026210.2052
func (bc *BuilderConfig) ValidBuilderType(name string) bool
ValidBuilderType returns true if the given name is a defined builder.
type BuilderMap ¶ added in v0.2026210.1533
#BuilderMap — a map of build type (pixi/npm/cargo/aur) → builder image name.
func EffectiveBuilderForBox ¶ added in v0.2026213.1726
func EffectiveBuilderForBox(cfg *Config, name string, img BoxConfig) BuilderMap
EffectiveBuilderForBox computes the builder image refs an image will build against, from a RAW BoxConfig — the FETCH-path counterpart to ResolveBox's resolved-value path. Both end at the ONE canonical ResolveEffectiveBuilder.
func PickDistroBuilder ¶ added in v0.2026213.1726
func PickDistroBuilder(candidates []DistroBuilderCandidate, distroTags []string) BuilderMap
PickDistroBuilder returns the builder map of the first candidate (in the given, caller-sorted order — determinism is the caller's responsibility, so the result is stable when more than one candidate shares a distro tag) whose Distro contains a tag, walking distroTags in priority order (most specific first, e.g. ["cachyos","arch"] or ["fedora:43","fedora"]). Only candidates with a non-empty Builder are considered.
func ResolveEffectiveBuilder ¶ added in v0.2026213.1726
func ResolveEffectiveBuilder(cfg *Config, name string, distro []string, base string, isExternalBase bool, imgBuilder BuilderMap) BuilderMap
ResolveEffectiveBuilder computes an image's effective builder map via the SINGLE canonical precedence, lowest→highest: defaults.builder → distro-keyed default → direct local base → per-image override — then self-references are filtered. EVERY builder-consuming path calls this so resolution can never drift between commands.
func (BuilderMap) AllBuilder ¶ added in v0.2026210.1533
func (m BuilderMap) AllBuilder() []string
AllBuilder returns a deduplicated sorted list of builder image names.
func (BuilderMap) BuilderFor ¶ added in v0.2026210.1533
func (m BuilderMap) BuilderFor(format string) string
BuilderFor returns the builder image name for the given format, or "".
func (BuilderMap) HasBuilder ¶ added in v0.2026210.1533
func (m BuilderMap) HasBuilder(format string) bool
HasBuilder returns true if a builder is configured for the given format.
type BuilderPreresolved ¶ added in v0.2026213.1726
BuilderPreresolved is one candy×builder's pre-resolved payload: the builder-specific render Context (opaque key/value map from the builder plugin's OpCollectContext) plus its Reverse teardown ops (from OpReverse). Consumed by the deploy-plan compiler, which only reads it.
type BuilderResolveInput ¶
type BuilderResolveInput struct {
Candy string `yaml:"candy,omitempty" json:"candy"`
Builder string `yaml:"builder,omitempty" json:"builder,omitempty"`
BuilderRef string `yaml:"builder_ref,omitempty" json:"builder_ref,omitempty"`
StageName string `yaml:"stage_name,omitempty" json:"stage_name,omitempty"`
LayerStage string `yaml:"layer_stage,omitempty" json:"layer_stage,omitempty"`
CopySrc string `yaml:"copy_src,omitempty" json:"copy_src,omitempty"`
UID int `yaml:"uid,omitempty" json:"uid,omitempty"`
GID int `yaml:"gid,omitempty" json:"gid,omitempty"`
Home string `yaml:"home,omitempty" json:"home,omitempty"`
User string `yaml:"user,omitempty" json:"user,omitempty"`
Manifest string `yaml:"manifest,omitempty" json:"manifest,omitempty"`
HasLockFile bool `yaml:"has_lock_file,omitempty" json:"has_lock_file,omitempty"`
InstallCmd string `yaml:"install_cmd,omitempty" json:"install_cmd,omitempty"`
ManylinuxFix string `yaml:"manylinux_fix,omitempty" json:"manylinux_fix,omitempty"`
HasBuildScript bool `yaml:"has_build_script,omitempty" json:"has_build_script,omitempty"`
BuildScript string `yaml:"build_script,omitempty" json:"build_script,omitempty"`
Packages []string `yaml:"packages,omitempty" json:"packages,omitempty"`
Options []string `yaml:"options,omitempty" json:"options,omitempty"`
CacheMountsOwned string `yaml:"cache_mounts_owned,omitempty" json:"cache_mounts_owned,omitempty"`
CacheMountsAuto string `yaml:"cache_mounts_auto,omitempty" json:"cache_mounts_auto,omitempty"`
Inline bool `yaml:"inline,omitempty" 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.
func BuilderResolveInputFrom ¶ added in v0.2026213.1726
func BuilderResolveInputFrom(candyName, builderName string, builderDef *BuilderDef, ctx *BuildStageContext) BuilderResolveInput
BuilderResolveInputFrom builds the serializable BuilderResolveInput a builder plugin's OpResolve leg needs, from the host-computed BuildStageContext. Cache mounts are PRE-RENDERED to flag strings here (RenderCacheMounts) with the SAME separator/trailing the former cacheMountsOwned/cacheMountsAuto template funcs used, so the plugin's rendered stage is byte-identical to the former embedded-vocabulary render. Shared by the box-build path (sdk/deploykit resolveDetectionBuilderReply) AND the pod-overlay build-emit (candy/plugin-installstep stepEmitBuilder, via the deploykit re-export) + the inline-builder seam (charly resolveInlineBuilderSeam), R3 — hence exported.
type BuilderResolveReply ¶
type BuilderResolveReply struct {
Stage string `yaml:"stage,omitempty" json:"stage,omitempty"`
CopyArtifacts []string `yaml:"copy_artifacts,omitempty" json:"copy_artifacts,omitempty"`
CopyBinary string `yaml:"copy_binary,omitempty" json:"copy_binary,omitempty"`
InlineFragment string `yaml:"inline_fragment,omitempty" json:"inline_fragment,omitempty"`
}
#BuilderResolveReply is what a builder plugin returns from an OpResolve Invoke at image-generation time — the build-time BUILDER leg.
type BuilderReverseInput ¶
type BuilderReverseInput struct {
Candy string `yaml:"candy,omitempty" json:"candy"`
Builder string `yaml:"builder,omitempty" json:"builder"`
Context map[string]any `yaml:"context,omitempty" json:"context,omitempty"`
}
#BuilderReverseInput is the OpReverse params: the candy + its resolved stage-context keys.
type BuilderReverseReply ¶
type BuilderReverseReply struct {
ReverseOps []ReverseOp `yaml:"reverse_ops,omitempty" json:"reverse_ops,omitempty"`
}
#BuilderReverseReply is the OpReverse reply: the builder's teardown ops.
type BuilderRunOpts ¶
type BuilderRunOpts struct {
Engine string // "podman" or "docker"; default "podman"
BuilderImage string // full image ref, e.g. "ghcr.io/opencharly/fedora-builder:latest"
CandyDir string // absolute path to candy source (bind-mounted as /work)
ScriptBody string // shell script contents to pass to bash -c
// ResolveImage + EnsureImage inject the image-presence behavior that used to
// read *Config directly (Config stays a charly kernel type; the executor is an
// sdk mechanism reached through this seam). ResolveImage maps a short/namespaced
// builder ref to its concrete podman storage key (side-effect-free — safe in the
// dry-run path). EnsureImage guarantees the image is present, falling back to a
// local `charly box build <basename>` when it is project-buildable. The charly
// callers close over Cfg + ProjectDir; see charly/host_build_box_ref_resolve.go
// (ResolveImage) and charly/dispatch_build_ensure.go (EnsureImage).
ResolveImage func(image string) (string, error)
EnsureImage func(ctx context.Context, image string) error
// Bind-mounts. Keys are container paths; values are host paths.
// Set by the caller based on the builder kind — pixi/npm/cargo use
// the same HOME-subdir layout, aur uses a tmpdir for /tmp/aur-pkgs.
BindMounts map[string]string
// Env vars to set inside the container (in addition to HOME).
Env map[string]string
// HostHome is the invoking user's absolute home dir. Set via HOME=
// inside the container so path-baking (pixi shebangs, etc.) resolves
// to a path that's valid both inside (via bind-mount) and outside.
HostHome string
// DryRun returns the command line that would run without executing.
// Used for --dry-run deploy.
DryRun bool
// RunAsRoot spawns the container as UID 0 instead of the host's
// UID. Needed for builders whose script body uses `sudo` against
// users that don't exist in the builder image's /etc/passwd —
// e.g. AUR's makepkg+yay flow inside a non-OCI-staged builder
// image. Under rootless podman, root-in-container maps to the
// host's UID, so file ownership in bind-mounts stays correct.
RunAsRoot bool
}
BuilderRunOpts describes one `podman run <builder>` invocation. The BuilderRun implementation (host-engine builder exec) lives in sdk/kit; this options struct is spec-homed because DeployExecutor.RunBuilder carries it across the IR.
type BuilderStep ¶ added in v0.2026210.2308
type BuilderStep struct {
Builder string // "pixi" | "npm" | "cargo" | "aur"
BuilderImage string // resolved image ref (e.g. "fedora-builder:2026.04")
CandyName string // layer that triggered this builder (for ledger)
CandyDir string // absolute layer source path (bind-mounted as /work)
Phase Phase // typically PhaseInstall
Artifacts []ArtifactRef // outputs to extract (empty for user-scope pixi/npm/cargo; populated for aur)
// Builder-specific template context — the compiler populates this from
// the candy's manifest files + the embedded builder definition (charly/charly.yml).
RawStageContext map[string]any
// LocalPkg is the package format's localpkg contract, populated by the
// compiler for the `aur` builder only. The aur builder produces package
// files (.pkg.tar.zst) that the host/VM deploy targets install onto the
// venue via the SAME config-driven transfer+install leg the localpkg step
// uses (R3) — so the install command + package glob come from
// the embedded `pac.local_pkg` vocabulary, never a hardcoded literal. Nil for
// pixi/npm/cargo (home-artifact builders, no package-file install).
LocalPkg *LocalPkg
// BuilderDef is the resolved builder definition from the embedded build vocabulary (charly/charly.yml) for this builder
// (img.BuilderConfig.Builder[Builder]), populated by the compiler. The
// host-venue deploy targets render its phase.install.host cell via
// renderBuilderScript — the plain-shell analog of the container stage — so the
// host build script is config-driven, not hardcoded Go. nil only on
// synthetic test paths that don't supply a BuilderConfig.
BuilderDef *BuilderDef
// PreResolvedReverse is the builder's teardown ops, resolved HOST-SIDE in the build
// PRE-PASS (builder_preresolve.go → the externalized builder plugin's OpReverse) and stashed
// here so Reverse() is a PURE getter — the per-builder reverse-op KIND (pixi-env-remove /
// package-remove / …) is exactly the logic externalized out-of-process, and Reverse() runs
// host-side (step-view projection + RunHostStep) with no registry handle. Carried across the
// wire on InstallStepView.ReverseOps (the host-computed teardown field) and restored by
// stepFromView. Nil for a custom candy builder / a direct compile with no pre-pass.
PreResolvedReverse []ReverseOp
}
BuilderStep runs a multi-stage builder (pixi/npm/cargo/aur). On the OCI target this emits a FROM + RUN stage plus a final-stage COPY --from. On the host target this emits a `podman run <builder>` with HOME-remap and appropriate bind-mounts.
func (*BuilderStep) Kind ¶ added in v0.2026210.2308
func (s *BuilderStep) Kind() StepKind
func (*BuilderStep) RequiresGate ¶ added in v0.2026210.2308
func (s *BuilderStep) RequiresGate() Gate
func (*BuilderStep) Reverse ¶ added in v0.2026210.2308
func (s *BuilderStep) Reverse() []ReverseOp
func (*BuilderStep) Scope ¶ added in v0.2026210.2308
func (s *BuilderStep) Scope() Scope
func (*BuilderStep) Venue ¶ added in v0.2026210.2308
func (s *BuilderStep) Venue() Venue
type BundleConfig ¶ added in v0.2026215.1748
type BundleConfig struct {
Provides *ProvidesConfig `yaml:"provides,omitempty" json:"provides,omitempty"`
Bundle map[string]BundleNode `yaml:"deploy" json:"deploy"`
// Sidecar carries the project's sidecar-template library as OPAQUE bodies
// (the raw PluginKinds["sidecar"] map). candy/plugin-sidecar's OpResolve merges
// these UNDER each deploy node's own overrides; the kernel reads no fields
// (the sidecar de-type, Cutover D).
Sidecar map[string]json.RawMessage `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
}
BundleConfig represents per-machine deployment overrides (~/.config/charly/charly.yml). Only runtime/deployment fields are supported — build-time fields are structurally excluded.
Schema v4: the top-level map key is `deployment:` (singular, flat). The legacy `images:` / `deployments.images.*` nesting is gone — all target kinds (host / vm / pod / k8s) live under the single `deployment:` map.
func (*BundleConfig) Lookup ¶ added in v0.2026215.1748
func (dc *BundleConfig) Lookup(deployName, instance string) (BundleNode, bool)
Lookup returns the BundleNode for (deployName, instance), or (zero, false) when the entry is absent. Safe to call on a nil *BundleConfig — lets callers chain `loadDeployConfigForRead(...).Lookup(deployName, instance)` without a separate nil check. deployName is the charly.yml key base the caller is operating on (typically c.Image), NOT the baked image short-name — for a kind:check bed or Pattern-B deploy the two differ. Pass the deploy key, never a value derived from an image label (see MergeDeployOntoMetadata).
func (*BundleConfig) LookupKey ¶ added in v0.2026215.1748
func (dc *BundleConfig) LookupKey(key string) (BundleNode, bool)
LookupKey looks up a deploy entry by its full charly.yml key (e.g. "foo", "foo/instance", "vm:name"). Safe on nil receiver.
type BundleNode ¶
type BundleNode = Deploy
--- top-level entity types ---
func FindVMClaimant ¶ added in v0.2026213.1726
func FindVMClaimant(tree map[string]BundleNode, vmEntity string) (string, BundleNode, bool)
FindVMClaimant returns the first node claiming the given VM entity via requires_exclusive.
func MergeBundleNode ¶ added in v0.2026213.1726
func MergeBundleNode(dst, src BundleNode) BundleNode
MergeBundleNode overlays src onto dst: every authored (yaml-tagged, non-zero) field of src wins, and the loader-DERIVED structural TREE fields (Target/Children/Members) merge explicitly (src non-zero wins) because the reflect loop skips yaml:"-" fields. Pure (reflect over the spec.BundleNode value type).
type CLIArg ¶
type CLIArg struct {
Prop string `yaml:"prop,omitempty" json:"prop"`
Name string `yaml:"name,omitempty" json:"name"`
Type string `yaml:"type,omitempty" json:"type"`
Help string `yaml:"help,omitempty" json:"help,omitempty"`
Enum []string `yaml:"enum,omitempty" json:"enum,omitempty"`
Default string `yaml:"default,omitempty" json:"default,omitempty"`
HasDefault bool `yaml:"has_default,omitempty" json:"has_default,omitempty"`
Required bool `yaml:"required,omitempty" json:"required,omitempty"`
IsBool bool `yaml:"is_bool,omitempty" json:"is_bool,omitempty"`
IsSlice bool `yaml:"is_slice,omitempty" json:"is_slice,omitempty"`
IsMap bool `yaml:"is_map,omitempty" json:"is_map,omitempty"`
Negated bool `yaml:"negated,omitempty" json:"negated,omitempty"`
ElemType string `yaml:"elem_type,omitempty" json:"elem_type,omitempty"`
}
CLI reflection is a wire contract between command plugins, the Charly host, and MCP. It is CUE-owned like every other authored or transported shape.
type CLILeaf ¶
type CLILeaf struct {
Path string `yaml:"path,omitempty" json:"path"`
Help string `yaml:"help,omitempty" json:"help,omitempty"`
Hidden bool `yaml:"hidden,omitempty" json:"hidden,omitempty"`
Positionals []CLIArg `yaml:"positionals,omitempty" json:"positionals,omitempty"`
Flags []CLIArg `yaml:"flags,omitempty" json:"flags,omitempty"`
}
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 CacheMountSpec ¶ added in v0.2026210.2308
CacheMountSpec mirrors the BuildKit cache-mount configuration carried in the embedded build vocabulary's format/builder definitions (charly/charly.yml). The OCI target renders these as `--mount=type=cache,...`; the host target ignores them.
type CacheMountView ¶
type CacheMountView struct {
Dst string `yaml:"dst,omitempty" json:"dst,omitempty"`
Sharing string `yaml:"sharing,omitempty" json:"sharing,omitempty"`
}
#CacheMountView is the wire mirror of package main's CacheMountSpec.
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 / hooks ---
// (the candy-level `libvirt:` raw-XML-snippet field was removed — zero live Go
// consumers, retired alongside the box-level libvirt hard-cutover; migrated
// away by candy/plugin-migrate's stripCandyLibvirtField. The paired `kind: vm`
// entity's OWN `libvirt: {...}` domain config, sdk/schema/vm.cue #Vm.libvirt,
// is unrelated and unaffected.)
Security *Security `yaml:"security,omitempty" json:"security,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"`
AgentProvide []AgentRuntimeCapability `yaml:"agent_provide,omitempty" json:"agent_provide,omitempty"`
// terminal_profile keeps the authored-key convention: every collection-
// holding authored key in this block is singular (env_provide, mcp_provide,
// agent_provide, …). The baked wire/label shape is the PLURAL
// `terminal_profiles` (schema/boxmetadata.cue; spec.LabelTerminalProfiles
// "ai.opencharly.terminal_profiles") — a key burned into built images, so
// the singular-authored ↔ plural-wire mapping is deliberate and stable,
// not a typo to "align" (renaming either side is a format change).
TerminalProfiles map[string]TerminalProfile `yaml:"terminal_profile,omitempty" json:"terminal_profile,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"`
// register: an optional NAME-BLIND post-retrieve processing hint (e.g. "kubeconfig") —
// what to do with the retrieved artifact beyond the generic retrieve/rewrite pipeline,
// dispatched by a host-side registry keyed on this word (retrieveArtifactsAndK3s /
// artifactRegisterHandlers). Replaces a hardcoded candy-NAME check
// (deployHasCandy(candyList, "k3s-server")) with a declaration the candy itself carries,
// so a future candy needing the same registration need not be named "k3s-server".
Register string `yaml:"register,omitempty" json:"register,omitempty"`
}
CandyArtifact — a file the candy publishes back to the operator post-setup.
type CandyArtifactRewrite ¶
type CandyCandidate ¶ added in v0.2026210.808
type CandyCandidate struct {
Scanned ScannedCandy
Version string // per-entity version (Scanned.Model.Version) — mandatory, never ""
GitTag string // fetch coordinate (the @github :vTAG)
Source string // "<repo>@<git-tag>" for warning attribution
}
CandyCandidate is one fetched materialization of a bare candy ref. The git tag is the fetch coordinate; Version is the candy's own per-entity `version:`. Relocated from loaderkit (candy_version.go) as loader-result DATA; the PickCandyVersion ARBITER that consumes it stays in loaderkit (it needs kit's semver/CalVer comparison — a mechanism).
type CandyCapabilitiesView ¶
type CandyCapabilitiesView struct {
PreserveUser bool `yaml:"preserve_user,omitempty" json:"preserve_user,omitempty"`
}
#CandyCapabilitiesView — the per-candy capability surface a consumer reads off the envelope. Carries exactly the caps the validate ENGINE consumes (preserve_user today); grow it only when a relocated rule reads a further cap. Mirrors the read half of the runtime candy's `capabilities:` block (charly AggregatedCandyCaps).
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 CandyModel ¶
type CandyModel struct {
// --- identity ---
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
SourceDir string `yaml:"source_dir,omitempty" json:"source_dir,omitempty"`
Reboot bool `yaml:"reboot,omitempty" json:"reboot,omitempty"`
// --- host-precomputed predicates (#67 render-DRIVE move) ---
// has_content / has_install_files are the LIVE *Candy.HasContent() / HasInstallFiles()
// verdicts, computed HOST-SIDE (the live Candy has the env/ports/route/volumes/aliases/
// libvirt/init fields + the fs-probe caches the envelope CandyModel cannot recompute
// faithfully) and carried here so the specCandyAdapter matches the live *Candy
// byte-exactly — the candy-graph composition (ExpandCandy/ResolveCandyOrder: a composing
// candy with no content is skipped) and the pixi-bound intermediate detection both gate
// on these. A pure-composition candy (e.g. agent-forwarding: candy: [gnupg,direnv,
// ssh-client], only a check plan) has has_content=false, so it is correctly EXCLUDED
// from the candy graph — matching the pre-move core render.
HasContent bool `yaml:"has_content,omitempty" json:"has_content,omitempty"`
HasInstallFiles bool `yaml:"has_install_files,omitempty" json:"has_install_files,omitempty"`
// --- plan + lowered ops ---
Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`
RunOps []Op `yaml:"run_ops,omitempty" json:"run_ops,omitempty"`
// --- services / extract / data / apk ---
Service []CandyService `yaml:"service,omitempty" json:"service,omitempty"`
Extract []CandyExtract `yaml:"extract,omitempty" json:"extract,omitempty"`
Data []CandyData `yaml:"data,omitempty" json:"data,omitempty"`
Apk []ApkPackageSpec `yaml:"apk,omitempty" json:"apk,omitempty"`
// --- package surface (resolved) ---
TopPackages []string `yaml:"top_packages,omitempty" json:"top_packages,omitempty"`
LocalPkg map[string]string `yaml:"localpkg,omitempty" json:"localpkg,omitempty"`
FormatSections map[string]PackageSection `yaml:"format_sections,omitempty" json:"format_sections,omitempty"`
TagSections map[string]TagPkgConfig `yaml:"tag_sections,omitempty" json:"tag_sections,omitempty"`
// --- env / vars / route / shell ---
Vars map[string]string `yaml:"vars,omitempty" json:"vars,omitempty"`
Env *EnvConfig `yaml:"env,omitempty" json:"env,omitempty"`
Route *RouteConfig `yaml:"route,omitempty" json:"route,omitempty"`
Shell *Shell `yaml:"shell,omitempty" json:"shell,omitempty"`
// --- volumes / aliases ---
Volumes []CandyVolume `yaml:"volumes,omitempty" json:"volumes,omitempty"`
Aliases []CandyAlias `yaml:"aliases,omitempty" json:"aliases,omitempty"`
// --- security / hooks (the OCI-label-collector surface: CollectSecurity/CollectHooks
// read this per-candy field then apply the box-level #ResolvedBoxView.security override) ---
Security *Security `yaml:"security,omitempty" json:"security,omitempty"`
Hook *CandyHook `yaml:"hook,omitempty" json:"hook,omitempty"`
// --- validate read-surface (candy-local config the validate ENGINE checks) ---
// external_builder: the reserved word of an EXTERNAL builder plugin this candy selects
// (from the candy manifest external_builder:). validateCandyContents reads it to accept a
// candy whose only content is an external-builder selection (deploykit EmitExternalBuilderStages).
ExternalBuilder string `yaml:"external_builder,omitempty" json:"external_builder,omitempty"`
// (the candy-level `libvirt:` field was removed here alongside #Candy — see
// candy.cue's note; CollectLibvirtSnippets, its only reader, was already dead.)
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
PortRelayPorts []int `yaml:"port_relay,omitempty" json:"port_relay,omitempty"`
ServiceFiles []string `yaml:"service_files,omitempty" json:"service_files,omitempty"`
EnvRequire []EnvDependency `yaml:"env_require,omitempty" json:"env_require,omitempty"`
EnvAccept []EnvDependency `yaml:"env_accept,omitempty" json:"env_accept,omitempty"`
SecretRequire []EnvDependency `yaml:"secret_require,omitempty" json:"secret_require,omitempty"`
SecretAccept []EnvDependency `yaml:"secret_accept,omitempty" json:"secret_accept,omitempty"`
MCPRequire []EnvDependency `yaml:"mcp_require,omitempty" json:"mcp_require,omitempty"`
MCPAccept []EnvDependency `yaml:"mcp_accept,omitempty" json:"mcp_accept,omitempty"`
// --- W9 mass-edit interface-completeness fill: the remaining fields the 42-file
// CandyReader repoint needs, already authored on #Candy (candy.cue) but not yet
// projected onto the build model. artifact/requires_capability/secret/port mirror
// candy.cue's own fields+shapes exactly; capability widens the CandyView's narrow
// (preserve_user-only) #CandyCapabilitiesView to the full authored #CandyCapability.
Artifact []CandyArtifact `yaml:"artifact,omitempty" json:"artifact,omitempty"`
RequiresCapability []string `yaml:"requires_capability,omitempty" json:"requires_capability,omitempty"`
Capability *CandyCapability `yaml:"capability,omitempty" json:"capability,omitempty"`
Secret []CandySecret `yaml:"secret,omitempty" json:"secret,omitempty"`
// port mirrors candy.cue's own union shape exactly (a plain int OR a "proto:port" string,
// normalized Go-side to PortSpec — there is no reusable #PortSpec def, per candy.cue's own note).
Port []PortSpec `yaml:"port,omitempty" json:"port,omitempty"`
// bake_plugin: the FINAL bare-string form (post remote-sibling qualification — see
// spec.CandyRefs, the hand-written pipeline-state type carrying the rich pre-qualification
// form). generate.go's emitBakedPlugins reads this to COPY the plugin's pre-built provider
// binary into every composing image.
BakePlugin []CandyRef `yaml:"bake_plugin,omitempty" json:"bake_plugin,omitempty"`
}
#CandyModel — the serializable candy build model. Every field optional (a projection). name is the stable key. Field order mirrors the runtime *Candy accessors (deploykit.CandyModel interface).
type CandyReader ¶
type CandyReader interface {
GetName() string
GetSourceDir() string
GetVersion() string
Vars() map[string]string
PlanSteps() []Step
Reboot() bool
Apk() []ApkPackageSpec
EnvConfig() (*EnvConfig, error)
Service() []CandyService
Shell() *Shell
TopPackages() []string
FormatSection(name string) *PackageSection
LocalPkg(format string) string
TagSection(tag string) *TagPkgConfig
HasFile(filename string) bool
// P8 render-delta accessors (build-mode graph/render surface). Field-backed
// members use Get* to avoid a field/method name collision on the charly *Candy
// implementer; the three already-methods keep their names.
GetIncludedCandy() []CandyRefEntry // candy: composition refs (splicing)
GetRequire() []CandyRefEntry // require: deps (ordering + resolution)
// GetBakePlugin (W9): bake_plugin: out-of-tree plugin candies baked into composing images
// (generate.go emitBakedPlugins). Same bare-string-wrapped-as-CandyRefEntry shape as
// GetRequire/GetIncludedCandy above (CandyModel.BakePlugin is the FINAL bare-string wire
// form; the adapter re-wraps it so callers keep using .Bare() uniformly).
GetBakePlugin() []CandyRefEntry
HasContent() bool
HasInstallFiles() bool // at least one install file (drives pixi-bound detection)
PixiManifest() string
GetHasPackageJson() bool
GetHasCargoToml() bool
GetExternalBuilder() string // external_builder: word (the out-of-tree builder plugin this candy selects)
HasFormatPackages() bool
GetRemote() bool // true if the candy came from a remote repo
HasExtract() bool
Extract() []CandyExtract
HasData() bool
Data() []CandyData
GetHasPixiLock() bool
GetRepoPath() string
GetSubPathPrefix() string
HasEnv() bool
HasRoute() bool
Route() (*RouteConfig, error)
// P8 init-cluster accessors (emitInitAssembly/emitInitFragmentStages/
// generateInitFragments). RelayPorts wraps the field-backed PortRelayPorts to
// avoid a field/method name collision on the charly *Candy implementer.
HasInit(initName string) bool // this candy contributes to the named init system
ServiceFiles() []string // file_copy-model service unit paths (globbed)
RelayPorts() []int // port_relay: ports (init-agnostic)
// P8 writeCandySteps accessors.
HasTasks() bool // the candy has any tasks: (runOps non-empty)
RunOps() []Op // the candy's plan lowered to build-mode run ops
// OCI-label-collector surface (CollectSecurity/CollectHooks/layer_secrets):
// per-candy security/hooks + the six env/secret/mcp dependency lists.
Security() *Security
Hooks() *CandyHook
EnvRequire() []EnvDependency
EnvAccept() []EnvDependency
SecretRequire() []EnvDependency
SecretAccept() []EnvDependency
MCPRequire() []EnvDependency
MCPAccept() []EnvDependency
// W9 mass-edit interface-completeness fill: the remaining accessors the 42-file
// repoint needs (CollectBoxAlias/CollectBoxVolume/ports.go/layer_capabilities.go/…)
// — every one of these already exists on the charly *Candy implementer (layers.go);
// this is a pure exposure widening, no new *Candy behavior. (Libvirt()/HasLibvirt()
// were added here for this same fill, to serve charly/libvirt.go's
// CollectLibvirtSnippets — that caller was dead code [box-level `libvirt:` was
// removed in the VM hard-cutover] and was deleted, and the candy-level `libvirt:`
// field itself was removed from the schema in the SAME cutover [candy.cue /
// candymodel.cue, migrated by candy/plugin-migrate's stripCandyLibvirtField] — so
// these two accessors are gone too, not merely deprecated.)
// Consumer-set audited: HasAnyInit/HasAnyPackages/HasApk/HasService/HasTagPackages
// were considered and DROPPED — zero external caller outside layers.go itself (they
// back OTHER *Candy-internal composition, e.g. HasContent/HasInstallFiles, which
// stays a host-precomputed field per the #67 predicate-carrying pattern above).
Alias() []CandyAlias
HasAliases() bool
Volume() []CandyVolume
HasVolumes() bool
Artifact() []CandyArtifact
Capabilities() *CandyCapability
RequiresCapabilities() []string
Engine() string
EnvProvides() map[string]string
MCPProvide() []CandyMCPProvide
Secret() []CandySecret
Port() ([]string, error)
PortSpecs() []PortSpec
LocalPkgFormats() []string
HasEnvAccepts() bool
HasEnvProvides() bool
HasEnvRequires() bool
HasMCPAccepts() bool
HasMCPProvides() bool
HasMCPRequires() bool
HasPorts() bool
HasSecretAccepts() bool
HasSecretRequires() bool
// W9 pipeline-retype fill: the candy's OWN `plugin:` declaration (registerExternalVerbsFromCandies'
// data-read half, findCommandPluginCandy) — the provider REGISTRATION itself stays core-M
// (touches the providerRegistry), but recognizing which capability words a scanned candy
// declares is a plain data read the interface must expose once *Candy is gone.
IsPluginCandy() bool
GetPluginSource() string
GetPluginProviders() []string
// W9 pipeline-retype fill: identity-view scalars multiple OCI-label-collector consumers
// (render_baked_metadata.go's candyStatus/worst-of-status + info-parts walk) need directly —
// widely-needed enough to earn a real interface method, unlike the FULL identity view
// (Description/Info/IsPlugin's sibling fields + the FormatSections/TagSections/RunOps maps)
// resolved_project_host.go alone needs via the RawCandy() escape hatch.
GetStatus() string
GetDescription() string
// W9 federated-control-plane fill: the candy-declared `agent_provide:`/`terminal_profile:`
// surfaces (56b1a6b) the OCI-label-collector (render_baked_metadata.go's per-box
// agent/terminal aggregation) needs once *Candy is gone.
AgentProvide() []AgentRuntimeCapability
HasAgentProvides() bool
TerminalProfiles() map[string]TerminalProfile
}
candy_reader.go — CandyReader, the read-only view of a runtime candy that the deploy-plan compiler (deploykit.BuildDeployPlan) and the build-render engine consume. It lives in spec (the shared contract home, alongside the CUE-generated wire envelopes) rather than sdk/deploykit so an import-clean charly file can reach it WITHOUT crossing the mechanism-kit import-purity line — matches the DocParser/ProjectWalker precedent in loader_seam.go. The concrete runtime Candy (charly core) implements it; deploykit's specCandyAdapter implements it from the CUE-generated CandyModel/CandyView wire views. Every referenced type below is already spec-native (Step/EnvConfig/EnvDependency/Op/Security/CandyHook/CandyService/Shell/…) or promoted here alongside the interface (CandyRefEntry, below) — a spec-hosted contract never needs an sdk mechanism-kit import.
type CandyRecord ¶ added in v0.2026215.1748
type CandyRecord struct {
SchemaVersion string `json:"schema_version,omitempty"`
Candy string `json:"candy"`
Version string `json:"version,omitempty"`
DeployedBy []string `json:"deployed_by"` // set of deploy IDs
DeployedAt string `json:"deployed_at"`
BuilderImage string `json:"builder_image,omitempty"`
Steps []StepRecord `json:"steps,omitempty"` // completed steps, in install order
ReverseOps []ReverseOp `json:"reverse_ops,omitempty"` // precomputed ops for teardown
}
CandyRecord is the per-candy ledger entry. Lists concrete artifacts (packages installed, files written, services enabled, env.d file created, repo changes) so reversal doesn't need to re-compile the plan from the candy manifest.
type CandyRefEntry ¶
type CandyRefEntry struct {
Raw string // original ref, e.g. "@github.com/org/repo/candy/x:v1" or bare "x"
// Resolved is the qualified candy-map key assigned when a freshly-fetched
// remote candy's plain-name sibling deps are qualified to
// "<repo>/<subpathprefix><name>" (qualifyRemoteSiblingDeps). Empty for every
// other ref, where the map key derives from Raw.
Resolved string
}
CandyRefEntry is a single candy reference as authored in the candy manifest `require:` / `candy:` (or charly.yml `candy:`). It carries the ORIGINAL ref string — with any `@repo` prefix and `:version` suffix — as the single source of truth; the bare map-key form (.Bare()) and the pinned version (.Version()) are DERIVED on demand.
func ToCandyRefEntries ¶
func ToCandyRefEntries(raw []string) []CandyRefEntry
ToCandyRefEntries wraps raw ref strings into CandyRefEntry values. Returns nil for a nil/empty input so an absent list stays absent.
func (CandyRefEntry) Bare ¶
func (r CandyRefEntry) Bare() string
Bare returns the candy-map key (no @ prefix, no :version) — the form used for dependency resolution and graph keying.
func (CandyRefEntry) IsRemote ¶
func (r CandyRefEntry) IsRemote() bool
IsRemote reports whether this is an @-prefixed remote ref.
func (CandyRefEntry) Version ¶
func (r CandyRefEntry) Version() string
Version returns the pinned version (the ":vX" suffix), or "" for an unpinned remote ref or a local (bare-name) ref.
type CandyRefs ¶
type CandyRefs struct {
Require []CandyRefEntry
IncludedCandy []CandyRefEntry
BakePlugin []CandyRefEntry
}
CandyRefs carries the RICH require:/candy:/bake_plugin: refs (CandyRefEntry, with a mutable .Resolved) a freshly scanned candy declares.
SDD classification (hand-written, non-wire — precedent: ParsedProject/LoadedProject above, the original hand-written-contract types this seam file establishes): CandyRefs is same-process PIPELINE STATE crossing ONLY the compiled-in typed CandyScanner seam — it is never marshaled, because the loader plugin is bootstrap-critical and ALWAYS compiled-in (see the package doc above: "the loader must ALWAYS resolve... registered at init() before the first load"), so this seam never crosses a real wire the way an out-of-process plugin's gRPC envelope would. It exists only between ScanCandyManifest and the host's qualifyRemoteSiblingDeps (which sets .Resolved on a remote candy's plain-name sibling deps) and the FINAL bare-string conversion into CandyView.Require/.IncludedCandy (mirrors the pre-move projectCandyView's bareRefs() call, which ran AFTER qualification on the live *Candy — this type is what lets that same ordering survive the *Candy struct's departure). The FINAL bare-string form lands on CandyView.Require/ .IncludedCandy and CandyModel.BakePlugin (FinalizeCandyRefs, sdk/loaderkit).
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 CandyScanner ¶
type CandyScanner interface {
ScanCandyManifest(path, name, manifestName string, parseManifest func(path string) (*Candy, error)) (CandyModel, CandyView, CandyRefs, error)
// ScanInlineCandy builds the two views for a candy declared INLINE in a unified charly.yml —
// ly is already the parsed body (no manifest file, no parseManifest seam needed). sourceDir is
// the charly.yml's own directory.
ScanInlineCandy(name, sourceDir string, ly *Candy) (CandyModel, CandyView, CandyRefs)
// ScanRemoteCandy scans specific candies out of a downloaded remote repository directory —
// only the bare refs in wantRefs (each "github.com/org/repo/candy/x" form). Sets each result's
// CandyView.Remote/.RepoPath/.SubPathPrefix and runs the remote-sibling-dep qualification
// (QualifyRemoteSiblingDeps) before returning, mirroring the pre-move charly/layers.go
// ScanRemoteCandy, which did the same two things (post-scan.Remote/RepoPath/SubPathPrefix
// mutation, then qualifyRemoteSiblingDeps) on the live *Candy it had just built.
ScanRemoteCandy(repoDir, repoPath string, wantRefs map[string]bool, parseManifest func(path string) (*Candy, error)) (map[string]ScannedCandy, error)
}
CandyScanner is the swappable CANDY-SCAN seam (W9): the loader plugin candy implements it (candy/plugin-loader, delegating to loaderkit.ScanCandyManifest), and the host resolves the registered loader provider to it and calls it once per candy directory. Typed (no wire envelope) — the compiled-in placement passes a live parseManifest callback (a Go function value, exactly like WalkSeams.Parser above) since the candy-manifest parse itself is registry-coupled (it threads the registered DocParser + the registry-derived Threaded snapshot) and so stays a HOST-injected seam rather than moving into loaderkit — only the SCAN+CONSTRUCT logic (fs-probes, the bake_plugin/package-derivation/port-normalization business logic) moves. Returns the two resolved envelope views (spec.CandyModel + spec.CandyView) DIRECTLY — the same shape sdk/deploykit.NewSpecCandyModel already consumes to build a spec.CandyReader, so core never needs a concrete Candy struct to hold the scan result.
ScanCandyManifest is named distinctly from the ESTABLISHED exported charly.ScanCandy(dir) (the whole-project scan-all-candies entry point, charly/layers.go) — a similar name on a single-candy-directory scan risks confusion once both exist side by side during the cutover.
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"`
// exec_start_pre / exec_start_post — ordered hook commands run around a CUSTOM
// (exec:) entry's own start, rendered by the init system's service_template
// (systemd: ExecStartPre= / ExecStartPost= lines, emitted in authored order).
// They exist for guards belonging to the SERVICE's lifecycle rather than to the
// install timeline: a plan step runs ONCE, at deploy, whereas a pre-start hook
// re-asserts a host precondition on EVERY start (a restart included), and a
// post-start hook is the only place a guard runs ALONGSIDE the daemon it
// guards — the sole vantage from which that daemon's own socket/API is
// reachable. Both are init-supervised, so a failure surfaces in the unit's
// status instead of being swallowed; prefix a command with "-" for systemd's
// ignore-failure semantics.
// Canonical consumer: candy/k3s-server (the cgroup cpuset-delegation assertion
// and the CRD-establishment wedge heal).
ExecStartPre []string `yaml:"exec_start_pre,omitempty" json:"exec_start_pre,omitempty"`
ExecStartPost []string `yaml:"exec_start_post,omitempty" json:"exec_start_post,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 CandyView ¶
type CandyView struct {
Name string `yaml:"name,omitempty" json:"name"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Status string `yaml:"status,omitempty" json:"status,omitempty"`
Info string `yaml:"info,omitempty" json:"info,omitempty"`
Remote bool `yaml:"remote,omitempty" json:"remote,omitempty"`
RepoPath string `yaml:"repo_path,omitempty" json:"repo_path,omitempty"`
// sub_path_prefix — the parent directory within the repo for sibling ref resolution
// (e.g. "candy/"). Build-mode remote-candy COPY-source leg: RepoPath + SubPathPrefix +
// ref reconstruct a remote candy's context path. Filled by the resolve projector; the
// specCandyAdapter returns it so the plugin-side render reproduces remote COPY sources
// WITHOUT the live *Candy (K3-D render move, #67 — the GetSubPathPrefix deferral).
SubPathPrefix string `yaml:"sub_path_prefix,omitempty" json:"sub_path_prefix,omitempty"`
IsPlugin bool `yaml:"is_plugin,omitempty" json:"is_plugin,omitempty"`
// the candy's OWN declared plugin block (validatePluginCandy SUBJECT, task #60): the
// `plugin.providers:` capability strings + `plugin.source:` so the validate plugin can check each
// declared BUILTIN `<class>:<word>` is a member of ResolvedProject.ProviderCapabilities (the TARGET
// set). Empty for a non-plugin candy. is_plugin stays the cheap presence bool for inspect/list.
PluginProviders []string `yaml:"plugin_providers,omitempty" json:"plugin_providers,omitempty"`
PluginSource string `yaml:"plugin_source,omitempty" json:"plugin_source,omitempty"`
Require []CandyRef `yaml:"require,omitempty" json:"require,omitempty"`
IncludedCandy []CandyRef `yaml:"candy,omitempty" json:"candy,omitempty"`
EnvProvides map[string]string `yaml:"env_provide,omitempty" json:"env_provide,omitempty"`
MCPProvide []CandyMCPProvide `yaml:"mcp_provide,omitempty" json:"mcp_provide,omitempty"`
AgentProvide []AgentRuntimeCapability `yaml:"agent_provide,omitempty" json:"agent_provide,omitempty"`
TerminalProfiles map[string]TerminalProfile `yaml:"terminal_profiles,omitempty" json:"terminal_profiles,omitempty"`
Ports []int64 `yaml:"port,omitempty" json:"port,omitempty"`
ServiceNames []string `yaml:"service_name,omitempty" json:"service_name,omitempty"`
// per-candy list-subcommand sources: `charly box list routes|volumes|aliases|services`.
// route/volumes/aliases carry the authored detail each subcommand prints (their presence IS
// the RouteCandy/VolumeCandy/AliasCandy predicate). has_init + port_relay reconstruct the
// InitCandy predicate (HasAnyInit() || PortRelayPorts>0) for `list services`.
Route *RouteConfig `yaml:"route,omitempty" json:"route,omitempty"`
Volumes []CandyVolume `yaml:"volumes,omitempty" json:"volumes,omitempty"`
Aliases []CandyAlias `yaml:"aliases,omitempty" json:"aliases,omitempty"`
HasInit bool `yaml:"has_init,omitempty" json:"has_init,omitempty"`
// init_systems — the PER-INIT-SYSTEM trigger map (W9 finding: HasInit alone is one AGGREGATE
// "any init" bool, which cannot answer "does this candy trigger init system Y" — the question
// deploykit's HasInit(initName) / sdk/deploykit's EmitInitFragmentStages actually ask). Mirrors
// the live *Candy.InitSystems field exactly; populated by the SAME cross-candy host-completion
// pass PopulateCandyInitSystem runs (once, after every candy is scanned) — see
// sdk/deploykit.specCandyAdapter.HasInit, which reads this field directly.
InitSystems map[string]bool `yaml:"init_systems,omitempty" json:"init_systems,omitempty"`
PortRelayPorts []int `yaml:"port_relay,omitempty" json:"port_relay,omitempty"`
// capabilities — the per-candy capability surface the validate ENGINE reads (task #60,
// ruling a). The projector fills it from the candy's `capabilities:` block; the validate
// plugin re-derives it via boxPreserveUser over a box's candy order (a boolean OR of
// preserve_user, order-independent) + the PreserveUser rule (the validatePackagedServices
// box-arm). An aggregate over RESOLVED models belongs on the
// envelope, not a host route-B diagnostic — a lean envelope is not a virtue when it keeps
// an R-rule in core.
Capabilities *CandyCapabilitiesView `yaml:"capabilities,omitempty" json:"capabilities,omitempty"`
}
#CandyView — the resolved candy GRAPH node a consumer reads: identity + dep-graph + provides + ports/services (the exported surface of the runtime Candy). The Has* filesystem probes, the unexported package/service sections, and the *CandyPluginDecl stay host — this is NOT the candy BUILD model (plan-steps / package-format sections), which is the CandyModel / S-CM concern (K3-D), distinct by design. require / candy pin to the bare map-key ref form (#CandyRef is @go(-), so a list of it generates []string). name is the stable key.
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 CapabilityInitDef ¶
type CapabilityInitDef struct {
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_commands,omitempty" json:"management_commands,omitempty"`
}
#CapabilityInitDef — the deploy-relevant subset of an InitDef (ai.opencharly.init_def).
type CapabilityService ¶
type CapabilityService struct {
Name string `yaml:"name,omitempty" json:"name"`
Scope string `yaml:"scope,omitempty" json:"scope,omitempty"`
Enable bool `yaml:"enable,omitempty" json:"enable,omitempty"`
UsePackaged string `yaml:"use_packaged,omitempty" json:"use_packaged,omitempty"`
Exec string `yaml:"exec,omitempty" json:"exec,omitempty"`
Env map[string]string `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"`
Stdout string `yaml:"stdout,omitempty" json:"stdout,omitempty"`
StopTimeout string `yaml:"stop_timeout,omitempty" json:"stop_timeout,omitempty"`
Kind string `yaml:"kind,omitempty" json:"kind,omitempty"`
Events string `yaml:"events,omitempty" json:"events,omitempty"`
AutoStart *bool `yaml:"auto_start,omitempty" json:"auto_start,omitempty"`
StartRetries int `yaml:"start_retries,omitempty" json:"start_retries,omitempty"`
StartSec 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"`
Init string `yaml:"init,omitempty" json:"init,omitempty"`
Candy string `yaml:"candy,omitempty" json:"candy,omitempty"`
}
#CapabilityService — the full structured service spec baked into ai.opencharly.service. name required; all else omitempty. Mirrors the hand struct byte-for-byte.
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 CheckBedGpuPrereqReply ¶ added in v0.2026217.301
type CheckBedGpuPrereqRequest ¶ added in v0.2026217.301
type CheckBedGpuPrereqRequest struct {
Tokens []string `yaml:"tokens,omitempty" json:"tokens,omitempty"`
}
#CheckBedRequest DIED (#55 W3 B2-full): the "check-bed" op-discriminated HostBuild seam it carried is gone. The compiled-in plugin-check now self-orchestrates the whole bed session itself — flock via spec/lock, the repo-override via spec/proc, the arbiter lease via a direct InvokeProvider(verb,"arbiter") call (the vm_arbiter_shim precedent) — exactly the K5 death this type's own header already predicted. See candy/plugin-check/bed_session.go.
#CheckBedGpuPrereqRequest/#CheckBedGpuPrereqReply is the ONE narrow seam that SURVIVES: GPU host-DETECTION (gpu_allocate.go's bedGPUPrereqMissing, DetectVFIO) is the project's explicitly operator-dropped exception (no hardware to verify against; fenced from every K-wave cutover, including this one — see gpu_shim.go's own header). Threads just the claimant's resource tokens out and the GPU-unsatisfiable verdict back, so the fenced core logic stays completely unchanged.
type CheckBedMember ¶
type CheckBedMember struct {
Key string `yaml:"key,omitempty" json:"key"`
IsVM bool `yaml:"is_vm,omitempty" json:"is_vm,omitempty"`
Image string `yaml:"image,omitempty" json:"image,omitempty"`
From string `yaml:"from,omitempty" json:"from,omitempty"`
}
#CheckBedMember — one sibling member's build coordinates (the group-bed member build loop).
type CheckBedPrereqSkip ¶
type CheckBedPrereqSkip struct {
Token string `yaml:"token,omitempty" json:"token"`
Vendor string `yaml:"vendor,omitempty" json:"vendor"`
Reason string `yaml:"reason,omitempty" json:"reason"`
}
#CheckBedPrereqSkip — a bed the host skips for an absent HOST prerequisite (a GPU resource whose vendor has no matching card): a clean SKIP (exit 3), not a failure.
type CheckBedReply ¶
type CheckBedReply struct {
Calver string `yaml:"calver,omitempty" json:"calver,omitempty"`
LogDir string `yaml:"log_dir,omitempty" json:"log_dir,omitempty"`
PrereqSkip *CheckBedPrereqSkip `yaml:"prereq_skip,omitempty" json:"prereq_skip,omitempty"`
// BedDescriptor — the substrate classification + refs the plugin drives from.
IsVM bool `yaml:"is_vm,omitempty" json:"is_vm,omitempty"`
IsLocal bool `yaml:"is_local,omitempty" json:"is_local,omitempty"`
IsGroup bool `yaml:"is_group,omitempty" json:"is_group,omitempty"`
IsExternal bool `yaml:"is_external,omitempty" json:"is_external,omitempty"`
// node_json is the bed ROOT BundleNode (spec.Deploy) serialized — including its nested
// Members peer map (each member's full BundleNode, with stamped Descent) — so the plugin
// bed runner can call deploykit.PersistBedDeployOverrides PLUGIN-SIDE for the bed root AND
// each member (#55 coneC-dsh β1 — the bed-root + member persist relocate off the host seam;
// the host-side persistBedDeployOverrides wrapper + its deploykit import shed). The plugin
// supplies its own loader-threaded marshalNode + reader (deployMarshalNode/deployConfigReader
// pattern); externalInPlace for the root is IsExternal above, for a member it is derivable from
// the member's stamped Descent (Venue parent/none → in-place). Empty for a VM root (the host
// seam's !isVM guard — a VM bed runs no `charly config`, so no root persist).
NodeJSON RawBody `yaml:"node_json,omitempty" json:"node_json,omitempty"`
Image string `yaml:"image,omitempty" json:"image,omitempty"`
HasAddCandy bool `yaml:"has_add_candy,omitempty" json:"has_add_candy,omitempty"`
// bed) — bed_run.go skips --tag at the config/start steps for such a bed: the FRESH artifact to
// verify is the overlay bundle-add just built + persisted (resolved via plugin-deploy-pod's
// resolveDeployRefLocal resolved_image overlay preference), not the base
// image's own --tag build ref.
VMTemplate string `yaml:"vm_template,omitempty" json:"vm_template,omitempty"`
BedDomain string `yaml:"bed_domain,omitempty" json:"bed_domain,omitempty"`
ImageTag string `yaml:"image_tag,omitempty" json:"image_tag,omitempty"`
LocalRef string `yaml:"local_ref,omitempty" json:"local_ref,omitempty"`
VMDomains []string `yaml:"vm_domains,omitempty" json:"vm_domains,omitempty"`
CheckLiveRefs []string `yaml:"check_live_refs,omitempty" json:"check_live_refs,omitempty"`
ChildKeys []string `yaml:"child_keys,omitempty" json:"child_keys,omitempty"`
// local_child_keys is the HOST-ROOTED (kind:local) subset of child_keys, in the same order. A VM
// root deploys ONLY these host-side (mirroring the core deployNestedLocalChildren): a VM's
// nested CONTAINER children are deployed in-guest by plugin-deploy-vm's PostApply, so a host-side
// re-deploy would be wrong. The pod path uses child_keys (all); the vm path uses local_child_keys.
LocalChildKeys []string `yaml:"local_child_keys,omitempty" json:"local_child_keys,omitempty"`
// members carries each sibling member's build coordinates so a GROUP bed's plugin can drive the
// per-member image build loop (`charly vm build <from>` / `charly box build <image>` + check box)
// BEFORE the host `members-up` op deploys them (bringUpMembers assumes pre-built images).
Members []CheckBedMember `yaml:"members,omitempty" json:"members,omitempty"`
RunBuild bool `yaml:"run_build,omitempty" json:"run_build,omitempty"`
RunRuntime bool `yaml:"run_runtime,omitempty" json:"run_runtime,omitempty"`
RunAgent bool `yaml:"run_agent,omitempty" json:"run_agent,omitempty"`
}
#CheckBedReply is now a plain plugin-internal DESCRIPTOR VALUE TYPE (#55 W3 B2-full), never a wire reply — candy/plugin-check/bed_session.go constructs it directly (no HostBuild round-trip; the type stays CUE-sourced per SDD, since it is still a useful named shape the plugin builds and consumes internally). PrereqSkip set ⇒ the bed is a clean SKIP (exit 3): the plugin writes the prereq-skip summary + returns CheckSkippedError, running NO other setup step (not even teardown — nothing was acquired on the skip path).
type CheckContext ¶ added in v0.2026213.1726
type CheckContext interface {
// Exec runs commands on the venue (in-container under CheckModeLive, in a disposable
// container under CheckModeBox, or host-side depending on the executor).
Exec() CheckExecutor
// Mode is the run mode (Live vs Box).
Mode() CheckRunMode
// HTTPDo issues an HTTP request from the CHARLY HOST's network namespace, applying
// the per-request TLS / redirect / CA policy in req, and returns the status, body, and
// response headers. It REPLACES the former HTTPClient() *http.Client leg: an
// *http.Client cannot cross a process boundary, so out-of-process the REQUEST crosses
// (CheckContextService.HTTPDo) and the host dials; in-process the host builds the client
// and dials directly. The transport-level error is returned as err (a non-2xx is NOT an
// error — the caller matches resp.Status).
HTTPDo(ctx context.Context, req CheckHTTPRequest) (CheckHTTPResponse, error)
// ResolveEndpoint resolves the check target's venue (container / VM / ssh / local) and
// returns a host-reachable "host:port" address for an in-venue TCP port, opening (and
// host-side tracking, for teardown after this verb's Invoke) any ssh -L forward a VM/ssh
// venue needs. An endpoint verb (cdp/vnc/spice/…) declares its in-venue port and dials
// the returned addr — REPLACING the per-verb host preresolvers: the host owns the
// venue/podman/go-libvirt machinery the out-of-process plugin lacks. Empty addr with a
// nil error means "no live venue" (box-mode / no-box) — the verb's own no-endpoint skip
// then fires; a resolution failure is returned as err.
ResolveEndpoint(ctx context.Context, port int) (addr string, err error)
// ResolveGraphicsEndpoint resolves a VM's <graphics type='<kind>'> listener (kind =
// "vnc" | "spice") to a dialable endpoint, opening (and host-side tracking, for teardown
// after this verb's Invoke) any ssh -L forward + socket->TCP bridge the venue needs. A
// graphics verb (vnc/spice) calls it instead of the removed per-verb host preresolver;
// the host owns the go-libvirt resolution, tunnel, bridge, and credential-store password.
// CheckGraphicsEndpoint.Skip=true means the deployment declares no graphics device of that
// kind (an N/A skip). A zero CheckGraphicsEndpoint with a nil error means no live VM context.
ResolveGraphicsEndpoint(ctx context.Context, kind string) (CheckGraphicsEndpoint, error)
// ResolveImageLabel reads one OCI label value off the deployment-under-test's image — the
// host owns the podman engine + container→image resolution the out-of-process plugin cannot
// reach. A verb declares the label it needs (mcp reads ai.opencharly.mcp_provide) and parses
// the returned value; an empty string means the label is absent on the image.
ResolveImageLabel(ctx context.Context, label string) (value string, err error)
// DialTimeout is the per-dial ceiling for host-side TCP reachability probes.
DialTimeout() time.Duration
// Box / Instance are the deployment's image + instance names (empty under CheckModeBox).
Box() string
Instance() string
// Distros is the image's distro tag list (e.g. ["fedora:43","fedora"]) for
// distro-specific package-name resolution.
Distros() []string
// AddBackground registers a host-side background process PID with the active plan run
// so plan teardown reaps it (SIGTERM). A no-op when the engine has no scenario context
// (a bare-Op run) or pid<=0. Used by a verb that fire-and-forgets a host process
// (the `command` verb's background path).
AddBackground(pid int)
}
CheckContext is the live check-engine surface a host-coupled verb's RunVerb consumes. charly's *Runner implements it; a candy reaches the running deployment through it without importing charly's package main.
type CheckDrainEndpointCleanupsRequest ¶ added in v0.2026217.301
type CheckDrainEndpointCleanupsRequest struct {
}
#CheckDrainEndpointCleanupsRequest signals plugin-check to close every forward its #CheckEndpointResolveRequest handler opened since the last drain (LIFO) — the plugin-side twin of the former core-side hostVerbResolver.runEndpointCleanups (#55 W3 B7). Carries no fields: the plugin's pending-cleanup list IS the state, reset+drained per single-verb Invoke — the SAME sequential per-Invoke lifecycle guarantee the former core-side h.endpointCleanups = nil / defer h.runEndpointCleanups() bracket relied on, now owned by the plugin instead.
type CheckEndpointResolveReply ¶ added in v0.2026217.301
type CheckEndpointResolveReply struct {
Addr string `yaml:"addr,omitempty" json:"addr,omitempty"`
}
type CheckEndpointResolveRequest ¶ added in v0.2026217.301
type CheckEndpointResolveRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Mode string `yaml:"mode,omitempty" json:"mode"`
Port int `yaml:"port,omitempty" json:"port"`
}
#CheckEndpointResolveRequest/#CheckEndpointResolveReply — the resolution BODY behind the fixed CheckContext.ResolveEndpoint reverse-RPC every out-of-process live-container verb (cdp/wl/vnc/dbus/mcp) dials back into (#55 W3 B7). The reverse-RPC SERVICE surface itself stays core (charly/check_endpoint_resolve.go's hostVerbResolver wraps the core-private verb-dispatch registry no out-of-process caller can bypass) — only the RESOLUTION WORK relocates, compiled-in-REQUIRED placement class (bed_session.go's precedent, #55 W3 B2-full): the venue-classify leg it calls was ALREADY plugin-native (#CheckVenueResolveRequest above), and the downstream resolution (spec/checkhost's EndpointForVenue) has zero core-private dependency of its own. Any ssh -L forward it opens is tracked in the plugin's OWN per-process pending-cleanup state, never on the wire (a live cleanup closure cannot cross ANY Invoke — compiled-in or not, the wire is JSON bytes only) — see #CheckDrainEndpointCleanupsRequest for the close-it-now signal.
type CheckEnv ¶
type CheckEnv struct {
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
ContainerName string `yaml:"container_name,omitempty" json:"container_name,omitempty"`
Distros []string `yaml:"distros,omitempty" json:"distros,omitempty"`
Venue string `yaml:"venue,omitempty" json:"venue,omitempty"`
VenueKind string `yaml:"venue_kind,omitempty" json:"venue_kind,omitempty"`
DialTimeoutNs int64 `yaml:"dial_timeout_ns,omitempty" json:"dial_timeout_ns,omitempty"`
}
#CheckEnv is the SINGLE-SOURCED scalar snapshot of a check verb's invocation context (K1-unblock W3 Unit B) — the ONE #CheckEnv def now generating the struct all THREE of its consumers share (a hand-written mirror per consumer is the exact "wire type not CUE-generated" violation SDD forbids in fresh code): (1) charly/provider_checkenv.go's host-side CheckEnv, filled by snapshotCheckEnv from a live *kit.Runner and threaded to an out-of-process verb's Invoke envelope; (2) sdk's out-of-process verb-serve decode (sdk/checkverb.go), which reconstructs a kit.CheckContext's scalar legs from this exact snapshot; (3) candy/plugin-check's InvokeProvider-backed VerbResolver (verb_resolver.go), which marshals this same shape when asking the host to dispatch a verb on its behalf. A 4th consumer (charly/plugin_dispatch_reverse.go's InvokeProvider host handler) DECODES it host-side to construct a detached kit.CheckContext for a CheckVerbProvider target — the SAME snapshot, not a second shape.
Every field is optional (a caller fills only what it has — an in-proc/live snapshot has a live Runner to read from; a box-mode run has no ContainerName/Venue; a detached construction may lack DialTimeoutNs). container_name/venue are HOST-COMPUTED-ONLY fields (never authored, carried for the out-of-process appium/vm-target verbs that need charly's naming convention without re-deriving it) — present on the wire regardless of which of the three marshal sites populates them, since all three now share this one shape.
type CheckExecutor ¶ added in v0.2026213.1726
type CheckExecutor interface {
// RunCapture runs a shell command/script on the venue, returning stdout,
// stderr, the exit code, and any execution error (NOT a non-zero exit — that
// is reported via the exit code). No root escalation; callers add sudo.
RunCapture(ctx context.Context, script string) (stdout, stderr string, exit int, err error)
// Kind classifies the venue: "host" | "container" | "image" | "vm".
Kind() string
}
CheckExecutor is the subset of DeployExecutor a check verb needs: run one command/script on the venue and capture stdout/stderr/exit separately. DeployExecutor satisfies this structurally (RunCapture + Kind have identical signatures), so *Runner.Exec is passed straight through.
type CheckGraphicsEndpoint ¶ added in v0.2026213.1726
type CheckGraphicsEndpoint struct {
Addr string
Socket string
Password string
Skip bool
SkipMessage string
}
CheckGraphicsEndpoint is the resolved, dialable VM graphics endpoint a vnc/spice verb gets from CheckContext.ResolveGraphicsEndpoint. Exactly one of Addr / Socket is set (the host bridges a UNIX socket to TCP for a TCP-only client, or forwards a remote listener, before returning). Password is the resolved ticket ("" = no auth). Skip=true (with SkipMessage) means the deployment declares no graphics device of that kind — an N/A skip, not a failure.
type CheckHTTPRequest ¶ added in v0.2026213.1726
type CheckHTTPRequest struct {
Method string
URL string
Body []byte
Headers map[string]string
Timeout string
AllowInsecure bool
NoFollowRedirects bool
CAPEM []byte
}
CheckHTTPRequest is the host-vantage HTTP request a check verb hands cc.HTTPDo. It carries the FULL request plus the per-request policy the host needs to build the client: Timeout is a Go duration string ("" = the engine's base timeout); CAPEM is the resolved CA PEM bytes (a candy reads its authored ca_file host-side and ships the bytes, so the host server needs no filesystem access). Both placements (in-proc + the CheckContextService RPC) consume the SAME struct.
type CheckHTTPResponse ¶ added in v0.2026213.1726
CheckHTTPResponse is the result of cc.HTTPDo: the status code, the response body, and the response headers as a pre-formatted "Key: value\n" blob (the host formats once — R3 — preserving multi-value headers the matcher pipeline consumes directly). A transport-level failure is returned as the HTTPDo error, not here.
func DoHTTPRequest ¶ added in v0.2026213.1726
func DoHTTPRequest(ctx context.Context, base *http.Client, req CheckHTTPRequest) (CheckHTTPResponse, error)
DoHTTPRequest issues req from the HOST's network namespace using a client built from base + req's per-request policy, returning the status, the body, and the formatted response-header blob. The ONE host-side HTTP-do path shared by the in-proc check context AND the CheckContextService reverse channel (R3). A transport-level failure is returned as err; a non-2xx is NOT an error (the caller matches resp.Status).
type CheckImageLabelResolveReply ¶ added in v0.2026217.301
type CheckImageLabelResolveReply struct {
Value string `yaml:"value,omitempty" json:"value,omitempty"`
}
type CheckImageLabelResolveRequest ¶ added in v0.2026217.301
type CheckImageLabelResolveRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Mode string `yaml:"mode,omitempty" json:"mode"`
Label string `yaml:"label,omitempty" json:"label"`
}
#CheckImageLabelResolveRequest/#CheckImageLabelResolveReply — the resolution BODY behind the fixed CheckContext.ResolveImageLabel reverse-RPC (#55 W3 B7), the sibling of #CheckEndpointResolveRequest above (same placement class; no live resource to track — a raw OCI label read is a pure podman-inspect computation).
type CheckLoadPluginsReply ¶
type CheckLoadPluginsReply struct {
}
#CheckLoadPluginsReply is empty on success — connect failures are best-effort WARNINGS on the host (mirroring resolveCheckRunnerContext's existing behavior: an unresolvable plugin fails loudly later, at actual verb dispatch, never here).
type CheckLoadPluginsRequest ¶
type CheckLoadPluginsRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
}
#CheckLoadPluginsRequest asks the host to connect the out-of-process plugin candies a check plan's verb words reference (K1-unblock wave — the "live" check-run arm). Verb dispatch itself crosses the wire generically via InvokeProvider (S1 — command:check's pluginVerbResolver), but InvokeProvider only resolves an ALREADY-CONNECTED provider (or a compiled-in one); connecting an out-of-process candy is the plugin-loading M-mechanism (the kernel/plugin boundary law's clause M — plugin discovery/loading/connect stays core), so this seam is the entry point a plugin calls BEFORE dispatching a plan whose verbs may need an out-of-process candy connected. The host runs the UNCHANGED core engine (LoadConfig + resolveCheckRunnerContext: ScanAllCandyWithConfigOpts + collectReferencedPluginWords + loadProjectPlugins) as a pure SIDE EFFECT on its own providerRegistry — every subsequent InvokeProvider call in this same `charly check run` invocation then resolves. Class-generic action noun "check-load-plugins" (F11 — never a substrate/provider word).
type CheckResult ¶
type CheckResult struct {
Op *Op `yaml:"op,omitempty" json:"op"`
Verb string `yaml:"verb,omitempty" json:"verb"`
Status Status `yaml:"status,omitempty" json:"status"`
Message string `yaml:"message,omitempty" json:"message"`
Elapsed time.Duration `yaml:"elapsed,omitempty" json:"elapsed"`
Attempts int `yaml:"attempts,omitempty" json:"attempts,omitempty"`
TotalElapsed time.Duration `yaml:"total_elapsed,omitempty" json:"total_elapsed,omitempty"`
CapturedValue string `yaml:"captured_value,omitempty" json:"captured_value,omitempty"`
}
CUE schema for the check-engine's per-step VERDICT envelope (FLOOR-SLIM Unit 4). NOT an authoring kind (never in #Node/#Op) — a pure generated wire/render struct, single-sourced here so `task cue:gen` produces the Go struct charly core's registry-coupled floor files (provider.go/provider_verb.go/verb_builtins.go/unified_targets.go/provider_checkenv.go) reference directly (spec.CheckResult), with zero new sdk/kit import.
#CheckResult covers every field EXCEPT the engine-internal `DeadlineExceeded` retry signal — the ONE spike-proven exception (P12, cited at sdk/kit/checkrun_seam.go and sdk/schema/seam.cue): `DeadlineExceeded bool json:"-"` has no gengotypes construct (a field that exists in memory but is excluded from marshaling). sdk/kit.CheckResult embeds this generated type and adds ONLY that one hand-written field back.
FLOOR-SLIM deliberately renames the wire keys to snake_case (Op→op, Verb→verb, Status→status, Message→message, Elapsed→elapsed) — the former hand-written type carried NO json tag on these fields, so encoding/json defaulted to the bare, inconsistent PascalCase Go field name. This is a documented, deliberate breaking wire-format fix for `--format json`/TAP consumers of `charly check box/live/run`, not an accident: it brings CheckResult in line with every other CUE-sourced wire type's snake_case convention. Every field that ALWAYS serialized before (op/verb/status/message/elapsed) stays REQUIRED (`!`) here so gengotypes omits `omitempty` — an omitempty regression would silently drop zero-valued fields from output, a SEPARATE wire change the rename must not introduce. Every field that already carried `,omitempty` (attempts/total_elapsed/captured_value) stays optional (`?`).
Status is carried as a plain int (@go(Status,type=Status) — Status is the check-engine's pass/fail/skip enum, HAND-WRITTEN in spec/status_result.go: gengotypes has no construct for an iota-based enum + String() method, so CUE owns the wire VALUE SET (an int) and Go owns the formatting behavior (String()), mirroring the #SubstrateKind split (status.cue) — there the enum is string-backed and suppressed via @go(-); here it is int-backed and referenced directly since there is no separate disjunction def to suppress.
Elapsed / TotalElapsed carry a nanosecond count wire-typed as time.Duration (@go(Elapsed,type=time.Duration)) — the RDD spike (T-P12, cited in /charly-internals:go) proved a custom-scalar @go(,type=…) override generates faithfully.
type CheckRunMode ¶ added in v0.2026213.1726
type CheckRunMode int
CheckRunMode is the mode a check runs under (charly's RunMode). It never crosses the wire — spec.CheckEnv carries the "box"/"live" string form — so it is a plain Go int.
const ( // CheckModeLive — `charly check live`, against a running container/VM (in-container probes). CheckModeLive CheckRunMode = iota // CheckModeBox — `charly check box`, against a disposable build container. CheckModeBox )
func (CheckRunMode) String ¶ added in v0.2026213.1726
func (m CheckRunMode) String() string
String renders the mode as "box" / "live" (mirrors charly's runModeName).
type CheckRunReply ¶ added in v0.2026213.1726
type CheckRunReply struct {
Steps []StepResult `yaml:"steps,omitempty" json:"steps,omitempty"`
Image string `yaml:"image,omitempty" json:"image,omitempty"`
NoSteps bool `yaml:"no_steps,omitempty" json:"no_steps,omitempty"`
Header string `yaml:"header,omitempty" json:"header,omitempty"`
Passthrough *StepPass `yaml:"passthrough,omitempty" json:"passthrough,omitempty"`
Score *CheckRunResults `yaml:"score,omitempty" json:"score,omitempty"`
}
#CheckRunReply is the host-resolved result of a check-run. Steps is the per-step verdict list the plugin formats (FormatStepResults*) and tallies into an exit code. Image is the resolved image ref for the "Image: <ref>" header line. NoSteps signals the image declared no plan (the plugin prints "No plan steps defined for this image." and exits 0) — distinct from an empty Steps that ran zero scored steps. Header is the pre-formatted, kind-specific banner line the host builds from data only the host holds (container name, ssh user/host/port, member count), so the plugin stays kind-blind: it prints Header, then the formatted Steps. Passthrough carries the one non-plan-run live path — a nested pod-in-VM leaf whose check the host delegates to the guest over SSH, forwarded verbatim; nil for every plan-run mode. Score is the "score"-mode reply (the AI-harness SCORING result, #CheckRunResults), nil for the box/live/feature plan-run modes that carry their verdicts in Steps. CUE-sourced (SDD): plain carrier, no json:"-".
type CheckRunRequest ¶
type CheckRunRequest struct {
Mode string `yaml:"mode,omitempty" json:"mode"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Image string `yaml:"image,omitempty" json:"image,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
Section string `yaml:"section,omitempty" json:"section,omitempty"`
Filter []string `yaml:"filter,omitempty" json:"filter,omitempty"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
Strict bool `yaml:"strict,omitempty" json:"strict,omitempty"`
Agent string `yaml:"agent,omitempty" json:"agent,omitempty"`
Timeout string `yaml:"timeout,omitempty" json:"timeout,omitempty"`
NoAgent bool `yaml:"no_agent,omitempty" json:"no_agent,omitempty"`
Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`
}
#CheckRunRequest asks the host to RUN a check plan against a venue and return the per-step results (P12). command:check (candy/plugin-check) owns the `charly check` CLI + output formatting, but RUNNING a plan is a composite of core host-serving Mechanisms a plugin (a separate module importing only sdk) cannot perform: the venue→executor construction, the OCI-label plan extraction, and the plan-walk's verb dispatch through the provider registry. So the plugin resolves its intent into this envelope and the host builds the venue + runs the kit-Runner through the in-core registry VerbResolver, exactly as command:vm resolves `vm build` plugin-side (candy/plugin-vm/vm_build_resolve.go — the former HostBuild("vm-build") is DELETED). The action noun "check-run" is class-generic (F11).
Mode selects the run shape (discriminated union): "box" — a pure-box run against a disposable container built from Image (RunModeBox, build-scope steps only, the CheckBoxCmd engine); "live" — a full-stack run against a running deployment resolved by Name (the host classifies vm/pod/local/group internally, so the plugin stays kind-blind), applying the Instance/Section/Filter selectors; "feature-box" / "feature-live" — the ADE acceptance run (SkipDeterministicRun) over Image (build scope) or the live deployment Name (deploy scope, the host-side agent grader wiring, gated by NoAgent/Agent/Timeout), scoped by Tag/Strict. Dir is the project dir (empty → the host uses its own cwd), matching LoadUnified(dir). `format` is deliberately NOT a field — the plugin formats the returned Steps itself. run-bed + iterate are NOT seam modes: the plugin drives them over HostBuild("cli").
The REPLY is CUE-sourced: #CheckRunReply / #StepResult / #StepPass (checkresult.cue) — generated to spec.CheckRunReply / spec.StepResult / spec.StepPass, byte-identical JSON to the former hand-written kit types. sdk/kit aliases each (kit.CheckRunReply = spec.CheckRunReply etc., sdk/kit/checkrun_seam.go) so the plugin reuses the kit formatters (FormatStepResults*) with byte-parity across every --format. A live `cue exp gengotypes` spike (P12) proved kit.CheckResult AS A WHOLE is genuinely inexpressible in CUE — its engine-internal `DeadlineExceeded bool json:"-"` field has no gengotypes construct — but confirmed the REST of the type (Op/Verb/Status/Message/Elapsed/Attempts/TotalElapsed/CapturedValue) generates faithfully. FLOOR-SLIM Unit 4 acted on that finding: #CheckResult (checkresult.cue) is the CUE-sourced base (→ spec.CheckResult), and kit.CheckResult is now `struct { spec.CheckResult; DeadlineExceeded bool json:"-" }` — an EMBEDDING wrapper, not a hand-duplicated type. The SDD sweep in THIS cutover extended CUE-sourcing to the reply envelope too: StepResult / CheckRunReply / StepPass are plain carriers (no json:"-"), so gengotypes expresses them faithfully — the `optional=nillable` marker emits the Passthrough / Score pointers. The exception the wire mandate's spike-proven path authorizes is now narrowed to EXACTLY the one kit.CheckResult field that forced it, not the whole type nor the reply envelope.
P12 Wave-2: the "score" mode adds Plan — a substituted, nonce-carrying scoring plan candy/plugin-check's pluginRunCheckLive walks (NOT the OCI-baked plan the "live" mode extracts; walked plugin-side directly since K1-unblock wave arm 3, no host round-trip). Its per-step scoring verdicts ride the kit.CheckRunReply.Score field (a *CheckRunResults, below).
type CheckRunResults ¶
type CheckRunResults struct {
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
Step []StepScore `yaml:"step,omitempty" json:"step,omitempty"`
Summary ScoreSummary `yaml:"summary,omitempty" json:"summary"`
}
#CheckRunResults / #StepScore / #ScoreSummary — the AI-harness SCORING result model (originally P12 Wave-2; the mode's walk moved plugin-side in K1-unblock wave arm 3). pluginRunCheckLive returns a *CheckRunResults (the scored check:/agent-check: verdicts, keyed by step id for plateau tracking); it doubles as the `charly check box --format yaml` payload the harness scorer parses (ParseCharlyTestOutput). These are plain structs — the gengotypes workhorse — CUE-sourced so the "score"-mode reply's Score field and the plugin scorer that produces AND consumes it import ONE definition (SDD; no alias). Every field mirrors the former hand-written Go tag set: required (!) fields carry no json-omitempty (json wire byte-identical for the seam reply); optional (?) fields carry it. The retag pass adds ,omitempty to every YAML tag uniformly — inert here since ID/Status are always set and a zero Summary block only elides on an empty (0-step) result ParseCharlyTestOutput tolerates.
type CheckVenueResolveReply ¶
type CheckVenueResolveReply struct {
Descriptor VenueDescriptor `yaml:"descriptor,omitempty" json:"descriptor"`
Kind string `yaml:"kind,omitempty" json:"kind"`
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
VMName string `yaml:"vm_name,omitempty" json:"vm_name,omitempty"`
Nested bool `yaml:"nested,omitempty" json:"nested,omitempty"`
}
#CheckVenueResolveReply is the wire-safe projection of the plugin-side CheckVenue: the generic #VenueDescriptor (the host re-materializes it into a live DeployExecutor — a live executor never crosses the wire) plus the scalar venue facts the host legs consume (kind for the vm-vs-not graphics branch + the IsContainer var-resolution gate; engine/name for container port mapping + image-label reads; vm_name for the VM graphics leg; nested marks a genuinely multi-hop target the single-hop descriptor degrades to zero for, so the host rebuilds its chain via ResolveDeployChain).
type CheckVenueResolveRequest ¶
type CheckVenueResolveRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
}
#CheckVenueResolveRequest asks plugin-check to CLASSIFY a check target's venue — the kind-decode (checkVmTarget/checkLocalTarget branch on the stamped venue trait) that is CHECK CAPABILITY LOGIC, not kernel fabric (#118 boundary-law self-test: a floor file switching on a kind is a leaked R-item). The host's floor reverse-legs (endpoint / graphics / gRPC exec-attach) call this INSTEAD of an in-core classifier duplicate, then re-materialize the returned generic #VenueDescriptor via kit.VenueFromDescriptor (single-hop) — or, for a nested target the flat descriptor cannot express, rebuild the N-hop chain host-side via the kind-blind deploykit.ResolveDeployChain. plugin-check reaches the merged deploy tree via its OWN InvokeProvider("build","project", OpResolve) peer-dispatch (the former HostBuild("resolved-project") seam is DELETED), so this seam carries only name+instance. Class-generic action noun (F11).
type CheckVerbProvider ¶ added in v0.2026213.1726
type CheckVerbProvider interface {
Reserved() string
RunVerb(ctx context.Context, cc CheckContext, op *Op) CheckVerbResult
}
CheckVerbProvider is the typed in-process contract a host-coupled check-verb candy implements. Reserved() is the verb word; RunVerb runs the probe against the live CheckContext and returns a CheckVerbResult. The authored plugin_input rides op.PluginInput (decode it into the candy's CUE-generated params struct).
type CheckVerbResult ¶ added in v0.2026213.1726
type CheckVerbResult struct {
Status Status
Message string
CapturedValue string // value stashed under `capture:` (recorded only on PASS)
}
CheckVerbResult is a host-coupled verb's verdict. charly converts it to its internal CheckResult (stamping the Op/Verb/timing) at the dispatch boundary.
type CliReply ¶
type CliReply struct {
Stdout string `yaml:"stdout,omitempty" json:"stdout,omitempty"`
ExitCode int `yaml:"exit_code,omitempty" json:"exit_code,omitempty"`
Error string `yaml:"error,omitempty" 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 ¶
type CliRequest struct {
Argv []string `yaml:"argv,omitempty" json:"argv"`
Capture bool `yaml:"capture,omitempty" json:"capture,omitempty"`
Combined bool `yaml:"combined,omitempty" json:"combined,omitempty"`
BestEffort bool `yaml:"best_effort,omitempty" json:"best_effort,omitempty"`
}
#CliRequest is the "cli" host-builder envelope (M4): a lifecycle plugin asks the HOST to run a `charly <argv>` subcommand.
type CloudInitRuntimeParams ¶ added in v0.2026215.1748
type CloudInitRuntimeParams struct {
// SSHPublicKey is the OpenSSH authorized_keys-format public key
// line (e.g. "ssh-ed25519 AAAA..."). Empty when key injection is
// disabled or when VmSSH.KeySource == "none".
SSHPublicKey string
// InstanceID is the stable UUIDv4 cloud-init instance-id.
// Pinned at first VM create and persisted in VmDeployState.
InstanceID string
// Hostname for the guest. Defaults to the VM name when empty.
Hostname string
// InjectKeyViaCloudInit is the resolved D13 key_injection.cloud_init
// channel state. When false the renderer emits no
// ssh_authorized_keys entries even if SSHPublicKey is populated.
InjectKeyViaCloudInit bool
}
CloudInitRuntimeParams carries the runtime-resolved state needed to render cloud-init user-data: the SSH public key to inject, the instance-id (stable UUIDv4 persisted in VmDeployState), the hostname, and whether cloud-init should inject the SSH key at all (computed from D13 auto-defaults + explicit VmKeyInjection overrides).
type CollectedAlias ¶
type CollectedAlias struct {
Name string `yaml:"name,omitempty" json:"name"`
Command string `yaml:"command,omitempty" json:"command"`
}
#CollectedAlias — a command alias baked into ai.opencharly.alias (name+command required).
type Config ¶
type Config struct {
Defaults BoxConfig `yaml:"defaults" json:"defaults"`
Box BoxMap `yaml:"box" json:"box"`
// Local carries kind:local templates so remote-ref collection + validation walk their candy:
// lists symmetrically with box candy lists (kind:local templates compose remote @-ref
// candies too). Populated from UnifiedFile.Local by ProjectConfig() (charly/unified.go).
Local map[string]json.RawMessage `yaml:"local,omitempty" json:"local,omitempty"`
// Sidecar carries the project's sidecar-template library as OPAQUE bodies (the raw
// PluginKinds["sidecar"] map). The kernel never reads their fields — candy/plugin-sidecar's
// OpResolve owns all sidecar business logic.
Sidecar map[string]json.RawMessage `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
// Namespaces carries child namespaces mounted by namespaced `import:` entries (alias →
// projected sub-Config). Qualified refs like `cachyos.cachyos` resolve through here.
// Populated from UnifiedFile.Namespaces by ProjectConfig().
Namespaces map[string]*Config `yaml:"-"`
}
Config represents the charly.yml configuration projection.
func (*Config) AllBoxNames ¶
AllBoxNames returns every image name (enabled or not), sorted — the raw-map view BoxNames filters. Used by the map-killing accessors where the enabled filter is applied separately. Exported: called cross-package from several charly consumers (host_build_feature.go, resolved_project_host.go, refs.go) and in-package by spec's own distroBuilderMap (builder_resolve.go).
func (*Config) BoxConfig ¶
BoxConfig decodes the authored image config for name; ok=false when absent.
func (*Config) EachBox ¶
EachBox iterates every image as (name, decoded BoxConfig) in sorted name order — the decode-on-iterate view consumers walk INSTEAD of the kernel holding a typed map. Exported: used cross-package as a range-over-func iterator by charly's validate.go / validate_project_host.go.
func (*Config) FindBoxByLeaf ¶
FindBoxByLeaf searches for a box whose leaf (unqualified) name equals `leaf` — first in c's own root box map, then recursively in each imported namespace (deterministic alias order). Returns the fully-qualified ref under which the box is reachable from c (bare for a root hit, `ns.<...>` for a namespaced hit), or ok=false when no box with that leaf exists anywhere in the namespace tree.
This is the DISCOVERY dual of ResolveBoxRef: ResolveBoxRef resolves a KNOWN qualified ref by descent, whereas FindBoxByLeaf discovers the qualification for a bare leaf that may live in a namespace. ensure-image's build-fallback needs it because it only has the basename of a full registry ref (e.g. `arch-builder` extracted from `ghcr.io/opencharly/arch-builder:<tag>`) and must find that the box lives under the `charly` namespace to build it locally.
func (*Config) ResolveBoxRef ¶
ResolveBoxRef resolves a (possibly qualified) box name to its BoxConfig and the Config (namespace context) it lives in. Bare names resolve in c; `ns.name` descends into c.Namespaces[ns] recursively. Exported: called cross-package from charly's deploy_ref.go, check_bed_run.go, host_build_box_ref_resolve.go, validate.go, refs.go, and from sdk/buildkit's resolveBase (via the *Config it's given).
func (*Config) WalkBaseChain ¶
func (c *Config) WalkBaseChain(boxName string) []BaseChainNode
WalkBaseChain walks boxName's ROOT-INTERNAL base-image chain and returns the images in walk order (self first, then each internal base). It is the ONE shared base-chain traversal used by every chain-walking collector (CollectHooks / CollectShell / CollectDescription / CollectBoxVolume) — each previously re-implemented the identical `for { img := cfg.Box[current]; ...; current = img.Base }` loop (R3: one implementation, no divergent copies), now cycle-safe for all of them.
It deliberately does NOT descend import namespaces. A namespace-qualified base (e.g. `selkies.selkies-labwc`) is a SEPARATELY-BUILT image that owns its own baked check / hooks / shell / volume labels; re-collecting its candies into the consumer would DOUBLE-COUNT every candy the consumer also lists directly. Namespace-AWARENESS belongs to NAME resolution (ResolveBoxRef / FindBoxByLeaf), not to this per-image candy-collection walk; the distro/build VALUE walkers (WalkBaseChainDistro / WalkBaseChainBuild) cross namespaces precisely because those are inherited values, whereas candy contributions are not.
Exported: called cross-package from sdk/deploykit's BoxCandyChain.
func (*Config) WalkBaseChainBuild ¶
WalkBaseChainBuild walks the base chain through box: entries to find the first ancestor with a build: field set. Returns nil if no ancestor defines build formats or the chain reaches an external base image. Exported: called cross-package from sdk/buildkit's resolveBuild.
func (*Config) WalkBaseChainDistro ¶
WalkBaseChainDistro walks the base chain through box: entries to find the first ancestor with a distro: field set. Returns nil if no ancestor defines distro tags or the chain reaches an external base image. Exported: called cross-package from sdk/buildkit's resolveDistro / effectiveBuilderForBox.
type ConfigResolveReply ¶
type ConfigResolveReply struct {
VmJSON RawBody `yaml:"vm_json,omitempty" json:"vm_json,omitempty"`
ResourcesJSON RawBody `yaml:"resources_json,omitempty" json:"resources_json,omitempty"`
BundleJSON RawBody `yaml:"bundle_json,omitempty" json:"bundle_json,omitempty"`
VmBackend string `yaml:"vm_backend,omitempty" json:"vm_backend,omitempty"`
BuildEngine string `yaml:"build_engine,omitempty" json:"build_engine,omitempty"`
RunEngine string `yaml:"run_engine,omitempty" json:"run_engine,omitempty"`
VmState *VmDeployState `yaml:"vm_state,omitempty" json:"vm_state,omitempty"`
VmEntities []string `yaml:"vm_entities,omitempty" json:"vm_entities,omitempty"`
}
#ConfigResolveReply is the host-resolved config data. For a kind:vm entity: VmJSON is the resolved vm value envelope (uf.VM[entity] via resolveVmViaPlugin, #Vm-defaulted host-side), ResourcesJSON the resolved resource map (uf.resolveResources() — drives GPU auto-allocation) — both opaque JSON of a hand-written runtime type with no CUE def. BundleJSON is the PROJECT deploy tree (uf.Bundle) as opaque JSON — the plugin merges it with the per-host overlay ITSELF via deploykit.MergedDeployTree + spec.FindVMClaimant (placement-invariant, #55 coneC-dsh β2 config-RESOLVE shed: the host no longer calls deploykit; the Claimant computation moved plugin-side, so Claimant/ClaimantNode are no longer wire fields). VmBackend/BuildEngine/RunEngine are the runtime-settings fields (ResolveRuntime) the create/build pipeline reads; VmBackend also feeds the plugin-side backend resolve (candy/plugin-vm/vm_backend_resolve.go, F6 vm-lifecycle move, coneB-vmlifecycle — the resolved Backend value itself no longer crosses the wire; the plugin computes it from VmBackend + its own project self-load, K-wave W3a A3-phase-2). VmState is the entity's persisted deploy-ledger runtime state (instance-id, ssh_port, disk path) — the host reads it spec-only via LoadUnified(perHostConfigDir) (#55 coneC-dsh β2 — no deploykit; consumed by plugin-vm + plugin-kube + plugin-deploy-vm, so it stays a wire field) so the plugin reuses the persisted auto-port + regenerates the seed ISO without holding the deploy-config lock. VmEntities is the project's declared kind:vm entity NAMES (the keys of uf.VM) — the enumeration `charly vm import` needs to detect name conflicts. Fields absent for an entity that does not need them stay zero.
type ConfigResolveRequest ¶
type ConfigResolveRequest struct {
Entity string `yaml:"entity,omitempty" json:"entity"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
}
#ConfigResolveRequest asks the host to resolve the project config for one entity. Entity is the resolved entity name (a kind:vm entity for command:vm; empty resolves the project-wide enumeration in VmEntities). Dir is the project dir (empty → the host uses its own cwd), matching the LoadUnified(dir) contract.
type ConstructStepReply ¶
type ConstructStepReply struct {
Step *InstallStepView `yaml:"step,omitempty" json:"step,omitempty"`
}
type ConstructStepRequest ¶
type ConstructStepRequest struct {
Op Op `yaml:"op,omitempty" json:"op"`
CandyName string `yaml:"candy_name,omitempty" json:"candy_name"`
CandySourceDir string `yaml:"candy_source_dir,omitempty" json:"candy_source_dir,omitempty"`
CandyVars map[string]string `yaml:"candy_vars,omitempty" json:"candy_vars,omitempty"`
ResolvedUser string `yaml:"resolved_user,omitempty" json:"resolved_user,omitempty"`
PkgFormat string `yaml:"pkg_format,omitempty" json:"pkg_format,omitempty"`
DistroTags []string `yaml:"distro_tags,omitempty" json:"distro_tags,omitempty"`
}
#ConstructStepRequest/#ConstructStepReply — the "construct-step" HostBuild seam (K5-A item 1, compile-seam ctx-threading): the ONE genuinely host-only piece of the former compileActOp — resolving a `run:` act op's `plugin:` word against the PROVIDER REGISTRY (a clause-M kernel mechanism, never reachable from a plugin directly) to decide whether it lowers into a typed InstallStep (TypedStepProvider.ConstructStep, an in-proc-only Go method today — no change), an ExternalPluginStep (an out-of-process executorInvoker verb), an ExternalStep (a class:step provider's declared StepContract), or the generic OpStep fallthrough. Everything ELSE compileActOp used to read off `layer`/`img` is ALREADY portable sdk/deploykit-side (CandyModel, *buildkit.ResolvedBox, deploykit.ResolveUserSpec) and never needs to cross this wire — the caller (deploykit.BuildDeployPlan, now ctx/exec-threaded) resolves those PORTABLE pieces itself and sends only the reduced scalars the registry-consult logic actually reads. The reply carries the constructed step as an OPAQUE #InstallStepView (StepToView/StepFromView, R3 — the SAME wire-view round-trip every other step-carrying seam uses) so the caller re-materializes a real deploykit.InstallStep without a second per-kind decode path. No new #Op selector is needed: the registry-consult decision is HostBuild-KIND-dispatched (string, like every other seam in this file), not a provider-targeted Invoke — compileActOp's logic runs UNCHANGED host-side inside the seam handler.
type CredentialHealth ¶
type CredentialHealth struct {
BackendName string `yaml:"backend_name,omitempty" json:"backend_name"`
ConfiguredBackend string `yaml:"configured_backend,omitempty" json:"configured_backend"`
KeyringAvailable bool `yaml:"keyring_available,omitempty" json:"keyring_available"`
KeyringLocked bool `yaml:"keyring_locked,omitempty" json:"keyring_locked"`
PlaintextCount int `yaml:"plaintext_count,omitempty" json:"plaintext_count"`
NoSession bool `yaml:"no_session,omitempty" json:"no_session"`
CollErr string `yaml:"coll_err,omitempty" json:"coll_err,omitempty"`
HealthyColls []string `yaml:"healthy_colls,omitempty" json:"healthy_colls,omitempty"`
BrokenColls []string `yaml:"broken_colls,omitempty" json:"broken_colls,omitempty"`
IndexTotal int `yaml:"index_total,omitempty" json:"index_total"`
IndexMissing []string `yaml:"index_missing,omitempty" json:"index_missing,omitempty"`
}
#CredentialHealth is the credential-store health snapshot. Rendered into the doctor "secret storage" checks by the plugin.
type CredentialMount ¶
type CueSchema ¶ added in v0.2026217.301
type CueSchema struct {
Ctx *cue.Context // the context every ingest/build/Unify call must use.
Root cue.Value // sharedCueSchema — every schema/*.cue file unified into one value.
// KindDef resolves a kind name to its compiled entity definition within Root (mirrors
// DeployTraits' registry-derived-DATA threading pattern above).
KindDef func(kind string) (cue.Value, bool)
}
CueSchema is the process-wide compiled CUE schema HANDLE the relocated CUE-validate mechanism (K1 unit 2) consults instead of compiling its own — cue.Value instances only interoperate within the cue.Context that built them, so a call that Unifies against Root MUST ingest/build using Ctx, the SAME context Root was compiled with (unlike DecodeEntityViaCUE's self-contained shorthand-decode, which never Unifies against the shared base schema and so safely owns an independent context). The host builds one value from its still-core D-data (the process-wide cueSchemaCtx / sharedCueSchema / cueKindDef — cue_schema.go, unchanged by K1) and passes it to every CUE-validate seam call below; loaderkit never constructs its own.
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 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"`
// descent is the loader-DERIVED venue-hop descriptor (the descent de-type,
// Cutover H): the substrate plugin stamps it at OpLoad (via kit.StampDescent) so
// the deploy chain (AppendHopForFlatPath) descends generically BY TRANSPORT,
// never by switching on the substrate kind word. charly-written state, never
// authored (rejected by #BundleValue).
Descent *DescentDescriptor `yaml:"descent,omitempty" json:"descent,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"`
// volume_project_checked marks that this deploy's PROJECT-declared `volume:` override has
// already been consulted ONCE (true regardless of whether the project declared one), so
// resolveDeployVolumes (candy/plugin-deploy-pod) never re-queries the project on a
// subsequent `charly config`/`charly update` merely because the per-host overlay's Volume
// field happens to be empty (that emptiness is ALSO produced by an unrelated overlay write —
// e.g. the port-resolution block — that never touched volumes at all, so key-existence alone
// cannot distinguish "checked, found nothing" from "not checked yet"). charly-written state
// (like resolved_port/resolved_image), never authored.
VolumeProjectChecked bool `yaml:"volume_project_checked,omitempty" json:"volume_project_checked,omitempty"`
// The Go type is OPAQUE (map[string]json.RawMessage): CUE still validates each
// per-deploy sidecar override against #Sidecar, but the kernel stores it as
// opaque bodies — ALL sidecar business logic lives in candy/plugin-sidecar's
// OpResolve (the sidecar de-type, Cutover D). The host never reads Sidecar fields.
Sidecar map[string]RawBody `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"`
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"`
// 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 ¶
HasChildren reports whether this node has any nested deployments.
func (Deploy) IsDisposable ¶
IsDisposable reports whether the deploy is safe to destroy + rebuild.
func (Deploy) IsEphemeral ¶
IsEphemeral reports whether this deploy is marked ephemeral.
func (Deploy) IsGroup ¶
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 ¶
IsPreemptible reports whether this deploy holds a preemptible exclusive resource (graceful-stop side).
func (Deploy) LifecycleTag ¶
LifecycleTag returns the deploy's lifecycle tag.
func (Deploy) PreemptionHolds ¶
PreemptionHolds returns the exclusive-resource token(s) this deploy occupies.
func (Deploy) RequiredExclusive ¶
RequiredExclusive returns the exclusive-resource token(s) this deploy claims.
func (Deploy) RequiredShared ¶
RequiredShared returns the shared-mode resource token(s) this deploy claims.
type DeployCompileReply ¶
type DeployCompileReply struct {
PlansJSON RawBody `yaml:"plans,omitempty" json:"plans"`
Base string `yaml:"base,omitempty" json:"base,omitempty"`
CandySet []string `yaml:"candy_set,omitempty" json:"candy_set,omitempty"`
}
#DeployCompileReply is the OpCompile reply: the compiled plans as marshalled []spec.InstallPlanView (a hand-written sdk/spec wire type with no CUE def → opaque RawBody envelope; the host unmarshals into []spec.InstallPlanView and re-materializes []*spec.InstallPlan via deploykit.PlanFromView), plus the base identity (box name) and the resolved candy set (the order, for deployID + overlay-candy propagation).
type DeployCompileRequest ¶
type DeployCompileRequest struct {
Dir string `yaml:"dir,omitempty" json:"dir"`
BoxView ResolvedBoxView `yaml:"box_view,omitempty" json:"box_view,omitempty"`
Order []string `yaml:"order,omitempty" json:"order,omitempty"`
HostContextJSON RawBody `yaml:"host_context,omitempty" json:"host_context"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
// The add_candy:/--add-candy ref(s) (if any) this compile call's own candy set was widened
// with host-side (scanCandiesForRef's synthetic-augmented scan, for a REMOTE ref) — threaded
// into the plugin's OWN InvokeProvider("build","project") re-fetch (as its extra_candy_refs) so
// the envelope's candy map ALSO carries them (RCA'd K1-alpha regression: the two scans were
// independent, so a remote add-candy resolved host-side never reached the envelope).
ExtraCandyRefs []string `yaml:"extra_candy_refs,omitempty" json:"extra_candy_refs,omitempty"`
// candy_ref selects the CANDY shape above (K4 unit B): the authored ref string (bare local
// name OR a `@github…` remote ref) the plugin resolves via BareRef against its own
// rp.CandyModels/rp.Candies (widened, when remote, by extra_candy_refs carrying the SAME raw
// ref — mirroring compileStandaloneCandySelection's ExtraCandyRefs: []string{ref.Raw}
// widening). Absent for the other shapes.
CandyRef string `yaml:"candy_ref,omitempty" json:"candy_ref,omitempty"`
// vm_entity selects the CANDY shape's synthetic-box kind, TOLERANTLY (mirrors the OLD host
// syntheticVmBox call site exactly — never a hard requirement): the plugin tries vm_entity
// against its own rp.Templates.VM; a HIT resolves it via the kind:vm provider's OpResolve leg
// for a guest-tuned box; a MISS (including vm_entity=="") falls through to a plain host-adhoc
// box via vmshared.DetectHostDistro. A non-vm deploy's node.From (e.g. a `local:` node's
// kind:local template ref) is ALSO threaded into vm_entity upstream (resolveVmEntity returns
// node.From unconditionally, not only for a real vm cross-ref) — so a miss here is the
// COMMON case, not an error condition. Ignored for the other shapes.
VmEntity string `yaml:"vm_entity,omitempty" json:"vm_entity,omitempty"`
// box_ref selects the BOX-REF shape above (K4 unit B box-half): the box's own name (never a
// remote ref — compileRefSelection already rejects a remote image ref before this request is
// built). The plugin's own InvokeProvider("build","project") re-fetch is asked to include_disabled
// so an explicitly-named `enabled: false` box still resolves (mirrors the OLD host
// ResolveBox(cfg, ref.Name, …) call, which never checked IsEnabled at all — enabled-filtering
// is a ResolveAllBox/listing concern, not a by-name-resolve one; zero disabled boxes exist
// repo-wide today, so this is a zero-cost future-proofing widening, not a live behavior
// change). Absent for the other shapes.
BoxRef string `yaml:"box_ref,omitempty" json:"box_ref,omitempty"`
// base_box_ref selects the ADD-CANDY-ON-BOX shape (K4 box-half completion) WHEN set alongside
// candy_ref: the primary pod/k8s base image's own name, read from rp.Boxes[base_box_ref] as the
// COMPILE CONTEXT the candy_ref overlay compiles against — replacing the former host-side
// buildkit.ResolveBox(baseImg) + scanCandiesForRef. Absent for every other shape (a standalone
// candy_ref with NO base_box_ref stays the CANDY shape, synthetic-box compiled).
BaseBoxRef string `yaml:"base_box_ref,omitempty" json:"base_box_ref,omitempty"`
}
#DeployCompileRequest is the per-node COMPILE seam (K4-B / K4 unit B): the host asks the command:bundle plugin's OpCompile handler to compile, in one of THREE selection SHAPES (a discriminated set, not three Ops — R3). The plugin fetches the resolved-project envelope itself via InvokeProvider("build","project", OpResolve) peer-dispatch (the former HostBuild("resolved-project") seam is DELETED — it does NOT receive the whole project in the request), loops deploykit.BuildDeployPlan over the resolved order, and returns []InstallPlanView. The host re-materializes []*InstallPlan from the views via deploykit.PlanFromView.
- BOX-VIEW selection (an already-resolved base image, ctx!=nil): box_view + order are POPULATED host-side (the host projects an ALREADY-RESOLVED base image via projectResolvedBox) and the plugin trusts them as sent. Retained for any caller that still resolves the base image host-side; the add_candy-on-pod/k8s path itself now uses the ADD-CANDY-ON-BOX shape below.
- ADD-CANDY-ON-BOX selection (compileCandyOnBoxSelection, the add_candy-on-pod/k8s shape, K4 box-half completion): candy_ref (the add_candy overlay ref) AND base_box_ref (the primary pod/k8s base image name) are BOTH set — the plugin reads rp.Boxes[base_box_ref] (the SAME ResolvedBoxView the primary BOX-REF shape reads, R3) as the COMPILE CONTEXT, resolves the add_candy's OWN topo order from rp.CandyModels (deploykit.ResolveCandyOrder over {BareRef(candy_ref)}, widened by extra_candy_refs for a remote overlay), prunes container-init-for-systemd, and compiles that order against the base image — replacing the former host-side buildkit.ResolveBox(baseImg) + scanCandiesForRef path.
- CANDY selection (compileStandaloneCandySelection, the target:local/vm standalone-candy shape, K4 unit B candy-half): candy_ref is set — the plugin resolves the candy key + topo order itself from its own envelope (deploykit.ResolveCandyOrder over rp.CandyModels, already pure) and builds ITS OWN synthetic box (vmshared.DetectHostDistro/DetectHostGlibc for a host target — already sdk-portable; the kind:vm provider's own OpResolve leg for a vm target, mirroring the kind:local OpResolve reuse in node_resolve.go's lookupLocalTemplate, K4 unit A).
- BOX-REF selection (compileBoxSelection, the primary pod/k8s image shape, K4 unit B box-half): box_ref is set — the plugin reads rp.Boxes[box_ref] (the SAME ResolvedBoxView hostBuildResolvedProject already computed to BUILD the envelope in the first place — no re-derivation, R3) directly via deploykit.NewSpecResolvedBox, and resolves the candy topo order itself from img.Candy over rp.CandyModels (deploykit.ResolveCandyOrder, same as the CANDY shape).
Exactly one of {box_view, box_ref, candy_ref-alone} is set, OR the ADD-CANDY-ON-BOX pair (candy_ref + base_box_ref) together. Neither CANDY nor BOX-REF needs LoadUnified or the provider-CONNECT registry (verified live, K4 unit B: candy/plugin-bundle's own ALREADY-EXISTING preresolveBuilderContexts, called unconditionally for every OpCompile, already S2-lazy-connects any externalized builder the resolved order+img trigger via exec.InvokeProvider — an exhaustive repo grep found zero target:local/vm or pod/k8s deploy anywhere needing a builder plugin outside the calling project's own candy closure, the one edge S2's Pass-1 project-scan can't cover), so neither needs a new HostBuild kind.
HostContextJSON is the marshalled spec.HostContext — the 4 host-computed WIRE scalars (MachineVenue/Distro/GlibcVersion/BuilderImage) — a hand-written spec type with no CUE def (the gengotypes spike cannot express its json:"-" in-process fields), so it rides as an opaque RawBody envelope (the VmJSON/PodConfigJSON idiom; the plugin unmarshals into spec.HostContext, which sdk/deploykit re-exports as deploykit.HostContext). ONLY the 4 scalars cross: the plugin populates the json:"-" BuilderContext (preresolveBuilderContexts over the reverse channel) + ActiveInit (off the resolved-project envelope's rp.Init) IN-PROCESS after the decode — never on the wire. Tag is the image CalVer pin (for the plan Version field when set). Dir is the project dir the plugin threads into its InvokeProvider("build","project") call (empty → plugin cwd).
type DeployDelResolveReply ¶
type DeployDelResolveRequest ¶
type DeployDelResolveRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
// tree_json is the merged project+operator deploy tree the command:bundle plugin already
// resolved PLUGIN-SIDE (resolveTreeViaLoader, which also connects the deployment's plugins) —
// threaded as DATA so the host resolveDelNode consumes it instead of re-loading the tree
// host-side (#55 Cone A Unit 3a). Marshalled map[string]spec.Deploy; an absent/empty tree
// falls through to resolveDelNode's non-tree fallbacks (vm-prefix / pod-artifact probe), exactly
// as a nil host-tree-read result did before.
TreeJSON RawBody `yaml:"tree_json,omitempty" json:"tree_json,omitempty"`
}
#DeployDelResolveRequest/#DeployDelResolveReply — resolve a `charly bundle del` target's BundleNode (resolveDelNode: literal "host" / "vm:"-prefix legacy forms / a charly.yml tree entry / a ref-based pod-artifact probe) — needs LoadUnified + the on-disk artifact probe, so it stays host-side; the plugin's `charly bundle del` calls this FIRST.
type DeployExecutor ¶
type DeployExecutor interface {
// Venue returns a stable identifier for where this executor's
// commands physically run. Examples:
//
// "local" — ShellExecutor.
// "ssh://arch@127.0.0.1:2224" — SSHExecutor.
// "nested:podman exec stack/local" — NestedExecutor over local.
// "nested:ssh vm/local" — NestedExecutor over SSH.
//
// The string is used as a map key for per-venue ledgers, so it
// must be stable across invocations for the same logical target.
// Not a URL — don't parse it; just compare.
Venue() string
// RunSystem executes a bash script with root privileges. On the
// host, this is `sudo bash -s <<<script`; on the VM target, it's
// `ssh <user>@<host> sudo bash -s <<<script`. The script body runs
// with set -e semantics at the caller's discretion.
RunSystem(ctx context.Context, script string, opts EmitOpts) error
// RunUser executes a bash script as the invoking user (no sudo).
// On the host, it's `bash -s <<<script`; on VM, `ssh <user>@<host>
// bash -s <<<script` where <user> is the unprivileged guest user.
RunUser(ctx context.Context, script string, opts EmitOpts) error
// RunBuilder invokes the multi-stage builder image (podman run
// <builder>) to compile pixi/npm/cargo/aur artifacts. On the host
// this calls the existing BuilderRun helper. On VM deploys, the
// builder runs *on the host* and artifacts are scp'd into the
// guest via PutFile — podman inside the guest is not required.
RunBuilder(ctx context.Context, opts BuilderRunOpts) ([]byte, error)
// PutFile places a file at a remote path. ownerRoot == true means
// the file is chown'd to root:root and chmod'd according to mode.
// On the host, this is a plain os.WriteFile (plus sudo chown when
// ownerRoot). On VM, this is scp into a tmp location followed by
// `sudo install -m <mode> -o root -g root` on the guest.
PutFile(ctx context.Context, localPath, remotePath string, mode uint32, ownerRoot bool, opts EmitOpts) error
// GetFile retrieves the contents of a file on the venue. asRoot==true
// runs the read via sudo to handle paths the deploying user cannot
// access (e.g. /etc/rancher/k3s/k3s.yaml on a k3s server). On the
// host, this is os.ReadFile (or `sudo cat` when asRoot). On VM, this
// is `ssh <host> sudo cat <path>` with stdout captured. On nested
// executors, delegates through the jump via the parent's own RunSystem
// semantics. Used by layer_artifacts.go to publish files back to the
// operator after deploy completion.
GetFile(ctx context.Context, remotePath string, asRoot bool, opts EmitOpts) ([]byte, error)
// RunCapture executes a single shell command (or short bash script) on
// the venue and returns stdout/stderr/exit/err separately. Used by the
// declarative test runner (testrun.go) to probe target state without
// the streamed-output ergonomics of RunSystem/RunUser. No root
// escalation — callers add `sudo` explicitly when needed; mirrors the
// previous test-time Executor.Exec semantics. After the executor-
// hierarchy cutover (2026-04), this is the single capture-output
// method used by every probe across `charly check live`, `charly check box`, and
// `charly check` scoring.
RunCapture(ctx context.Context, script string) (stdout, stderr string, exit int, err error)
// RunInteractive runs a command on the venue wired to the operator's LIVE TTY:
// stdin/stdout/stderr are INHERITED from the host charly process (the reverse-channel
// server runs IN that process, so os.Std* IS the operator's terminal). The child
// (`podman exec -it` / `ssh -t`) owns the PTY, resize (SIGWINCH), and Ctrl-C. Blocks
// until the session ends; returns its exit code. NOT ctx-deadlined — the TTY owns its
// lifetime (the hostBuildCli doctrine). Stdio NEVER crosses the plugin wire — only the
// script + the exit code (F12; the live-stdio sibling of RunCapture). Consumers: `charly
// shell` (-it) and `charly cmd` (-i, stdin piped). A venue that cannot host an interactive
// session returns ErrNotSupported.
RunInteractive(ctx context.Context, script string) (exit int, err error)
// RunStream runs a command on the venue streaming stdout/stderr LIVE to the operator
// (inherited os.Stdout/os.Stderr; NO stdin). Blocks until exit. Same host-holds-the-
// terminal semantics as RunInteractive (F12). Consumer: `charly logs --follow`. Returns
// ErrNotSupported on a venue that cannot stream.
RunStream(ctx context.Context, script string) (exit int, err error)
// Kind returns a coarse classification of the venue used by the test
// runner for reporting and skip decisions. Values:
// "host" — ShellExecutor (operator's machine)
// "container" — NestedExecutor with JumpPodmanExec / JumpDockerExec
// "vm" — SSHExecutor or NestedExecutor with JumpSSH/JumpVirshConsole
// Replaces the test-time Executor.Kind() method deleted in the
// 2026-04 executor-hierarchy cutover.
Kind() string
// ResolveHome returns the absolute path of $HOME for the named user
// on the venue. Empty user means "the executor's default user" (the
// invoking operator for ShellExecutor; the SSH login user for
// SSHExecutor). Implementations consult `getent passwd` so they
// don't depend on $HOME being set in the calling environment — that
// matters for SSH executors where the operator's $HOME has nothing
// to do with the remote user's home, and for ShellExecutor when the
// caller wants a different user's home (e.g. running as root but
// resolving an unprivileged user's home).
//
// Bundled as part of the 2026-05 shell:-schema cutover. Replaces the
// `the local deploy target.HostHome = os.Getenv("HOME")` static-field
// initialization that mis-targeted SSH deploys: the operator's
// $HOME is not the remote user's home, so every shell-rc edit
// (env.d sourcing block included) was landing in the wrong place
// for `host: user@machine` deploys.
ResolveHome(ctx context.Context, user string) (string, error)
}
DeployExecutor is the in-process host-side executor: the concrete venue an InstallPlan's steps are applied against (ShellExecutor on the operator's machine, SSHExecutor over ssh, NestedExecutor through a jump). The three implementations live in sdk/kit; the InstallPlan IR (EmitOpts.ParentExec) carries one through the nested-deploy tree. DISTINCT from kit.DeployExecutor (the reverse-channel executor a plugin serves over gRPC) — same role, different wire shape (typed EmitOpts + file paths here; JSON-marshalled opts + byte content there).
type DeployExpose ¶
type DeployFromBoxReply ¶
type DeployFromBoxReply struct {
}
#DeployFromBoxReply is the "deploy-from-box" host-builder reply — empty (prints host-side, errors via the return).
type DeployFromBoxRequest ¶
type DeployFromBoxRequest struct {
Ref string `yaml:"ref,omitempty" json:"ref"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Env []string `yaml:"env,omitempty" json:"env,omitempty"`
Port []string `yaml:"port,omitempty" json:"port,omitempty"`
Cluster string `yaml:"cluster,omitempty" json:"cluster,omitempty"`
Namespace string `yaml:"namespace,omitempty" json:"namespace,omitempty"`
}
#DeployFromBoxRequest carries the `charly bundle from-box` command flags (the former BundleFromBoxCmd) — a SOURCE-LESS deploy driven entirely by an image's baked OCI labels. The plugin forwards these to HostBuild("deploy-from-box"); the host runs the existing from-box orchestration VERBATIM (the project-free runConfig core via BoxConfigSetupCmd, or the K8s Kustomize path with --cluster).
type DeployNodeDelDispatchReply ¶
type DeployNodeDelDispatchReply struct {
}
type DeployNodeDelDispatchRequest ¶
type DeployNodeDelDispatchRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
Node *Deploy `yaml:"node,omitempty" json:"node,omitempty"`
AssumeYes bool `yaml:"assume_yes,omitempty" json:"assume_yes,omitempty"`
KeepRepoChanges bool `yaml:"keep_repo_changes,omitempty" json:"keep_repo_changes,omitempty"`
KeepServices bool `yaml:"keep_services,omitempty" json:"keep_services,omitempty"`
KeepImage bool `yaml:"keep_image,omitempty" json:"keep_image,omitempty"`
DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,omitempty"`
}
#DeployNodeDelDispatchRequest/#DeployNodeDelDispatchReply — the `charly bundle del` terminal step: ResolveTarget + target.Del, honoring the teardown gates (the live ReverseRunner is still never carried on the wire — a programmatic teardown needing a specific runner is resolved host-side during dispatch).
type DeployOverlayReply ¶
type DeployOverlayReply struct {
ConfigJSON RawBody `yaml:"config_json,omitempty" json:"config_json,omitempty"`
}
#DeployOverlayReply carries the marshaled per-host BundleConfig. config_json is the JSON encoding of *deploykit.BundleConfig (nil-safe: absent/null when no per-host overlay file exists yet, matching LoadDeployConfigForRead's own nil-BundleConfig contract).
type DeployOverlayRequest ¶
type DeployOverlayRequest struct {
Context string `yaml:"context,omitempty" json:"context"`
}
#DeployOverlayRequest asks the host for the PER-HOST deploy-config overlay (K4: deploykit.LoadDeployConfigForRead — the runtime ledger at ~/.config/charly/charly.yml, NOT the project charly.yml the resolved-project envelope projects; Mode Purity keeps the two apart, same distinction #PodDisposableRequest documents). Unlike #PodDisposableRequest (a single overlay BIT), several pod-lifecycle resolvers need CROSS-DEPLOYMENT visibility (deploykit.BundleConfig's GlobalEnvForImage/OccupiedHostPorts/DeployedContainerNames all read OTHER deploys' entries, not just the caller's own), so a single-field extraction can't serve them — the host returns the WHOLE marshaled *deploykit.BundleConfig and the plugin calls the SAME already-portable deploykit methods locally. Re-fetched on EVERY call (no caching): the ledger can change between invocations (an intervening `charly config`), and PrepareVenue-time data is stale by the time OpStart/OpStop/OpShell run much later — this is NOT threaded through the one-shot LifecyclePrepareInput. Class-generic action noun "deploy-overlay" (F11 — never a substrate word).
type DeployPluginsConnectReply ¶
type DeployPluginsConnectReply struct {
Dir string `yaml:"dir,omitempty" json:"dir"`
}
type DeployPluginsConnectRequest ¶
type DeployPluginsConnectRequest struct {
Path string `yaml:"path,omitempty" json:"path"`
AddCandy []string `yaml:"add_candy,omitempty" json:"add_candy,omitempty"`
}
#DeployPluginsConnectRequest/#DeployPluginsConnectReply — the K1-LOADER RELOCATION witness (Unit D). candy/plugin-bundle now DRIVES loaderkit.LoadUnified ITSELF, plugin-side, over the reverse-channel LoaderExecutor (execLoaderExecutor → the "loader-*" host legs), to resolve the `charly bundle add` deploy tree — the host no longer runs a host-side merged-tree read for the walk. This seam is the ONE host-only PREAMBLE the plugin still needs: connect the deployment's out-of-tree plugin candies (loadDeployPlugins — registry-coupled, a core Mechanism) BEFORE ResolveTarget can route to an external substrate, and return the resolved project dir (host os.Getwd — the SAME dir the host loader uses) the plugin passes to loaderkit.LoadUnified. The plugin reads root-venue-ssh itself from the tree's stamped node.Descent (loaderkit.LoadUnified stamps it), so no host trait call — proving plugin-bundle → loaderkit.LoadUnified end-to-end.
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 DeployRecord ¶ added in v0.2026215.1748
type DeployRecord struct {
// SchemaVersion is the ledger-format version (the ledger-candy-keys
// cutover's CalVer). Empty means a pre-cutover record (json:"layer"
// keys) — the read path rejects it with a `charly migrate` hint.
SchemaVersion string `json:"schema_version,omitempty"`
DeployID string `json:"deploy_id"`
Image string `json:"image"`
Tag string `json:"tag,omitempty"`
Target string `json:"target"` // the deploy-record key, e.g. "vm:<name>" (only VM/local deploys write a DeployRecord)
Candy []string `json:"candy,omitempty"`
AddCandy []string `json:"add_candy,omitempty"`
DeployedAt string `json:"deployed_at"`
}
DeployRecord is the top-level entry in deploys/<deploy-id>.json. Lists the image, tag, and the ordered candy set included in this deploy (image candies + add_candy overlays, already topo-sorted).
type DeployReply ¶
type DeployReply struct {
ReverseOps []ReverseOp `yaml:"reverse_ops,omitempty" json:"reverse_ops,omitempty"`
Record DeployReplyRecord `yaml:"record,omitempty" 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.
type DeployReplyRecord ¶
type DeployReplyRecord struct {
Candy string `yaml:"candy,omitempty" json:"candy"`
Version string `yaml:"version,omitempty" 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 DeployResolveTargetAddReply ¶
type DeployResolveTargetAddReply struct {
}
type DeployResolveTargetAddRequest ¶
type DeployResolveTargetAddRequest struct {
Path string `yaml:"path,omitempty" json:"path"`
DeployName string `yaml:"deploy_name,omitempty" json:"deploy_name"`
Node *Deploy `yaml:"node,omitempty" json:"node,omitempty"`
Target string `yaml:"target,omitempty" json:"target"`
Dir string `yaml:"dir,omitempty" json:"dir"`
// plans_json is the marshalled []spec.InstallPlanView (deployID + AddCandies already stamped
// plugin-side); the host re-materializes []*spec.InstallPlan via deploykit.PlanFromView.
PlansJSON RawBody `yaml:"plans_json,omitempty" json:"plans_json"`
AncestorPaths []string `yaml:"ancestor_paths,omitempty" json:"ancestor_paths,omitempty"`
AncestorNodes []Deploy `yaml:"ancestor_nodes,omitempty" json:"ancestor_nodes,omitempty"`
NodeOnly bool `yaml:"node_only,omitempty" json:"node_only,omitempty"`
Pull bool `yaml:"pull,omitempty" json:"pull,omitempty"`
Verify bool `yaml:"verify,omitempty" json:"verify,omitempty"`
WithServices bool `yaml:"with_services,omitempty" json:"with_services,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"`
AssumeYes bool `yaml:"assume_yes,omitempty" json:"assume_yes,omitempty"`
BuilderImage string `yaml:"builder_image,omitempty" json:"builder_image,omitempty"`
// dev_local_pkg marks a DISPOSABLE CHECK BED's deploy, the deploy-side twin of `charly box
// build --dev-local-pkg`. On a bed, a localpkg candy whose package source cannot be found is
// a HARD FAILURE rather than the benign skip a normal deploy takes — a bed exists to prove
// the in-development package builds and installs, so silently installing nothing (or an
// older release) makes the bed assert something it never tested.
DevLocalPkg bool `yaml:"dev_local_pkg,omitempty" json:"dev_local_pkg,omitempty"`
}
#DeployResolveTargetAddRequest/#DeployResolveTargetAddReply — the K4-C SHAPE-2 per-node terminal step: ResolveTarget + DeployContext + target.Add for ONE tree position, reached once per node from the plugin's own walk. UNLIKE the retired #DeployNodeDispatchRequest (which had the host RE-COMPILE the plans via an in-proc OpCompile round-trip — the plugin→host→plugin double-bounce), the plugin now COMPILES the InstallPlans IN-PROC (walk.go dispatchOne → compileNodePlans → compilePlansForRequest, no OpCompile hop) and ships the ALREADY-COMPILED plans (with deployID + AddCandies stamped plugin-side, round-tripped through InstallPlanView) as plans_json. The host half does ONLY the genuine floor-M residue a plugin cannot: reconstruct the ancestor executor chain (deriveChildExecutorForPath — registry-coupled), loadConfigForDeploy (LoadUnified), ResolveTarget + DeployContext + utgt.Add.
ancestor_paths/ancestor_nodes let the host reconstruct the SAME parentExec chain the OLD in-core walk built (deriveChildExecutorForPath is pure Go over spec/kit types, re-run HOST-side) — a live DeployExecutor never crosses the wire. target is the plugin-classified substrate word (a pure ClassifyNodeTarget of node+path), carried so the host synthesizes a Target-only node when node is nil (a ref-based deploy with no charly.yml entry). The gate flags are the FINAL resolved EmitOpts values (node.InstallOpts already applied over the CLI flags plugin-side); dry_run never reaches this seam — a dry-run prints the compiled plans plugin-side and returns without dispatch.
type DeployResources ¶
type DeploySecret ¶
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 DeployTarget ¶
type DeployTarget interface {
Name() string
Emit(plans []*InstallPlan, opts EmitOpts) error
}
DeployTarget is the interface OCI + container-deploy + host-deploy emitters satisfy. Taking a slice of plans (rather than a single plan) lets whole-image deploys pass all per-candy plans at once and let the target merge them — useful because OCITarget may want to emit a single Containerfile for the image while the local deploy target may batch steps across candies.
type DeployTargetDelOpts ¶
type DeployTargetDelOpts struct {
DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,omitempty"`
AssumeYes bool `yaml:"assume_yes,omitempty" json:"assume_yes,omitempty"`
KeepLedger bool `yaml:"keep_ledger,omitempty" json:"keep_ledger,omitempty"`
RemoveVolumes bool `yaml:"remove_volumes,omitempty" json:"remove_volumes,omitempty"`
KeepRepoChanges bool `yaml:"keep_repo_changes,omitempty" json:"keep_repo_changes,omitempty"`
KeepServices bool `yaml:"keep_services,omitempty" json:"keep_services,omitempty"`
KeepImage bool `yaml:"keep_image,omitempty" json:"keep_image,omitempty"`
}
#DeployTargetDelOpts mirrors the former charly-core DelOpts (`charly bundle del`) PLUS the three teardown gates externalDeployTarget used to receive as externally-set STRUCT FIELDS (KeepRepoChanges/KeepServices/KeepImage, set via a type-assertion in host_build_deploy_node_del_dispatch.go before calling .Del()) — folded into DelOpts proper (S3b) since the moved target is now constructed fresh per call from data alone, with no settable-after-construction fields to type-assert onto.
type DeployTargetDispatchReply ¶
type DeployTargetDispatchReply struct {
Status DeployTargetStatus `yaml:"status,omitempty" json:"status,omitempty"`
Output string `yaml:"output,omitempty" json:"output,omitempty"`
ExitCode int64 `yaml:"exit_code,omitempty" json:"exit_code,omitempty"`
VenueJSON RawBody `yaml:"venue_json,omitempty" json:"venue_json,omitempty"`
ArtifactKey string `yaml:"artifact_key,omitempty" json:"artifact_key,omitempty"`
}
#DeployTargetDispatchReply carries whatever the dispatched op produces — Status for "status", Output/ExitCode for "shell"/"attach" (the F12 non-zero-exit propagation via *sdk.ExitCodeError), Venue for "add"/"update" (the spec.VenueDescriptor PrepareVenue produced or re-confirmed, threaded back to core so a substrate WITH a lifecycle hook — pod's overlay container, vm's guest — hands core the SAME live executor for --verify and every subsequent dispatch on this target, mirroring the pre-S3b `t.exec = exec` reassignment inside apply()), nothing for the rest (success is "no error").
type DeployTargetDispatchRequest ¶
type DeployTargetDispatchRequest struct {
Op string `yaml:"op,omitempty" json:"op"`
Name string `yaml:"name,omitempty" json:"name"`
Word string `yaml:"word,omitempty" json:"word"`
HasLifecycle bool `yaml:"has_lifecycle,omitempty" json:"has_lifecycle,omitempty"`
HasPreresolve bool `yaml:"has_preresolve,omitempty" json:"has_preresolve,omitempty"`
HasPlan bool `yaml:"has_plan,omitempty" json:"has_plan,omitempty"`
Node *Deploy `yaml:"node,omitempty" json:"node,omitempty"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
NodeOnly bool `yaml:"node_only,omitempty" json:"node_only,omitempty"`
// ledger_root OPTIONALLY overrides the ledger root directory (kit.LedgerPaths.Root) — the
// pre-S3b externalDeployTarget carried a settable `paths *kit.LedgerPaths` field a TEST could
// redirect to a temp dir instead of the operator's real ~/.config/opencharly/installed/; this
// is the wire-safe equivalent (a bare root string, the plugin derives Deploys/Candies/LockFile
// from it exactly like kit.DefaultLedgerPaths does). Empty (the default) — kit.DefaultLedgerPaths().
LedgerRoot string `yaml:"ledger_root,omitempty" json:"ledger_root,omitempty"`
PlansJSON RawBody `yaml:"plans_json,omitempty" json:"plans_json,omitempty"`
OptsJSON RawBody `yaml:"opts_json,omitempty" json:"opts_json,omitempty"`
ChecksJSON RawBody `yaml:"checks_json,omitempty" json:"checks_json,omitempty"`
Cmd []string `yaml:"cmd,omitempty" json:"cmd,omitempty"`
TTY bool `yaml:"tty,omitempty" json:"tty,omitempty"`
// venue_json is the ALREADY-MATERIALIZED spec.VenueDescriptor for this deploy. Two distinct
// producers set it: (a) core, when this dispatch is a NESTED non-lifecycle child (a
// `local:`/`android:`/`k8s:` deploy under a vm/pod, tree position) — Add flattens the
// live ancestor executor (EmitOpts.ParentExec) into this field via kit.DescriptorFromExecutor
// BEFORE the very first "add" dispatch, since that live value cannot itself cross the wire
// (FIX ROUND, S3b follow-up — its absence on "add" was the R10 bed regression: every nested
// child silently applied on the operator's host instead of the parent venue); (b) core again,
// carrying FORWARD a prior dispatch's reported venue for the SAME target's lifetime
// (Update/Del/Start/Stop/Status/Logs/Shell/Attach/Rebuild, after a lifecycle substrate's "add"
// already ran PrepareVenue once). Either way the plugin re-materializes it via
// kit.VenueFromDescriptor instead of re-deriving from the node or re-running PrepareVenue.
// Absent on "add" for a ROOT (non-nested) OR lifecycle (vm/pod) target — those derive their
// own venue fresh (root: RootExecutorForDeployNode(node); lifecycle: PrepareVenue).
VenueJSON RawBody `yaml:"venue_json,omitempty" json:"venue_json,omitempty"`
// distro_cfg_json is the marshalled *buildkit.DistroConfig ("add"/"update" only) — recordDeploy's
// FillReverseUninstallCmds needs it to render an aur-builder ReverseOpPackageRemove's
// UninstallCmd host-side-equivalent, now plugin-side since buildkit.DistroConfig is a plain sdk
// type with no core-only coupling (unlike the core-only buildEngineContext wrapper it used to
// travel inside).
DistroCfgJSON RawBody `yaml:"distro_cfg_json,omitempty" json:"distro_cfg_json,omitempty"`
// host_env_json is the marshalled spec.HostEnv (CharlyBin/Home/Version) — the R10 bed-found
// fifth bug in this cluster's move: every lifecycle Op the deleted substrate_lifecycle_grpc.go
// sent used its OWN hostEnvJSON() helper, computed HOST-side (os.Executable() resolves to the
// charly binary only when called IN CORE — a plugin's os.Executable() resolves to the PLUGIN
// binary, wrong for an out-of-process placement even though today's compiled-in placement
// happened not to crash on it, since a bare `spec.HostEnv{}` zero-value was marshalled instead
// of ever actually calling it, plugin-side, in the S3b port). Core (unified_targets.go's
// dispatch, the ONLY place that reliably knows its OWN binary regardless of the substrate's or
// command:bundle's own placement) now computes it ONCE per dispatch call and threads it here;
// candy/plugin-bundle forwards it verbatim to every lifecycle Op instead of computing its own.
HostEnvJSON RawBody `yaml:"host_env_json,omitempty" json:"host_env_json,omitempty"`
}
#DeployTargetDispatchRequest (S3b) is the ONE generic host→command:bundle envelope every UnifiedDeployTarget/LifecycleTarget method dispatches through, discriminated by `op` (the project rulebook's "generic over ad-hoc" — one wire shape, not eleven). Core's thin ResolveTarget proxy (unified_targets.go) constructs this per call from data alone — it never holds a *grpcProvider or any core-private registry object, so the type is free to live entirely on the wire. `word` is the resolved deploy-substrate provider word (e.g. "pod"/"vm"/ "local"/"k8s"/"android") the plugin dispatches the ACTUAL substrate leg to, via its own sdk.Executor.InvokeProvider — core never talks to the substrate provider directly once this lands. `has_lifecycle` is the ONE piece of substrate metadata core must resolve itself (the registered-provider's own `lifecycle` flag lives on the core-private *grpcProvider) — it gates whether Start/Stop/Status/Logs/Shell/Attach/Rebuild are even valid for this substrate (mirroring the former ErrNotSupportedOnExternal branches) AND whether the Q1 arbiter bracket applies to Start/Stop — `has_plan` is that DIFFERENT, narrower boolean (K4-exit, FLOOR-SLIM- proper Unit-8): core still computes it (lifecycleStartPlanHooks[word]/lifecycleStopPlanHooks[word] presence, pod_lifecycle_dispatch.go, unmoved) and now THREADS it on the wire instead of bracketing the dispatch call itself — command:bundle's handleLifecycleSimple owns the bracket call, via the arbiter-bracket-acquire/-release HostBuild seams below (the same os.Setenv/os.Getenv nested-subprocess-inheritance property the former arbiter_bracket.go preserved, now reached plugin-side). `node` is the dispatch-merged BundleNode (nil for a ref-based deploy with no charly.yml entry) — required when has_plan is true (the claim fields live on it). `plans_json` carries []InstallPlanView for Add/Update. `opts_json` carries the op-specific opts struct as an opaque envelope (the zero-value-safe pattern every #Pod*Opts request already uses) — kept opaque rather than one field per opts type so a NEW deploy verb never needs a NEW CUE field, only a new decode branch plugin-side.
type DeployTargetLogsOpts ¶
type DeployTargetLogsOpts struct {
Follow bool `yaml:"follow,omitempty" json:"follow,omitempty"`
Tail int64 `yaml:"tail,omitempty" json:"tail,omitempty"`
Sidecar string `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
}
#DeployTargetLogsOpts mirrors the former charly-core LogsOpts (`charly logs`).
type DeployTargetRebuildOpts ¶
type DeployTargetRebuildOpts struct {
RebuildImage bool `yaml:"rebuild_image,omitempty" json:"rebuild_image,omitempty"`
AssumeYes bool `yaml:"assume_yes,omitempty" json:"assume_yes,omitempty"`
DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,omitempty"`
}
#DeployTargetRebuildOpts mirrors the former charly-core RebuildOpts (`charly update` rebuild path).
type DeployTargetStatus ¶
type DeployTargetStatus struct {
State string `yaml:"state,omitempty" json:"state,omitempty"`
Healthy bool `yaml:"healthy,omitempty" json:"healthy,omitempty"`
Details map[string]string `yaml:"details,omitempty" json:"details,omitempty"`
}
#DeployTargetStatus (S3b, Unit-6 design) mirrors the former charly-core StatusInfo — a deployment's live runtime state, now CUE-sourced because it crosses the plugin boundary once externalDeployTarget/grpcSubstrateLifecycle move to candy/plugin-bundle.
type DeployTraits ¶
type DeployTraits struct {
// venue: how commands physically execute in this substrate's venue:
//
// container — podman/docker exec into the container by name (pod).
// ssh — an ssh hop into the guest (vm).
// shell — the substrate's own root executor runs on the host (local; k8s host-side).
// parent — reached via the parent's venue, no own executor (android).
// none — external-in-place (zero value / group).
Venue string `yaml:"venue,omitempty" json:"venue,omitempty"`
// image_backed: the substrate runs a baked OCI image (pod).
ImageBacked bool `yaml:"image_backed,omitempty" json:"image_backed,omitempty"`
// image_context: the substrate composes over an image build context (pod overlay, k8s manifests).
ImageContext bool `yaml:"image_context,omitempty" json:"image_context,omitempty"`
// machine_venue: the substrate is a full machine with a system init (host/vm/local) — its
// services render as systemd units, not a container init.
MachineVenue bool `yaml:"machine_venue,omitempty" json:"machine_venue,omitempty"`
// exclusive_venue: the substrate holds an exclusive host-resource lease boundary (vm).
ExclusiveVenue bool `yaml:"exclusive_venue,omitempty" json:"exclusive_venue,omitempty"`
// leaf_only: the substrate is a deploy-chain LEAF — it cannot be descended into (k8s).
LeafOnly bool `yaml:"leaf_only,omitempty" json:"leaf_only,omitempty"`
// bracketed_lifecycle: the substrate's Start/Stop accept direct-mode CLI opts (env/port/
// volume/bind/no-auto-detect on Start, unmount on Stop) AND need the Q1 resource-arbiter
// claim bracketed around the dispatch (pod). A substrate that manages its own venue
// lifecycle + resource claim (vm, via `charly vm start`/`stop`) leaves this false.
BracketedLifecycle bool `yaml:"bracketed_lifecycle,omitempty" json:"bracketed_lifecycle,omitempty"`
// bed_target: the substrate is a valid `kind:check` bed target — pod/vm/local/android run a
// disposable check bed; k8s (leaf_only) does not. validateCheckBeds reads this instead of
// switching on the substrate word (the boundary-law incomplete-seam gate).
BedTarget bool `yaml:"bed_target,omitempty" json:"bed_target,omitempty"`
// supports_ephemeral: the substrate's Add/Del path wires the ephemeral register/teardown seam
// (TTL timer + charly.yml persistence) — vm only today; pod/k8s reject `ephemeral: true` until
// their Add/Del wire the same seam. validateEphemeral reads this instead of a substrate switch.
SupportsEphemeral bool `yaml:"supports_ephemeral,omitempty" json:"supports_ephemeral,omitempty"`
// supports_from_snapshot: the substrate has a backing chain `from_snapshot:` restores from — vm
// only (pod/k8s have no backing chains). validateEphemeral reads this instead of a word switch.
SupportsFromSnapshot bool `yaml:"supports_from_snapshot,omitempty" json:"supports_from_snapshot,omitempty"`
}
#DeployTraits is a SUBSTRATE kind's DECLARED deploy behaviour (P9): a substrate plugin advertises it per word over Describe (ProvidedCapability.deploy_traits), and kit.StampDescent stamps it onto every node's #DescentDescriptor. It is the SINGLE plugin-declared source for "how does this substrate behave in the deploy chain", so the kernel consults the traits off node.Descent BY TRAIT — never by switching on the substrate kind word (the boundary law). Canonical table: pod=container+image_backed+image_context; vm=ssh+machine_venue+exclusive_venue; local=shell+machine_venue; k8s=shell+image_context+leaf_only; android=parent; zero value = external-in-place. Not authored — charly-written state stamped at load.
type DeployTraitsCarrier ¶
type DeployTraitsCarrier interface {
DeclaredDeployTraits() *DeployTraits
}
DeployTraitsCarrier is implemented by a provider (grpcProvider out-of-proc, inprocProvider compiled-in) that carries a SUBSTRATE class:kind capability's DECLARED DeployTraits (P9). Non-nil → deployTraitsFor returns them so kit.StampDescent stamps node.Descent BY TRAIT; nil (or not implemented) → the external-in-place default. This is the SINGLE plugin-declared source for a substrate's deploy behaviour — the consult sites read the stamped traits off node.Descent, never switching on the substrate kind word (the kernel/plugin boundary law).
type DeployVenue ¶
type DeployVenue struct {
DeployName string `yaml:"deploy_name,omitempty" json:"deploy_name"`
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
Substrate RawBody `yaml:"substrate,omitempty" 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).
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 DeploymentStatus ¶
type DeploymentStatus struct {
Kind SubstrateKind `yaml:"kind,omitempty" json:"kind"`
Image string `yaml:"image,omitempty" json:"image"`
ImageRef string `yaml:"image_ref,omitempty" json:"image_ref,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Status string `yaml:"status,omitempty" json:"status"`
Uptime string `yaml:"uptime,omitempty" json:"uptime,omitempty"`
Container string `yaml:"container,omitempty" json:"container"`
Ports []PortMapping `yaml:"ports,omitempty" json:"ports,omitempty"`
Devices []string `yaml:"devices,omitempty" json:"devices,omitempty"`
Tools []ToolStatus `yaml:"tools,omitempty" json:"tools,omitempty"`
Volumes []string `yaml:"volumes,omitempty" json:"volumes,omitempty"`
Network string `yaml:"network,omitempty" json:"network,omitempty"`
Tunnel string `yaml:"tunnel,omitempty" json:"tunnel,omitempty"`
Secrets []string `yaml:"secrets,omitempty" json:"secrets,omitempty"`
RunMode string `yaml:"run_mode,omitempty" json:"run_mode"`
Nested []*DeploymentStatus `yaml:"nested,omitempty" json:"nested,omitempty"`
Source string `yaml:"source,omitempty" json:"source,omitempty"`
}
#DeploymentStatus — the rendered shape for the table + JSON outputs across every deployment substrate (pod / vm / k8s / local / android). kind discriminates the substrate; nested carries multi-hop children (RECURSIVE self-reference, populated by the nested overlay); source records provenance (libvirt|ledger|adb|tree|podman).
type DescentDescriptor ¶
type DescentDescriptor struct {
// transport: how the deploy chain descends INTO this node's substrate (DERIVED from the
// declared venue + leaf_only by kit.DescentFromTraits):
//
// none — shares the parent venue; no hop (local, android).
// container-exec — enter the container by name (pod; podman/docker per engine).
// ssh — an ssh hop into the guest (vm).
// reject — unreachable via the deploy chain (k8s → use kubectl).
Transport string `yaml:"transport,omitempty" json:"transport"`
// host_rooted: the substrate's own ROOT executor runs directly on the host
// (local), so the check runner uses rootExecutorForDeployNode, not a container chain.
HostRooted bool `yaml:"host_rooted,omitempty" json:"host_rooted,omitempty"`
// venue: how commands physically execute in this substrate's venue:
//
// container — podman/docker exec into the container by name (pod).
// ssh — an ssh hop into the guest (vm).
// shell — the substrate's own root executor runs on the host (local; k8s host-side).
// parent — reached via the parent's venue, no own executor (android).
// none — external-in-place (zero value / group).
Venue string `yaml:"venue,omitempty" json:"venue,omitempty"`
// image_backed: the substrate runs a baked OCI image (pod).
ImageBacked bool `yaml:"image_backed,omitempty" json:"image_backed,omitempty"`
// image_context: the substrate composes over an image build context (pod overlay, k8s manifests).
ImageContext bool `yaml:"image_context,omitempty" json:"image_context,omitempty"`
// machine_venue: the substrate is a full machine with a system init (host/vm/local) — its
// services render as systemd units, not a container init.
MachineVenue bool `yaml:"machine_venue,omitempty" json:"machine_venue,omitempty"`
// exclusive_venue: the substrate holds an exclusive host-resource lease boundary (vm).
ExclusiveVenue bool `yaml:"exclusive_venue,omitempty" json:"exclusive_venue,omitempty"`
// leaf_only: the substrate is a deploy-chain LEAF — it cannot be descended into (k8s).
LeafOnly bool `yaml:"leaf_only,omitempty" json:"leaf_only,omitempty"`
// bracketed_lifecycle: the substrate's Start/Stop accept direct-mode CLI opts (env/port/
// volume/bind/no-auto-detect on Start, unmount on Stop) AND need the Q1 resource-arbiter
// claim bracketed around the dispatch (pod). A substrate that manages its own venue
// lifecycle + resource claim (vm, via `charly vm start`/`stop`) leaves this false.
BracketedLifecycle bool `yaml:"bracketed_lifecycle,omitempty" json:"bracketed_lifecycle,omitempty"`
// bed_target: the substrate is a valid `kind:check` bed target — pod/vm/local/android run a
// disposable check bed; k8s (leaf_only) does not. validateCheckBeds reads this instead of
// switching on the substrate word (the boundary-law incomplete-seam gate).
BedTarget bool `yaml:"bed_target,omitempty" json:"bed_target,omitempty"`
// supports_ephemeral: the substrate's Add/Del path wires the ephemeral register/teardown seam
// (TTL timer + charly.yml persistence) — vm only today; pod/k8s reject `ephemeral: true` until
// their Add/Del wire the same seam. validateEphemeral reads this instead of a substrate switch.
SupportsEphemeral bool `yaml:"supports_ephemeral,omitempty" json:"supports_ephemeral,omitempty"`
// supports_from_snapshot: the substrate has a backing chain `from_snapshot:` restores from — vm
// only (pod/k8s have no backing chains). validateEphemeral reads this instead of a word switch.
SupportsFromSnapshot bool `yaml:"supports_from_snapshot,omitempty" json:"supports_from_snapshot,omitempty"`
}
#DescentDescriptor is the loader-DERIVED venue-hop descriptor (Cutover H + P9). candy/plugin-substrate declares its per-word #DeployTraits over Describe, and kit.StampDescent stamps them here so the deploy chain descends generically by TRANSPORT and every consult site reads the substrate behaviour off the stamped TRAITS — never by switching on the substrate kind word. transport/host_rooted are the DERIVED nesting view (from the traits); the closed transport set is the kernel's nesting-boundary vocabulary.
func DescentFromTraits ¶ added in v0.2026213.1726
func DescentFromTraits(t *DeployTraits) *DescentDescriptor
DescentFromTraits builds a #DescentDescriptor from a substrate's DECLARED #DeployTraits, deriving the closed nesting-transport vocabulary GENERICALLY from the traits — never by switching on a substrate kind word. It copies the declared traits verbatim (the consult sites read them off node.Descent) and computes transport + host_rooted:
leaf_only → reject (k8s: a deploy-chain leaf, unreachable via exec) venue == container → container-exec (pod: podman/docker exec by name) venue == ssh → ssh (vm: an ssh hop into the guest) otherwise → none (shell/parent/none share the parent venue)
nil traits (a targetless group / empty target / a word with no declared substrate traits) map to the external-in-place default (container-exec) — behaviour-preserving for the former descentForTarget default arm that covered pod and the empty/group target.
type DetectedDevices ¶
type DetectedDevices struct {
GPU bool `yaml:"gpu,omitempty" json:"gpu"`
AMDGPU bool `yaml:"amd_gpu,omitempty" json:"amd_gpu"`
AMDGFXVersion string `yaml:"amd_gfx_version,omitempty" json:"amd_gfx_version"`
RenderNode string `yaml:"render_node,omitempty" json:"render_node"`
Devices []string `yaml:"devices,omitempty" json:"devices"`
}
#DetectedDevices holds the results of host device auto-detection.
type Diagnostic ¶
type Diagnostic struct {
Severity string `yaml:"severity,omitempty" json:"severity,omitempty"`
Message string `yaml:"message,omitempty" json:"message"`
Path string `yaml:"path,omitempty" json:"path,omitempty"`
}
#Diagnostic is one finding from a plugin kind's deep OpValidate check (F7/C8).
type Diagnostics ¶
type Diagnostics struct {
Items []Diagnostic `yaml:"items,omitempty" json:"items,omitempty"`
}
#Diagnostics is the OpValidate reply. HasErrors() is a pure Go METHOD — CUE cannot express it — and stays hand-written in spec/deploy_methods.go.
func (Diagnostics) HasErrors ¶
func (d Diagnostics) HasErrors() bool
HasErrors reports whether any item is error-severity (empty severity counts as error).
type DiscoverConfig ¶ added in v0.2026210.808
type DiscoverConfig []ScanSpec
DiscoverConfig is a FLAT list of generic scan specs. Each spec scans a path for directories containing its manifest; every discovered manifest is parsed as a multi-document stream and routed by SHAPE (the kind-key it carries), so one discover root can surface candies, boxes, deploys — any kind. There is no kind dimension and no hardcoded path/filename: discovery is fully configured in charly.yml.
type DiscoveredManifest ¶
type DiscoveredManifest struct {
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
Manifest string `yaml:"manifest,omitempty" json:"manifest,omitempty"`
RootDir string `yaml:"rootDir,omitempty" json:"rootDir,omitempty"`
Docs []ParsedProject `yaml:"docs,omitempty" json:"docs,omitempty"`
}
#DiscoveredManifest — one discovered entity directory's manifest (a `discover:` walk hit), applied by the host AFTER the docs (explicit-entry-wins). `dir`/`rootDir`/`manifest` let the host register a lazy candy `From:` reference (a LAYER candy) or materialize the node (any other kind); `docs` are the manifest's parsed documents.
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 ¶
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 ¶
PrimaryFormat returns the distro's primary (non-secondary) package format.
type DistroBuilderCandidate ¶ added in v0.2026213.1726
type DistroBuilderCandidate struct {
Name string `yaml:"name,omitempty" json:"name"`
Distro []string `yaml:"distro,omitempty" json:"distro"`
Builder BuilderMap `yaml:"builder,omitempty" json:"builder"`
}
#DistroBuilderCandidate is one named entity's Distro tags + Builder map — the generic shape PickDistroBuilder consumes (R3: the shared abstraction behind the "distro-keyed builder default" lookup). Pure data (string/[]string/#BuilderMap) with zero buildkit-specific typing, so it is CUE-sourced here alongside #BuilderMap per the same rule — moved out of buildkit (the builder-map value-primitive cluster: EffectiveBuilderForBox/ResolveEffectiveBuilder/ distroBuilderMap/PickDistroBuilder + this candidate type relocated to spec/spec as pure-value CONTRACT computation, plan Rule 2 + step 3). The lookup FUNCTIONS themselves are BEHAVIOR (hand-written Go, not CUE-generated) and live in spec/spec/builder_resolve.go.
type DistroConfig ¶ added in v0.2026210.2052
type DistroConfig struct {
Distro map[string]*ResolvedDistro `yaml:"distro,omitempty" json:"distro"`
}
#DistroConfig is the `distro:` build-vocabulary container — the whole distro map keyed by distro name (charly/charly.yml's `distro:` section). #55 step 3-III: relocated from sdk/buildkit so every charly/plugin file that uses ONLY this value type drops its sdk/buildkit import for spec (the import-purity lever). Its vocabulary-resolution methods (ResolveDistro / ResolveInherits / AllFormatNames / FindFormat / ValidFormat / ExpandPackageInheritance) are pure Go METHODS on the type's own map — CUE cannot express them — and stay hand-written in spec/distro_config_methods.go (mirrors ResolvedDistro's PrimaryFormat in spec/distro_methods.go: a method, not a type).
func ProjectDistroConfig ¶ added in v0.2026213.1726
func ProjectDistroConfig(uf *UnifiedFile, resolveDistro func(json.RawMessage) (*ResolvedDistro, error)) *DistroConfig
ProjectDistroConfig reconstructs the *DistroConfig (distro: section) from uf, resolving each opaque `distro` body via the caller-supplied resolveDistro callback (the distro plugin's OpResolve leg). Nil when no distros are configured.
func WrapDistroDef ¶ added in v0.2026210.2052
func WrapDistroDef(def *ResolvedDistro) *DistroConfig
WrapDistroDef presents one already-resolved ResolvedDistro as a DistroConfig so the format-keyed FindFormat resolver returns that def's Format. Returns nil for a nil def.
func (*DistroConfig) AllFormatNames ¶ added in v0.2026210.2052
func (dc *DistroConfig) AllFormatNames() []string
AllFormatNames returns a sorted, deduplicated list of all format names across all distros.
func (*DistroConfig) ExpandPackageInheritance ¶ added in v0.2026210.2052
func (dc *DistroConfig) ExpandPackageInheritance(tags []string) []string
ExpandPackageInheritance appends, AFTER the authored distro tags, every `inherits:` ancestor of a tag whose distro def opts into package inheritance via `inherit_packages: true` — most-specific authored tags kept first, ancestors appended as the least-specific levels.
func (*DistroConfig) FindFormat ¶ added in v0.2026210.2052
func (dc *DistroConfig) FindFormat(name string) *Format
FindFormat returns the Format for a format name (rpm/deb/pac/aur), resolving distro `inherits:` chains. The first distro that defines the format wins. Returns nil when no distro defines it.
func (*DistroConfig) ResolveDistro ¶ added in v0.2026210.2052
func (dc *DistroConfig) ResolveDistro(distroTags []string) *ResolvedDistro
ResolveDistro finds the distro definition matching the image's distro tags. Walks tags in order, strips :version suffix to match base distro name. Follows inherits: chains with cycle detection, inheriting formats from parent.
func (*DistroConfig) ResolveInherits ¶ added in v0.2026210.2052
func (dc *DistroConfig) ResolveInherits(def *ResolvedDistro, maxDepth int) *ResolvedDistro
func (*DistroConfig) ValidFormat ¶ added in v0.2026210.2052
func (dc *DistroConfig) ValidFormat(name string) bool
ValidFormat returns true if any distro defines this format name.
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 DistroResolveInput ¶
type DistroResolveInput struct {
Distro RawBody `yaml:"distro,omitempty" json:"distro"`
}
#DistroResolveInput carries one opaque distro body to project.
type DistroResolveReply ¶
type DistroResolveReply struct {
Resolved *ResolvedDistro `yaml:"resolved,omitempty" json:"resolved,omitempty"`
}
#DistroResolveReply wraps the resolved distro.
type DoMode ¶
type DoMode string
DoMode is the act/assert/instruct axis. act = perform a side-effect; assert = run the matchers (read-only); instruct = hand free-form text to the agent grader.
func StepDoMode ¶
StepDoMode maps the step keyword to the act/assert/instruct dispatch enum.
type DocParser ¶
type DocParser interface {
ParseDoc(doc *yaml.Node, t Threaded) (directives map[string]*yaml.Node, pp ParsedProject, err error)
}
DocParser is the swappable per-document PARSE seam: the loader plugin candy implements it (candy/plugin-loader, delegating to loaderkit.ParseDoc), and the host resolves the registered loader provider to it and calls it for every config document — so an alternative loader plugin serves a different config front-end by implementing this. Typed (no wire envelope) since it runs on every document load. `directives` is the reserved-directive mapping (import/discover/version/ repo/defaults/provides); `pp` is the decomposed entity nodes.
type DocShape ¶
type DocShape int
DocShape classifies one parsed YAML document's top level.
const ( // DocShapeEmpty — a scalar-null / empty mapping document (nothing to load). DocShapeEmpty DocShape = iota // DocShapeNode — the unified name-first node-form: reserved document // directives (version/import/discover/defaults/repo/provides) plus a flat // map of arbitrary-name entity nodes (each `<name>: {<discriminator>: …}`), // and NO top-level kind-map key. The ONE authoring surface; a legacy // kind-keyed / root-shape document is hard-rejected downstream (the host's // #NodeDoc CUE gate), not here. DocShapeNode )
func ClassifyDoc ¶
ClassifyDoc inspects a document's top level and returns its shape: a non-empty mapping is a node-form document (arbitrary entity-name nodes and/or the reserved directives version/import/discover/…); a scalar-null / empty mapping is DocShapeEmpty; a non-mapping top level is an error. Entity + directive validation happens downstream (the loader parse + the #NodeDoc CUE gate).
type EmitOpts ¶
type EmitOpts struct {
DryRun bool
FormatJSON bool // print IR as JSON on stdout instead of table
AllowRepoChanges bool
AllowRootTasks bool
WithServices bool
SkipIncompatible bool
AssumeYes bool // skip sudo preflight, confirmation prompts
Verify bool // run layer tests after install
Pull bool // force re-fetch of remote refs / image pull
BuilderImageOverride string
// DevLocalPkg marks a DISPOSABLE CHECK BED's deploy — the deploy-side twin of `charly box
// build --dev-local-pkg`, set by the bed runner and by nothing else. It changes ONE decision:
// a localpkg candy whose package source cannot be located becomes a hard failure instead of
// the benign "the candy's curl/COPY task installs it instead" skip. A bed exists to prove the
// IN-DEVELOPMENT package builds and installs, so a silent skip there makes the bed assert
// something it never tested — which is exactly what happened when an uninitialized pkg/fedora
// submodule let a bed skip building the rpm and fail only later, at an unrelated live check.
DevLocalPkg bool
// ParentExec is the DeployExecutor of the parent deployment in a
// nested tree. Non-nil iff this target is dispatched as a child of
// another — BundleAddCmd's tree walker builds the chain root-first
// and passes the immediate ancestor's executor here. Targets that
// support being nested (host, container, vm) compose their own
// executor over ParentExec via NestedExecutor; leaf-only targets
// (kubernetes) ignore it and error if non-nil.
//
// When nil, the target runs against its natural root venue
// (ShellExecutor for host, a fresh SSHExecutor for vm, etc.)
// — preserving the flat-schema behavior for v2 configs that happen
// to have no `children:`.
ParentExec DeployExecutor
// ParentNode is the BundleNode above this target in the tree.
// Useful for targets that need parent-level context beyond the
// executor (e.g. a vm child wants to know its parent container's
// name to wire network forwarding). nil at the root.
ParentNode *BundleNode
// Path is the dotted-path identifier of this node (e.g.
// "stack.web.db"). Used for logging + ledger keying.
Path string
}
EmitOpts carries cross-cutting toggles passed by command-line flags. Gates are checked per-step by the target; target-specific options (the container target's registry auth, the host target's --assume-yes, --dry-run) are bundled here too.
func (EmitOpts) ContextOrDefault ¶
ContextOrDefault returns opts' context if one's attached, or a background context.
type EmitReply ¶
type EmitReply struct {
Fragment string `yaml:"fragment,omitempty" json:"fragment"`
ActScript bool `yaml:"act_script,omitempty" json:"act_script,omitempty"`
}
#EmitReply is what a plugin verb/builder returns from an OpEmit Invoke at build time. By default fragment is a verbatim Containerfile FRAGMENT the generator splices into the emitted Containerfile. When act_script is true the fragment is instead a raw state-provision ACT SHELL (the former ProvisionActor.RenderProvisionScript output): the render wraps it in a RUN via the same EmitCmd path a literal command verb uses, rather than splicing it verbatim. This lets a state-provision verb self-declare its build-emit shape over the uniform Invoke(OpEmit), so the render dispatches every verb through InvokeProvider(OpEmit) with no package-main concrete-type assert.
type EncExecInput ¶
type EncExecInput struct {
Method string `yaml:"method,omitempty" json:"method"`
ImageID string `yaml:"image_id,omitempty" json:"image_id"`
BoxName string `yaml:"box_name,omitempty" json:"box_name"`
Passphrase string `yaml:"passphrase,omitempty" json:"passphrase,omitempty"`
OldPass string `yaml:"old_pass,omitempty" json:"old_pass,omitempty"`
NewPass string `yaml:"new_pass,omitempty" json:"new_pass,omitempty"`
Volumes []EncVolumePlan `yaml:"volumes,omitempty" 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 `yaml:"error,omitempty" 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 `yaml:"name,omitempty" json:"name"`
CipherDir string `yaml:"cipher_dir,omitempty" json:"cipher_dir"`
PlainDir string `yaml:"plain_dir,omitempty" json:"plain_dir"`
ScopeUnit string `yaml:"scope_unit,omitempty" json:"scope_unit"`
Initialized bool `yaml:"initialized,omitempty" json:"initialized"`
Mounted bool `yaml:"mounted,omitempty" 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 EnsureBuildersParams ¶ added in v0.2026213.1726
type EnsureBuildersParams struct {
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
Words []string `yaml:"words,omitempty" json:"words,omitempty"`
}
#EnsureBuildersParams carries the builder words the host must scan+connect for the "ensure-builders" render-seam method (RenderSeamEnsureBuilders).
type EnvConfig ¶
type EnvConfig struct {
Vars map[string]string `yaml:"vars,omitempty" json:"vars,omitempty"`
PathAppend []string `yaml:"path_append,omitempty" json:"path_append,omitempty"`
}
#EnvConfig — resolved candy env (KEY=value vars + PATH-append entries). Mirrors kit.EnvConfig.
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 EnvKV ¶
type EnvKV struct {
Key string `yaml:"key,omitempty" json:"key"`
Value string `yaml:"value,omitempty" json:"value"`
}
#EnvKV is a deterministic env-var ordering helper (template iteration order). Named #EnvKV, NOT #KeyValue — the schemagen vocab generator auto-registers EVERY top-level `#<X>Value` def as a fake kind-value gate (kindValueDefs, `^#?([A-Za-z0-9]+)Value$`), so `#KeyValue` would spuriously register a "key" kind (RDD-caught live: it appeared in the generated KindValueDefs map). The charly-name alias `KeyValue = EnvKV` (spec/charly_names.go) preserves the exported Go type name every real consumer (deploykit.MapToKeyValueSlice, spec.SortedEnvList, charly/service_render.go) already uses.
type EnvProvideEntry ¶ added in v0.2026210.808
type EnvProvideEntry struct {
Name string `yaml:"name" json:"name"`
Value string `yaml:"value" json:"value"`
Source string `yaml:"source" json:"source"`
}
EnvProvideEntry is a resolved env_provides entry in charly.yml. Relocated from deploykit (provides.go) so UnifiedFile.Provides (*ProvidesConfig) lives entirely in spec; deploykit keeps the provides PIPELINE (FilterOwnProvides/ResolveTemplate/…), consuming these spec types.
func (EnvProvideEntry) GetName ¶ added in v0.2026210.808
func (e EnvProvideEntry) GetName() string
func (EnvProvideEntry) GetSource ¶ added in v0.2026210.808
func (e EnvProvideEntry) GetSource() 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 EphemeralRegisterReply ¶
type EphemeralRegisterReply struct {
}
type EphemeralRegisterRequest ¶
type EphemeralRegisterRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
Node *Deploy `yaml:"node,omitempty" json:"node"`
}
#EphemeralRegisterRequest/#EphemeralRegisterReply — the host→command:bundle OpEphemeralRegister leg (FINAL/K5 unit 6a): ephemeral_lifecycle.go's cross-substrate ephemeral-instance registration (systemd TTL transient timer + parent-detection + charly.yml persistence) moved to candy/plugin-bundle, the substrate-neutral deploy-lifecycle owner (vm/pod/k8s all register through it via deploy_add_shared.go's registerEphemeralIfMarked, which STAYS host-side — candidate-floor sibling of bundle_add_cmd.go — and Invokes this as the FIRST action of every Add). Registration failure is best-effort (logged plugin-side, never fatal to the deploy) — the reply is empty; the host discards the returned handle (the prior in-core contract already did, registerEphemeralIfMarked only checked the error).
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"`
// DeployAddress is the CLI-addressable deploy identity (the dotted tree path for a nested
// deploy, e.g. "parent.child" — `charly bundle del <DeployAddress>`), DISTINCT from the
// dc.Bundle MAP KEY this entry is stored under (which is a dot-SANITIZED "vm:<domain-id>"
// form — a literal '.' is illegal in a map key, see
// ValidateDeploymentName (deploy_tree_validate.go); FINAL/K5 unit 6a RCA #2). A nested-ephemeral-child teardown scan
// (candy/plugin-bundle's teardownChildrenRec) has ONLY this struct to recover the real
// address from — the map key it was found under cannot be reversed back to the original
// dotted path (VmDomainIdentity's "." -> "-" replacement is lossy).
DeployAddress string `yaml:"deploy_address,omitempty" json:"deploy_address,omitempty"`
}
EphemeralRuntime is the live runtime state of an active ephemeral instantiation. Source: charly/deploy.go EphemeralRuntime.
type EphemeralTeardownReply ¶
type EphemeralTeardownReply struct {
}
type EphemeralTeardownRequest ¶
type EphemeralTeardownRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
Node *Deploy `yaml:"node,omitempty" json:"node"`
}
#EphemeralTeardownRequest/#EphemeralTeardownReply — the OpEphemeralTeardown leg any substrate's own post-teardown handling can Invoke directly (recursive nested-child teardown, TTL timer cancel, snapshot/parent refcount decrement, charly.yml cleanup lives in candy/plugin-bundle's teardownEphemeral). Today's one caller is candy/plugin-deploy-vm/ lifecycle.go's vmPostTeardown (F6 vm-lifecycle move, coneB-vmlifecycle — formerly a host-side pre-dispatch hook, charly/vm_lifecycle_preresolve.go's vmLifecyclePostTeardown, dispatched in-proc via the now-deleted charly/ephemeral_dispatch.go's TeardownEphemeralLifecycle).
type ExecContext ¶
type ExecContext string
ExecContext is where an op runs. An op's Context list (or its VerbCatalog default) declares legality; the active engine supplies the running context and skips ops whose context set does not include it.
const ( CtxBuild ExecContext = "build" CtxDeploy ExecContext = "deploy" CtxRuntime ExecContext = "runtime" )
type ExternalPluginStep ¶ added in v0.2026210.2308
type ExternalPluginStep struct {
Op *Op // the run: step op — Plugin (verb word) + PluginInput + RunAs
CandyName string // owning candy (provenance + the ledger CandyRecord key)
ResolvedUser string // uid:gid or "root" after resolveUserSpec — drives Scope
Distros []string // the image's distro tag chain (img.Tags) for the build-emit BuildEnv
}
ExternalPluginStep is the install-timeline IR node for a `run: plugin: <verb>` step whose verb is served by an EXTERNAL (out-of-process) plugin — a grpcProvider, not a built-in TypedStepProvider (package/service) or ProvisionActor (the state-provision shell verbs) or the `command` install verb. constructOpStep (via the "construct-step" seam's registry consult) routes it here; the Invoke op is picked per venue, placement-agnostic above the registry:
- the DEPLOY venue (the install runs ON the target, not into an image): BOTH target:local AND target:vm externalized, so an ExternalPluginStep on a deploy is reached by the external local/vm plugin's kit.WalkPlans, which routes it through the host's RunHostStep (the nested ExternalPluginStep case) → executeExternalPluginStep → Invoke(OpExecute) WITH the live DeployExecutor on the go-plugin broker (the E3b reverse channel), so the plugin runs its deploy-context effect (RunSystem/RunUser) on the real venue and RETURNS its teardown ReverseOps which the host records into the ledger (record-and-replay — only recorded ops are reversed at `charly bundle del`). This is the deploy-context counterpart of the build-context OpEmit leg (tasks.go invokeVerbBuildEmit), reusing the SAME spec.DeployReply / ReverseOp wire as the external deploy TARGET (candy/plugin-bundle/deploy_target.go, S3b — was charly core's deploy_target_external.go before the deploy-dispatch cluster moved) — R3.
- EmitOCI (a BUILD venue — the pod-overlay Containerfile): Invoke(OpEmit) via the SHARED invokeVerbBuildEmit, splicing the plugin's Containerfile FRAGMENT verbatim — it cannot deploy-execute at build (no live venue). Identical behaviour to the box-build path (writeCandySteps → emitTasks `case "plugin"`), just relocated. EmitOCI is the ONLY in-proc Emit* this provider still implements.
Reverse() is static-nil: the reversal ops are DYNAMIC — they come from the plugin's OpExecute reply at emit time, recorded by the host's RunHostStep handler into the CandyRecord (the externalDeployTarget record-and-replay pattern).
func (*ExternalPluginStep) Kind ¶ added in v0.2026210.2308
func (s *ExternalPluginStep) Kind() StepKind
func (*ExternalPluginStep) RequiresGate ¶ added in v0.2026210.2308
func (s *ExternalPluginStep) RequiresGate() Gate
RequiresGate is GateNone — an external plugin verb-step is operator-authorized build/deploy-time plugin execution (the candy author opted into it), mirroring the external deploy TARGET which is ungated; the plugin picks its own privilege via RunSystem/RunUser on the reverse channel.
func (*ExternalPluginStep) Reverse ¶ added in v0.2026210.2308
func (s *ExternalPluginStep) Reverse() []ReverseOp
Reverse is static-nil: the teardown ops are recorded DYNAMICALLY from the plugin's OpExecute DeployReply at emit time (EmitLocal/EmitVM append reply.ReverseOps to the CandyRecord), exactly like the external deploy target — never recomputed.
func (*ExternalPluginStep) Scope ¶ added in v0.2026210.2308
func (s *ExternalPluginStep) Scope() Scope
Scope classifies by the step's user context — the SAME rule OpStep uses (OpStepScope): root/empty → system, a named/numeric non-root user → user. The plugin then chooses RunSystem vs RunUser on the reverse channel per its own effect; this scope drives only the ledger reverse-op privilege bookkeeping.
func (*ExternalPluginStep) Venue ¶ added in v0.2026210.2308
func (s *ExternalPluginStep) Venue() Venue
type ExternalStep ¶ added in v0.2026210.2308
type ExternalStep struct {
Word string // the reserved step word; Kind() = "external:" + Word
ScopeV Scope // plugin-declared (StepContract.scope)
VenueV Venue // plugin-declared (StepContract.venue)
GateV Gate // plugin-declared (StepContract.gate) — the step SKIPs if the gate is not enabled
Payload json.RawMessage // opaque per-kind input — the OpExecute params
CandyName string // owning candy (provenance + the ledger CandyRecord key)
ReverseOps []ReverseOp // set DYNAMICALLY from the plugin's OpExecute reply (record-and-replay)
}
ExternalStep is an EXTERNAL, plugin-CONTRIBUTED install-step KIND: a step whose Kind() is "external:<word>", carried OPAQUELY (Payload) and whose Scope/Venue/Gate come from the serving class:step plugin's DECLARED StepContract. Its host EXECUTION funnels through the OpExecute-to-the-serving-plugin path; teardown ops are DYNAMIC (recorded from the OpExecute reply), so Reverse() returns the recorded slice.
func (*ExternalStep) Kind ¶ added in v0.2026210.2308
func (s *ExternalStep) Kind() StepKind
func (*ExternalStep) RequiresGate ¶ added in v0.2026210.2308
func (s *ExternalStep) RequiresGate() Gate
func (*ExternalStep) Reverse ¶ added in v0.2026210.2308
func (s *ExternalStep) Reverse() []ReverseOp
func (*ExternalStep) Scope ¶ added in v0.2026210.2308
func (s *ExternalStep) Scope() Scope
func (*ExternalStep) Venue ¶ added in v0.2026210.2308
func (s *ExternalStep) Venue() Venue
type FeatureEntity ¶
type FeatureEntity struct {
Kind string `yaml:"kind,omitempty" json:"kind"`
Name string `yaml:"name,omitempty" json:"name"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`
}
#FeatureEntity is one enumerated kind: entity + its RAW plan data (Step is already a plain CUE-sourced wire type, so no separate flattened form is needed on the wire). An entity with neither a description nor a plan is still listed (as "(no description)") but the plugin skips summarizing/validating it, matching the former engine.
type FeatureReply ¶
type FeatureReply struct {
Entities []FeatureEntity `yaml:"entities,omitempty" json:"entities,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
#FeatureReply is the "feature" HostBuild kind reply — the enumerated entities the plugin transforms (summary/steps/validation) and formats into the list/pending/validate output. Error is a human-facing message on a load failure.
type FeatureRequest ¶
type FeatureRequest struct {
Filter string `yaml:"filter,omitempty" 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 FileStep ¶ added in v0.2026210.2308
type FileStep struct {
Source string // path to source content (under layer dir or .build/_inline/)
Dest string // absolute destination path
Mode os.FileMode // permissions
Owner string // "root" or "uid:gid" or the invoking user's name
CandyName string // for ledger
}
FileStep places a single file. The compiler may emit these for candy-declared file directives that aren't wrapped as tasks (e.g. supervisord fragment assembly). Today's tasks.go handles most file placement via OpStep; FileStep exists for cases where the compiler synthesizes file writes that weren't in the candy manifest (e.g. service unit files, managed-block contents).
func (*FileStep) RequiresGate ¶ added in v0.2026210.2308
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 Gate ¶
type Gate string
Gate is the name of a CLI flag that must be enabled for a given step to run on the host target. Steps without a gate run unconditionally; steps with a gate are skipped (with a warning) unless the user opts in.
The OCI target ignores gates — container builds are already isolated, so gating would only slow down image construction without adding safety.
type GoogleAccount ¶
type GpuProbeInput ¶
type GpuProbeInput struct {
Action string `yaml:"action,omitempty" json:"action"`
Group int `yaml:"group,omitempty" json:"group,omitempty"`
}
#GpuProbeInput is the action-multiplexed input to verb:gpu over OpRun. Action selects the host probe. The three data tables (device_patterns/gpu_vendors/pci_class_labels) are now candy/plugin-gpu's OWN embedded data (K5 seam-death — they moved out of charly-core's embedded charly.yml alongside the "hostprobe" HostBuild kind's dissolution; plugin-gpu is the actual detection consumer, so it is the one data source, R3), so this input no longer threads them.
type GpuProbeReply ¶
type GpuProbeReply struct {
// "bool" is quoted (a bare `bool` field name collides with the CUE builtin
// type keyword — the arbiter.cue #ArbiterInvokeReply precedent).
Bool bool `yaml:"bool,omitempty" json:"bool,omitempty"`
Str string `yaml:"str,omitempty" json:"str,omitempty"`
Vfio *VFIOReport `yaml:"vfio,omitempty" json:"vfio,omitempty"`
HostDevices *DetectedDevices `yaml:"host_devices,omitempty" json:"host_devices,omitempty"`
MemlockSoft uint64 `yaml:"memlock_soft,omitempty" json:"memlock_soft,omitempty"`
MemlockHard uint64 `yaml:"memlock_hard,omitempty" json:"memlock_hard,omitempty"`
}
#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,omitempty" json:"name"`
Target string `yaml:"target,omitempty" json:"target"`
Base string `yaml:"base,omitempty" json:"base"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Vm string `yaml:"vm,omitempty" json:"vm,omitempty"`
}
#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 — the retag step emits both automatically, mirroring the json key.
func HolderAddrFor ¶ added in v0.2026213.1726
func HolderAddrFor(name string, node BundleNode) HolderAddr
HolderAddrFor derives the resource-arbiter holder address for a deploy-tree node — servable off a plain map[string]BundleNode (the shape both a freshly-loaded uf.Bundle and a resolved-project envelope's Deploy map carry).
type HolderDescriptor ¶
type HolderDescriptor struct {
Name string `yaml:"name,omitempty" json:"name"`
Holds []string `yaml:"holds,omitempty" json:"holds"`
Addr HolderAddr `yaml:"addr,omitempty" json:"addr"`
Restore string `yaml:"restore,omitempty" json:"restore"`
}
#HolderDescriptor is one candidate preemptible holder, pre-projected (PreemptionHolds() + HolderAddrFor() + PreemptEffectiveRestore(), sdk/deploykit's FilterPreemptibleHolders) so the plugin's holdersToStop is pure config-free coordination. Filled plugin-side now (K1-unblock wave 1, off the #ResolvedProject envelope) rather than by a bespoke host reply type.
type HostContext ¶ added in v0.2026213.1726
type HostContext struct {
// MachineVenue selects compilation mode (P9): false (zero value) → compile for a
// CONTAINER image build (the pod overlay / OCI target); true → compile for a MACHINE
// venue with a system init (a target:local / target:vm deploy — services render as
// systemd units, home is deferred via {{.Home}}). detectHostContext sets it true for a
// host deploy; the OCI/pod-overlay compile passes the zero value. It replaced the former
// string Target ("host"/"vm"/"oci"), whose "vm" arm was dead (a vm deploy compiles with
// the host detectHostContext) — the machine-vs-container distinction IS the trait.
MachineVenue bool
// Distro is the resolved host distro tag, e.g. "fedora:43". Used to
// pick the right format section when compiling for a host target
// whose distro differs from the image's primary distro.
Distro string
// GlibcVersion is the host's glibc major.minor as reported by
// `ldd --version`. Used by the host target's preflight check against
// the selected builder image. Optional; empty means skip the check.
GlibcVersion string
// BuilderImage overrides the default builder-image selection for
// VenueContainerBuilder steps. Populated from --builder-image. ""
// means "use the embedded build vocabulary's default".
BuilderImage string
// BuilderContext carries the host-side build PRE-PASS result: each externalized
// detection-builder's per-candy stage context + teardown ops, keyed by
// BuilderCtxKey(candy, builder). Populated by preresolveBuilderContexts BEFORE
// this pure compile (the deploy command path); read by collectBuilderContext +
// compileBuilderSteps so the compiler NEVER dials a builder plugin (purity). Nil
// when no pre-pass ran (a direct BuildDeployPlan caller / test) or no externalized
// builder is triggered → the affected builder gets base-only context, no teardown.
// `json:"-"`: IN-PROCESS-ONLY, populated plugin-side after the HostContextJSON decode —
// never crosses the wire (see the file doc + spec.BuilderPreresolved).
BuilderContext map[string]BuilderPreresolved `json:"-"`
// ActiveInitName/ActiveInit carry the MachineVenue's preresolved active init system —
// resolved ONCE per whole-deploy compile plugin-side off the resolved-project envelope's
// rp.Init (candy/plugin-bundle/compile.go). compileServiceSteps reads these instead of
// re-deriving the active init per-candy or guessing via a container-oriented auto-detect
// heuristic (which cannot disambiguate a machine venue's init from a plain custom-exec
// service entry — proven live 2026-07-20). Nil/empty for a direct BuildDeployPlan caller /
// test that compiles outside the seam; compileServiceSteps falls back to its own lazy
// per-call lookup then. `json:"-"`: IN-PROCESS-ONLY, populated plugin-side — never crosses.
ActiveInitName string `json:"-"`
ActiveInit *ResolvedInit `json:"-"`
}
host_context.go — the deploy-compile HOST CONTEXT value type, promoted from sdk/deploykit (#55 K4 import-purity). A hand-written spec value carrier, NOT a CUE wire type: it crosses the plugin boundary ONLY as the opaque `host_context: bytes` RawBody payload on #DeployCompileRequest (the sanctioned VmJSON/PodConfigJSON idiom), so CUE never decodes it at the wire. Its WIRE form is the 4 host-computed SCALARS below (MachineVenue/Distro/GlibcVersion/ BuilderImage); the remaining fields (BuilderContext/ActiveInitName/ActiveInit) are `json:"-"` IN-PROCESS-ONLY — populated PLUGIN-SIDE after the HostContextJSON decode (candy/plugin-bundle's preresolveBuilderContexts over the reverse channel + the rp.Init active-init resolve), never crossing. The `cue exp gengotypes` spike is the SDD arbiter: it CANNOT express the `json:"-"` intent (it emits `,omitempty`, which would wrongly serialize the in-process fields into the wire blob) and it embeds the already-hand spec.BuilderPreresolved (opaque map[string]any Context) — so hand-written is SPIKE-JUSTIFIED per the SDD mandate, mirroring its child BuilderPreresolved. sdk/deploykit keeps a `type HostContext = spec.HostContext` forwarder so the deploy-compile mechanism (BuildDeployPlan + the compile helpers) + candy/plugin-bundle compile unchanged.
type HostEnv ¶
type HostEnv struct {
CharlyBin string `yaml:"charly_bin,omitempty" json:"charly_bin,omitempty"`
Home string `yaml:"home,omitempty" json:"home,omitempty"`
// version is the host charly's CalVer (CharlyVersion()) — the
// delivery-decision authority for EnsureCharlyInGuest.
Version string `yaml:"version,omitempty" 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 and the host home.
type ImageUserInput ¶
type ImageUserInput struct {
Ref string `yaml:"ref,omitempty" json:"ref"`
UID int `yaml:"uid,omitempty" json:"uid"`
}
#ImageUserInput — inspect a remote image's /etc/passwd for the user with the given uid (the build engine's base-image adopt-user probe).
type ImportEntry ¶ added in v0.2026210.808
type ImportEntry struct {
Namespace string // "" = flat import into the current root namespace
Ref string // local path or `@host/org/repo[/sub/path]:version`
}
ImportEntry is one parsed `import:` list item. A flat entry (Namespace == "") merges the referenced file into the current root namespace; a namespaced entry mounts the referenced project under Namespace.
type ImportList ¶ added in v0.2026210.808
type ImportList []ImportEntry
ImportList is the `import:` field type. Custom YAML decoding accepts a list whose items are either a bare string (flat) or a single-key mapping `alias: ref` (namespaced child import).
func (ImportList) MarshalYAML ¶ added in v0.2026210.808
func (il ImportList) MarshalYAML() (any, error)
MarshalYAML emits each entry compactly: a flat entry as a scalar string, a namespaced entry as a single-key `alias: ref` map — the same shapes UnmarshalYAML accepts (round-trip safe; used by migrators that write configs).
func (*ImportList) UnmarshalYAML ¶ added in v0.2026210.808
func (il *ImportList) UnmarshalYAML(node *yaml.Node) error
UnmarshalYAML decodes the mixed-shape import list.
type Incident ¶
type Incident struct {
ID UUIDv7 `yaml:"id,omitempty" json:"id"`
RunID UUIDv7 `yaml:"run_id,omitempty" json:"run_id,omitempty"`
State string `yaml:"state,omitempty" json:"state"`
Summary string `yaml:"summary,omitempty" json:"summary"`
CreatedAt string `yaml:"created_at,omitempty" json:"created_at"`
EvidenceRefs []string `yaml:"evidence_refs,omitempty" json:"evidence_refs,omitempty"`
}
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 InitConfig ¶ added in v0.2026213.1726
type InitConfig struct {
Init map[string]*ResolvedInit `yaml:"init,omitempty" json:"init"`
}
#InitConfig is the `init:` build-vocabulary container — the whole init map keyed by init-system name (charly/charly.yml's `init:` section). #55 Cluster-B: CUE-sourced (completing #72, which CUE-sourced its siblings #DistroConfig / #Builder but left InitConfig hand-written) so every charly/plugin file that uses ONLY this value type reaches it over spec+proto — the import-purity lever. Its detection / resolution methods (DetectCandyInit / ResolveInitSystem / ActiveInit / InitNames) are pure Go METHODS CUE cannot express and stay hand-written in spec/init_config_methods.go (mirrors #DistroConfig's methods in spec/distro_config_methods.go: a method, not a type). This is NOT a wire type — it is never marshaled across the host↔plugin boundary; it mirrors the authored `init:` config surface, so SDD CUE-sources it exactly as #DistroConfig.
func ProjectInitConfig ¶ added in v0.2026213.1726
func ProjectInitConfig(uf *UnifiedFile, resolveInit func(json.RawMessage) (*ResolvedInit, error)) *InitConfig
ProjectInitConfig reconstructs the *InitConfig (init: section) from uf, resolving each opaque `init` body via the caller-supplied resolveInit callback (the init plugin's OpResolve config leg). Nil when no init systems are configured.
func (*InitConfig) ActiveInit ¶ added in v0.2026213.1726
func (ic *InitConfig) ActiveInit(layers map[string]CandyReader, candyOrder []string) map[string]*ResolvedInit
ActiveInit returns all init systems that are active for the given image. An image can have multiple active inits (e.g., supervisord + systemd on bootc-flavored compositions).
func (*InitConfig) DetectCandyInit ¶ added in v0.2026213.1726
func (ic *InitConfig) DetectCandyInit(ly *CandyYAML, candyPath string) []string
DetectCandyInit returns which init system names a candy triggers, based on its candy manifest fields and file patterns.
func (*InitConfig) InitNames ¶ added in v0.2026213.1726
func (ic *InitConfig) InitNames() []string
InitNames returns a sorted list of all init system names.
func (*InitConfig) ResolveInitSystem ¶ added in v0.2026213.1726
func (ic *InitConfig) ResolveInitSystem(layers map[string]CandyReader, candyOrder []string, explicit string) (string, *ResolvedInit)
ResolveInitSystem determines the active init system for an image. Priority: explicit override → auto-detect from candies. Returns ("", nil) if no init system is needed.
Candy capability requirements (RequiresCapabilities) are checked against the aggregated candy caps for the composition; init systems whose requirements aren't met are filtered out. The aggregated caps are also consulted for the bootc-prefer-systemd heuristic via PreserveUser (the canonical signal that this is a bootc-flavored composition).
type InitResolveInput ¶
type InitResolveInput struct {
Init RawBody `yaml:"init,omitempty" json:"init"`
}
#InitResolveInput is candy/plugin-init's OpResolve config-leg input: the opaque init body to resolve into a ResolvedInit.
type InitResolveReply ¶
type InitResolveReply struct {
Resolved *ResolvedInit `yaml:"resolved,omitempty" json:"resolved,omitempty"`
}
#InitResolveReply wraps the resolved init.
type InitResolveRequest ¶
type InitResolveRequest struct {
Render *ServiceRenderInput `yaml:"render,omitempty" json:"render,omitempty"`
Config *InitResolveInput `yaml:"config,omitempty" json:"config,omitempty"`
}
#InitResolveRequest is the discriminated OpResolve input for candy/plugin-init: exactly one of Render (leg 1 — render one service unit) or Config (legs 2–4 — the resolved init envelope) is set.
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 InlineBuilderParams ¶ added in v0.2026213.1726
type InlineBuilderParams struct {
Dir string `yaml:"dir,omitempty" json:"dir"`
BoxName string `yaml:"box_name,omitempty" json:"box_name"`
CandyName string `yaml:"candy_name,omitempty" json:"candy_name"`
BuilderName string `yaml:"builder_name,omitempty" json:"builder_name"`
BDef *Builder `yaml:"b_def,omitempty" json:"b_def,omitempty"`
Ctx *BuildStageContext `yaml:"ctx,omitempty" json:"ctx,omitempty"`
}
#InlineBuilderParams carries the inputs to the host resolveInlineBuilderSeam for the "inline-builder" render-seam method (RenderSeamInlineBuilder). It rides INSIDE the opaque #RenderSeamRequest.params bytes (marshalled by candy/plugin-build's render, decoded by the host builder). BDef is #Builder, Ctx is #BuildStageContext — both existing CUE defs, so gengotypes generates typed pointers (*Builder / *BuildStageContext).
type InlineBuilderResult ¶ added in v0.2026213.1726
type InlineBuilderResult struct {
Fragment string `yaml:"fragment,omitempty" json:"fragment,omitempty"`
}
#InlineBuilderResult carries the resolved inline fragment back to the render.
type InlineCandy ¶ added in v0.2026210.808
type InlineCandy struct {
From string `yaml:"from,omitempty" json:"from,omitempty"`
CandyYAML `yaml:",inline"`
// Manifest carries the discovery manifest filename for a `From:` directory so the candy
// scan reads the right file. Not YAML-authored; carried through the opaque candy-map fold
// via JSON, hence exported + json-tagged.
Manifest string `yaml:"-" json:"__manifest,omitempty"`
}
InlineCandy is a candy declared inline in the unified file's `candy:` map. Mutually exclusive options: `from:` points at a directory to scan (the loader's CandyScanner seam), OR the inline body defines the candy (same fields as the candy manifest, flattened via yaml:",inline").
func DecodeInlineCandy ¶ added in v0.2026210.808
func DecodeInlineCandy(raw json.RawMessage) (*InlineCandy, bool)
DecodeInlineCandy decodes one opaque layer body into the InlineCandy loader shape.
type InstallContext ¶
type InstallContext struct {
CacheMounts []CacheMount `yaml:"cache_mounts,omitempty" json:"cache_mounts,omitempty"`
Packages []string `yaml:"packages,omitempty" json:"packages,omitempty"`
Repos []map[string]any `yaml:"repos,omitempty" json:"repos,omitempty"`
Options []string `yaml:"options,omitempty" json:"options,omitempty"`
Copr []string `yaml:"copr,omitempty" json:"copr,omitempty"`
Modules []string `yaml:"modules,omitempty" json:"modules,omitempty"`
Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty"`
Keys []string `yaml:"keys,omitempty" json:"keys,omitempty"`
StageName string `yaml:"stage_name,omitempty" json:"stage_name,omitempty"`
BuilderRef string `yaml:"builder_ref,omitempty" json:"builder_ref,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"`
Home string `yaml:"home,omitempty" json:"home,omitempty"`
}
#InstallContext — data a distro format's install/prepare/cleanup template renders against: packages + repo/copr/module/exclude/key modifiers, resolved cache mounts, and the builder-stage identity/uid/gid/home.
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 InstallPlan ¶
type InstallPlan struct {
// Identity — populated by the compiler.
DeployID string // per-deploy unique ID (hash of image + add_candy list)
Box string // deployable box name (or candy name for single-candy deploys)
Version string // candy/box CalVer version
Distro string // resolved host distro tag, e.g. "fedora:43"
Candy string // candy name when this plan is for a single candy; "" for whole-image merges
// The ordered step sequence.
Steps []InstallStep
// Provenance — used by teardown and status.
CandiesIncluded []string // ordered layer names this plan composes (for whole-image merges)
AddCandies []string // layers added on top via charly.yml add_layers: (for provenance)
BuilderImage string // selected builder image for VenueContainerBuilder steps
Meta map[string]string // free-form metadata (builder image, glibc version, …)
}
InstallPlan is the full ordered list of steps for one candy or one whole-image deploy. Compiled by the deploykit compiler and consumed by any DeployTarget implementation.
The compiler produces one InstallPlan per candy (then merges them in topological order for whole-image deploys). A whole-image deploy keeps candy boundaries visible so the ledger can refcount which candies participate.
func PlanFromView ¶ added in v0.2026210.2308
func PlanFromView(v InstallPlanView) (*InstallPlan, error)
PlanFromView re-materializes the rich in-core *InstallPlan from its JSON-roundtrippable InstallPlanView wire form — the REVERSE of WireView, used by the host to reconstruct []*InstallPlan from the command:bundle plugin's OpCompile reply (K4-B). Steps round-trip through the SINGLE stepsFromView converter (install_step_view.go), already proven round-trip-faithful by TestStepView_RoundTrip. The host re-materialized plan is byte-equivalent to the former in-proc compile output (the K4-B parity golden proves it via DeepEqual against the OLD host-compile path).
func (*InstallPlan) StepsByVenue ¶
func (p *InstallPlan) StepsByVenue() []StepBatch
StepsByVenue partitions the plan's steps by (Scope, Venue) tuple while preserving intra-partition order. Host target emission uses this to batch contiguous same-(scope, venue) runs into one heredoc. Not used by the OCI target (it walks Steps directly). Uses only the InstallStep interface (Scope()/Venue()), so it belongs with the envelope.
type InstallPlanView ¶
type InstallPlanView struct {
DeployID string `yaml:"deploy_id,omitempty" json:"deploy_id,omitempty"`
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
Distro string `yaml:"distro,omitempty" json:"distro,omitempty"`
Candy string `yaml:"candy,omitempty" json:"candy,omitempty"`
CandiesIncluded []string `yaml:"candies_included,omitempty" json:"candies_included,omitempty"`
AddCandies []string `yaml:"add_candies,omitempty" json:"add_candies,omitempty"`
BuilderImage string `yaml:"builder_image,omitempty" json:"builder_image,omitempty"`
Meta map[string]string `yaml:"meta,omitempty" 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.
Steps []InstallStepView `yaml:"steps,omitempty" json:"steps,omitempty"`
}
#InstallPlanView is the JSON-roundtrippable wire VIEW of an InstallPlan.
func WireView ¶ added in v0.2026210.2308
func WireView(p *InstallPlan) InstallPlanView
WireView projects the rich in-core InstallPlan onto the JSON-roundtrippable InstallPlanView the host marshals into an external deploy/step provider's op.Params. The Steps interface slice round-trips through the SINGLE StepsToView / stepsFromView converter (install_step_view.go) — an external deploy/step plugin walks the same ordered step IR the in-proc DeployTargets walk and EXECUTES it on the venue (R3; proven by the step-IR round-trip test). The remaining fields are identity + provenance.
A free function (not a method on InstallPlan) because it type-switches the concrete step vocabulary via StepsToView.
type InstallStep ¶
type InstallStep interface {
// Kind returns the step's concrete type discriminator.
Kind() StepKind
// Scope classifies where the effect lands on the target filesystem.
Scope() Scope
// Venue classifies where the commands physically execute.
Venue() Venue
// RequiresGate names the opt-in flag that must be enabled, or
// GateNone if the step can run unconditionally. Only consulted by
// host-target emission; the OCI target ignores gates.
RequiresGate() Gate
// Reverse returns the teardown actions this step contributes to the
// ledger. Called at install time (not at teardown) so the ledger
// captures the exact reversal actions tied to the specific artifacts
// created. Empty return value means no reversal is recorded (e.g.
// phases that leave no state).
Reverse() []ReverseOp
}
InstallStep is one polymorphic step in an InstallPlan. Every concrete step kind (SystemPackages, Builder, Op, File, …) implements it; a DeployTarget walks a plan's steps and dispatches on the returned discriminators.
func StepFromView ¶ added in v0.2026210.2308
func StepFromView(v InstallStepView) (InstallStep, error)
StepFromView reconstructs the concrete InstallStep a view projects. An unknown Kind is a hard error (the wire carried a step kind this charly does not know — a genuine version/contract breach, never silently dropped).
type InstallStepView ¶
type InstallStepView struct {
Kind string `yaml:"kind,omitempty" json:"kind"`
// Derived ADVISORY fields (Scope()/Venue()/RequiresGate() results).
Scope Scope `yaml:"scope,omitempty" json:"scope,omitempty"`
Venue int `yaml:"venue,omitempty" json:"venue,omitempty"`
Gate string `yaml:"gate,omitempty" json:"gate,omitempty"`
// payload is the OPAQUE per-kind input for an EXTERNAL (plugin-contributed)
// step kind.
Payload RawBody `yaml:"payload,omitempty" json:"payload,omitempty"`
// reverse_ops is the step's host-computed teardown ops.
ReverseOps []ReverseOp `yaml:"reverse_ops,omitempty" json:"reverse_ops,omitempty"`
// Shared identity / provenance.
CandyName string `yaml:"candy_name,omitempty" json:"candy_name,omitempty"`
CandyDir string `yaml:"candy_dir,omitempty" json:"candy_dir,omitempty"`
// SystemPackagesStep + RepoChangeStep + LocalPkgInstallStep.
Format string `yaml:"format,omitempty" json:"format,omitempty"`
// SystemPackagesStep + BuilderStep three-phase tag (int Phase: prepare/install/cleanup).
Phase int `yaml:"phase,omitempty" json:"phase,omitempty"`
// SystemPackagesStep.
Packages []string `yaml:"packages,omitempty" json:"packages,omitempty"`
Repos []map[string]any `yaml:"repos,omitempty" json:"repos,omitempty"`
Options []string `yaml:"options,omitempty" json:"options,omitempty"`
Copr []string `yaml:"copr,omitempty" json:"copr,omitempty"`
Modules []string `yaml:"modules,omitempty" json:"modules,omitempty"`
Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty"`
Keys []string `yaml:"keys,omitempty" json:"keys,omitempty"`
CacheMount []CacheMountView `yaml:"cache_mount,omitempty" json:"cache_mount,omitempty"`
RawInstallContext map[string]any `yaml:"raw_install_context,omitempty" json:"raw_install_context,omitempty"`
// BuilderStep.
Builder string `yaml:"builder,omitempty" json:"builder,omitempty"`
BuilderImage string `yaml:"builder_image,omitempty" json:"builder_image,omitempty"`
Artifacts []ArtifactView `yaml:"artifacts,omitempty" json:"artifacts,omitempty"`
RawStageContext map[string]any `yaml:"raw_stage_context,omitempty" json:"raw_stage_context,omitempty"`
BuilderDef *BuilderDef `yaml:"builder_def,omitempty" json:"builder_def,omitempty"`
// local_pkg is shared by BuilderStep (aur) + LocalPkgInstallStep.
LocalPkg *LocalPkg `yaml:"local_pkg,omitempty" json:"local_pkg,omitempty"`
// OpStep + ExternalPluginStep (Op + the shared user/ctx/distro fields).
Op *Op `yaml:"op,omitempty" json:"op,omitempty"`
CtxPath string `yaml:"ctx_path,omitempty" json:"ctx_path,omitempty"`
ResolvedUser string `yaml:"resolved_user,omitempty" json:"resolved_user,omitempty"`
To string `yaml:"to,omitempty" json:"to,omitempty"`
CandyVars map[string]string `yaml:"candy_vars,omitempty" json:"candy_vars,omitempty"`
Distros []string `yaml:"distros,omitempty" json:"distros,omitempty"`
// FileStep.
Source string `yaml:"source,omitempty" json:"source,omitempty"`
Dest string `yaml:"dest,omitempty" json:"dest,omitempty"`
Mode uint32 `yaml:"mode,omitempty" json:"mode,omitempty"`
Owner string `yaml:"owner,omitempty" json:"owner,omitempty"`
// ServicePackagedStep + ServiceCustomStep.
Unit string `yaml:"unit,omitempty" json:"unit,omitempty"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
TargetScope Scope `yaml:"target_scope,omitempty" json:"target_scope,omitempty"`
Enable bool `yaml:"enable,omitempty" json:"enable,omitempty"`
OverridesText string `yaml:"overrides_text,omitempty" json:"overrides_text,omitempty"`
OverridesPath string `yaml:"overrides_path,omitempty" json:"overrides_path,omitempty"`
PriorEnabled bool `yaml:"prior_enabled,omitempty" json:"prior_enabled,omitempty"`
UnitText string `yaml:"unit_text,omitempty" json:"unit_text,omitempty"`
UnitPath string `yaml:"unit_path,omitempty" json:"unit_path,omitempty"`
// ShellHookStep.
EnvVars map[string]string `yaml:"env_vars,omitempty" json:"env_vars,omitempty"`
PathAdd []string `yaml:"path_add,omitempty" json:"path_add,omitempty"`
EnvFile string `yaml:"env_file,omitempty" json:"env_file,omitempty"`
// ShellSnippetStep.
Origin string `yaml:"origin,omitempty" json:"origin,omitempty"`
Shell string `yaml:"shell,omitempty" json:"shell,omitempty"`
Snippet string `yaml:"snippet,omitempty" json:"snippet,omitempty"`
PathAppend []string `yaml:"path_append,omitempty" json:"path_append,omitempty"`
Destination string `yaml:"destination,omitempty" json:"destination,omitempty"`
Marker string `yaml:"marker,omitempty" json:"marker,omitempty"`
UseDropin bool `yaml:"use_dropin,omitempty" json:"use_dropin,omitempty"`
Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
// RepoChangeStep.
File string `yaml:"file,omitempty" json:"file,omitempty"`
Content string `yaml:"content,omitempty" json:"content,omitempty"`
Checksum string `yaml:"checksum,omitempty" json:"checksum,omitempty"`
// ApkInstallStep.
ApkPackages []ApkPackageSpec `yaml:"apk_packages,omitempty" json:"apk_packages,omitempty"`
// LocalPkgInstallStep.
PkgbuildRef string `yaml:"pkgbuild_ref,omitempty" json:"pkgbuild_ref,omitempty"`
ProjectDir string `yaml:"project_dir,omitempty" json:"project_dir,omitempty"`
}
#InstallStepView is the JSON-roundtrippable wire form of ONE InstallStep — a SUPERSET struct: each kind populates the subset of fields it carries, all optional so the wire stays compact.
func StepToView ¶ added in v0.2026210.2308
func StepToView(step InstallStep) InstallStepView
StepToView projects one concrete InstallStep onto its serializable wire view.
func StepsToView ¶ added in v0.2026210.2308
func StepsToView(steps []InstallStep) []InstallStepView
StepsToView / stepsFromView convert a whole ordered slice. Order is preserved (the InstallPlan's step ordering is load-bearing — see ResolveHome / StepsByVenue).
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 ¶
NotesEnabled reports whether the iteration NOTES.md memory is on (default on).
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 K8sDeployVenue ¶
type K8sDeployVenue struct {
OverlayPath string `yaml:"overlay_path,omitempty" json:"overlay_path"`
TreeRoot string `yaml:"tree_root,omitempty" json:"tree_root,omitempty"`
KubeContext string `yaml:"kube_context,omitempty" json:"kube_context,omitempty"`
DeployName string `yaml:"deploy_name,omitempty" json:"deploy_name,omitempty"`
}
#K8sDeployVenue is the preresolved deploy:k8s substrate payload the host's k8s deploy preresolver produces in DeployVenue.Substrate and the candy/plugin-kube deploy:k8s provider decodes.
type K8sGatewayAPI ¶
type K8sGenFile ¶
type K8sGenFile struct {
RelPath string `yaml:"rel_path,omitempty" json:"rel_path"`
Doc RawBody `yaml:"doc,omitempty" json:"doc"`
EgressKind string `yaml:"egress_kind,omitempty" 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 `yaml:"deployment_name,omitempty" json:"deployment_name"`
Instance string `yaml:"instance,omitempty" json:"instance"`
ImageRef string `yaml:"image_ref,omitempty" json:"image_ref"`
Deploy Deploy `yaml:"deploy,omitempty" json:"deploy"`
// cluster is the decoded kind:k8s cluster template. After the k8s
// substrate-value de-type (Cutover K) the KERNEL no longer sets it — it
// ships the opaque body in ClusterRaw and the plugin decodes ClusterRaw
// into Cluster before generating, so the kernel never types spec.K8s.
Cluster K8s `yaml:"cluster,omitempty" json:"cluster,omitempty"`
ClusterRaw RawBody `yaml:"cluster_raw,omitempty" json:"cluster_raw,omitempty"`
Ports []string `yaml:"ports,omitempty" json:"ports"`
UID int `yaml:"uid,omitempty" json:"uid"`
GID int `yaml:"gid,omitempty" json:"gid"`
OutputDir string `yaml:"output_dir,omitempty" json:"output_dir"`
}
#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 `yaml:"overlay_rel_path,omitempty" json:"overlay_rel_path"`
Files []K8sGenFile `yaml:"files,omitempty" 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 K8sGenerateKustomizeRequest ¶
type K8sGenerateKustomizeRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
ImageRef string `yaml:"image_ref,omitempty" json:"image_ref"`
Node *Deploy `yaml:"node,omitempty" json:"node"`
CapsJSON RawBody `yaml:"caps,omitempty" json:"caps"`
ClusterJSON RawBody `yaml:"cluster,omitempty" json:"cluster"`
OutputDir string `yaml:"output_dir,omitempty" json:"output_dir,omitempty"`
}
#K8sGenerateKustomizeRequest/#K8sGenerateKustomizeReply — the request/reply shape candy/plugin-kube's materializeKustomize (materialize.go) takes/returns. The former "k8s-generate-kustomize" HostBuild seam this type pair used to cross (FINAL/K5 unit 6a) is RETIRED (K5-A item 6): the egress-validated Kustomize GENERATION now runs ENTIRELY plugin-side — verb:k8sgen + verb:egress reached peer-to-peer via InvokeProvider, disk I/O done directly by the plugin, no host round trip left — so this pair now travels as a plain Go function signature (materializeKustomize's params/return), not a wire envelope. Both callers (candy/plugin-kube/preresolve.go's deploy:k8s preresolve, which self-loads the cluster template + image ref/capabilities itself now too — K-wave W3a A3-phase-2 — and candy/plugin-bundle/deploy_from_box.go's source-less from-box path) construct it directly. Cluster/Capabilities ride opaque (the established RawBody idiom this file uses throughout for hand-written host-side types with no CUE def — e.g. CapsJSON/ClusterJSON in this very def below).
type K8sHostname ¶
type K8sImagesDefaults ¶
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 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 K8sResolveInput ¶
type K8sResolveInput struct {
K8s RawBody `yaml:"k8s,omitempty" json:"k8s"`
}
#K8sResolveInput carries one opaque k8s cluster template body to project.
type K8sResolveReply ¶
type K8sResolveReply struct {
Resolved *ResolvedK8s `yaml:"resolved,omitempty" json:"resolved,omitempty"`
}
#K8sResolveReply wraps the resolved k8s cluster template.
type K8sResourceDefaults ¶
type K8sResourceValues ¶
type K8sResources ¶
type K8sResources struct {
Requests K8sResourceValues `yaml:"requests,omitempty" json:"requests,omitempty"`
Limits K8sResourceValues `yaml:"limits,omitempty" json:"limits,omitempty"`
}
type K8sSecretsBackend ¶
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 KeyValue ¶
type KeyValue = EnvKV
--- wire sub-types renamed to dodge the schemagen kindValueDefs collision (a top-level `#<X>Value` CUE def auto-registers as a kind-value gate; see schema/init.cue's #EnvKV doc comment for the RDD-caught mechanism) ---
func SortedEnvList ¶ added in v0.2026213.1726
SortedEnvList returns env as a deterministically-ordered []KeyValue (template iteration order — a Go map has none of its own).
type LabelDataEntry ¶
type LabelDataEntry struct {
Volume string `yaml:"volume,omitempty" json:"volume"`
Staging string `yaml:"staging,omitempty" json:"staging"`
Candy string `yaml:"candy,omitempty" json:"candy"`
Dest string `yaml:"dest,omitempty" json:"dest,omitempty"`
}
#LabelDataEntry — a data mapping in ai.opencharly.data (volume/staging/candy required).
type LabelDescriptionSet ¶
type LabelDescriptionSet struct {
Candy []LabeledDescription `yaml:"candy,omitempty" json:"candy,omitempty"`
Box []LabeledDescription `yaml:"box,omitempty" json:"box,omitempty"`
Deploy []LabeledDescription `yaml:"deploy,omitempty" json:"deploy,omitempty"`
}
#LabelDescriptionSet — the three-section (candy/box/deploy) description manifest (ai.opencharly.description). kit.LabelDescriptionSet aliases this.
func (*LabelDescriptionSet) IsEmpty ¶
func (s *LabelDescriptionSet) IsEmpty() bool
IsEmpty reports whether the set carries no labeled descriptions in any layer.
type LabelRouteEntry ¶
type LabelRouteEntry struct {
Host string `yaml:"host,omitempty" json:"host"`
Port int `yaml:"port,omitempty" json:"port"`
}
#LabelRouteEntry — a traefik route in ai.opencharly.route (host+port both required).
type LabelSecretEntry ¶
type LabelSecretEntry struct {
Name string `yaml:"name,omitempty" json:"name"`
Target string `yaml:"target,omitempty" json:"target"`
Env string `yaml:"env,omitempty" json:"env,omitempty"`
}
#LabelSecretEntry — a secret requirement in ai.opencharly.secret (name+target required).
type LabelShellSet ¶
type LabelShellSet struct {
Candy []ShellEntry `yaml:"candy,omitempty" json:"candy,omitempty"`
Box []ShellEntry `yaml:"box,omitempty" json:"box,omitempty"`
Deploy []ShellEntry `yaml:"deploy,omitempty" json:"deploy,omitempty"`
}
#LabelShellSet — the three-section shell-init manifest (ai.opencharly.shell).
type LabelVolumeEntry ¶
type LabelVolumeEntry struct {
Name string `yaml:"name,omitempty" json:"name"`
Path string `yaml:"path,omitempty" json:"path"`
}
#LabelVolumeEntry — a volume in the ai.opencharly.volume label (name+path both required).
type LabeledDescription ¶
type LabeledDescription struct {
Origin string `yaml:"origin,omitempty" json:"origin"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`
}
#LabeledDescription — one origin's plan-shaped self-description (MARSHALED into ai.opencharly.description). origin is REQUIRED (json:"origin", no omitempty). kit aliases it.
type LibvirtAudio ¶
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 LibvirtCPUFeature ¶
type LibvirtCPUTopology ¶
type LibvirtCPUTune ¶
type LibvirtCPUTune struct {
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 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 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 ¶
func (ll LibvirtGraphicsListeners) First() LibvirtGraphicsListen
First returns the first listener, or the zero value if empty.
func (*LibvirtGraphicsListeners) UnmarshalJSON ¶
func (ll *LibvirtGraphicsListeners) UnmarshalJSON(data []byte) error
UnmarshalJSON accepts the three interchangeable listen shapes on the JSON read path (the opaque substrate-decode path a de-typed vm body takes): a bare scalar address ("127.0.0.1" → one type:address listener), a single object, or a list of objects. The YAML read path decodes the same three via cue.Value.Decode running THIS method (the Matcher/PortScope self-decoding strategy), so ONE decoder now serves both paths (R3) — replacing the former charly-side expandLibvirtListenersNode YAML expander.
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 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 LibvirtIOThreadPin ¶
type LibvirtInput ¶
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 LibvirtMemBalloon ¶
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 LibvirtMemoryBacking ¶
type LibvirtMemoryBacking struct {
Hugepages *LibvirtHugepages `yaml:"hugepages,omitempty" json:"hugepages,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 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 LibvirtParallel ¶
type LibvirtPortForward ¶
type LibvirtRNG ¶
type LibvirtRedirDev ¶
type LibvirtResource ¶
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 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 LibvirtSound ¶
type LibvirtSound struct {
Model string `yaml:"model,omitempty" json:"model"`
}
type LibvirtSpinlocks ¶
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 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 LibvirtURI ¶ added in v0.2026213.1726
type LibvirtURI struct {
// Mode is "session" (currently the only supported value).
Mode string
// Remote is the SSH target when scheme is qemu+ssh://. Zero
// value means local (qemu:///session).
Remote SSHTarget
// Raw is the original URI string for error messages.
Raw string
}
LibvirtURI is the parsed form of an `charly --uri …` value.
func ParseLibvirtURI ¶ added in v0.2026213.1726
func ParseLibvirtURI(s string) (LibvirtURI, error)
ParseLibvirtURI handles the four recognized shapes. Empty / missing URI returns a local-session value; invalid URIs return an error.
func (LibvirtURI) IsLocal ¶ added in v0.2026213.1726
func (u LibvirtURI) IsLocal() bool
IsLocal reports whether the URI targets the local libvirt session. Equivalent to len(u.Remote.Host) == 0.
type LibvirtUSB ¶
type LibvirtVCPUPin ¶
type LibvirtVendorID ¶
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 LibvirtWatchdog ¶
type LifecycleOpts ¶
type LifecycleOpts struct {
DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,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"`
WithServices bool `yaml:"with_services,omitempty" json:"with_services,omitempty"`
AssumeYes bool `yaml:"assume_yes,omitempty" json:"assume_yes,omitempty"`
Verify bool `yaml:"verify,omitempty" json:"verify,omitempty"`
Pull bool `yaml:"pull,omitempty" json:"pull,omitempty"`
SkipIncompatible bool `yaml:"skip_incompatible,omitempty" json:"skip_incompatible,omitempty"`
BuilderImageOverride string `yaml:"builder_image_override,omitempty" 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. LifecycleOptsFromEmit (spec/deploy_methods.go) is the ONE hand-written converter — a pure function, not a type, so it stays hand-written.
func LifecycleOptsFromEmit ¶
func LifecycleOptsFromEmit(o EmitOpts) LifecycleOpts
LifecycleOptsFromEmit projects an EmitOpts (spec/deploy_executor.go — a LIVE behaviour-contract type that cannot itself cross the wire, since EmitOpts.ParentExec carries a live DeployExecutor) onto its wire-safe LifecycleOpts subset — the ONE shared conversion the deploy-dispatch envelope (S3b, #DeployTargetDispatchRequest.opts_json) uses on BOTH sides: charly core (unified_targets.go's Add/Update) builds this BEFORE marshaling into OptsJSON — never marshal a raw EmitOpts directly — and candy/plugin-bundle decodes OptsJSON directly into a LifecycleOpts, so no plugin-side conversion is needed at all.
type LifecyclePrepareInput ¶
type LifecyclePrepareInput struct {
Entity string `yaml:"entity,omitempty" json:"entity"`
VM *ResolvedVm `yaml:"vm,omitempty" json:"vm,omitempty"`
SSHUser string `yaml:"ssh_user,omitempty" json:"ssh_user"`
SSHPort int `yaml:"ssh_port,omitempty" json:"ssh_port"`
Alias string `yaml:"alias,omitempty" json:"alias"`
SSHKeyPath string `yaml:"ssh_key_path,omitempty" json:"ssh_key_path"`
KnownHostsPath string `yaml:"known_hosts_path,omitempty" json:"known_hosts_path"`
StateDir string `yaml:"state_dir,omitempty" json:"state_dir"`
PriorState *VmDeployState `yaml:"prior_state,omitempty" json:"prior_state,omitempty"`
}
#LifecyclePrepareInput is the host-resolved DATA a vm substrate's OpPrepareVenue needs but cannot derive itself.
type LoadedDoc ¶
type LoadedDoc struct {
// directives is the RAW reserved-directive mapping serialized as YAML bytes (NOT the RawBody
// JSON the entity bodies use) — so the host replays the ORIGINAL mergeUnifiedDocs decode exactly
// (`yaml.Unmarshal(directives, &sub)`), honoring the custom YAML unmarshalers on import/discover.
Directives []byte `yaml:"directives,omitempty" json:"directives,omitempty"`
Project ParsedProject `yaml:"project,omitempty" json:"project,omitempty"`
SrcDir string `yaml:"srcDir,omitempty" json:"srcDir,omitempty"`
SrcLabel string `yaml:"srcLabel,omitempty" json:"srcLabel,omitempty"`
}
#LoadedDoc — one parsed document of a namespace's flattened file tree (root file OR a flat import), in merge order. `directives` is the RAW reserved-directive mapping bytes (version/repo/defaults/provides/discover) the host yaml-decodes into a sub *UnifiedFile before merging (import is consumed by the walk); `project` is the document's decomposed entity nodes; `srcDir` anchors the document's discover paths + labels diagnostics.
type LoadedProject ¶
type LoadedProject struct {
ID int64 `yaml:"id,omitempty" json:"id,omitempty"`
Docs []LoadedDoc `yaml:"docs,omitempty" json:"docs,omitempty"`
Discovered []DiscoveredManifest `yaml:"discovered,omitempty" json:"discovered,omitempty"`
Namespaces []*NamespaceMount `yaml:"namespaces,omitempty" json:"namespaces,omitempty"`
}
#LoadedProject — the whole WALK result of one project (root OR a mounted namespace): a stable per-walk id (so a namespaced cycle/diamond back-reference resolves to the SAME materialized *UnifiedFile), the ordered documents, the discovered manifests, and the mounted namespace subtrees. The host replays materialize+merge over this to reconstruct the typed *UnifiedFile (identical to the former in-line loadUnifiedInto), then applies the binary-embedded default vocab.
type LoaderExecutor ¶ added in v0.2026213.1726
type LoaderExecutor interface {
// LoaderThreaded returns the CURRENT registry-derived snapshot (recognized kinds / deploy
// substrates / DeployTraits / ExternalDeploySubstrates / …). Called FRESH at each DATA-seam
// invocation — NEVER cached at seam-build time — because the walk's connect-declared-kind pass
// mutates the registry BETWEEN seam construction and the post-walk validators.
LoaderThreaded() Threaded
// RunBootstrapPhase invokes every registered bootstrap-phase plugin on the raw root bytes,
// returning the (possibly transformed) bytes. A leg failure (e.g. a broken reverse-channel
// HostBuild round trip) is a hard error — never a silent no-op fallback to the raw bytes,
// which would let LoadUnified proceed on an un-bootstrapped root with zero visible signal.
RunBootstrapPhase(data []byte) ([]byte, error)
// WalkProject runs the kind-blind import/discover/namespace walk (the registered ProjectWalker,
// reached via the host's WalkSeams) → the generic LoadedProject envelope. The host #NodeDoc CUE
// gate (WalkSeams.GateDoc) runs INSIDE this walk.
WalkProject(dir string, rootData []byte) (LoadedProject, error)
// MaterializeLoadedProject replays the host's per-document/per-namespace MATERIALIZE + root-wins
// MERGE over the walk envelope (registry kind-decode via the registered Materializer).
MaterializeLoadedProject(lp *LoadedProject, merged *UnifiedFile, byID map[int64]*UnifiedFile) error
// ValidateAndroidDevices enforces the kind:android box⊻adb XOR — resolves android templates via
// the provider registry (host-coupled), so a leg, not a pure loaderkit move.
ValidateAndroidDevices(uf *UnifiedFile) error
// ValidatePreemptible validates preemptible / requires_exclusive / requires_shared across the
// deploy map, including the resource-vocabulary cross-check (resolves the resource plugin kind +
// vm/resource entities via the registry) — host-coupled, so a leg.
ValidatePreemptible(uf *UnifiedFile) error
}
LoaderExecutor is the typed host-leg contract for the registry-/host-coupled loader steps the kind-blind LoadUnified orchestration cannot do itself: the bootstrap-phase plugin invocation, the registry-coupled import/discover walk, the materialize kind-decode + merge, and the two registry-resolving validators. Promoted here from sdk/loaderkit (#55 loader-keystone) so charly core can hold the host LoaderExecutor implementation while importing ONLY the dedicated spec module — the interface's method signatures already reference only spec types (Threaded / LoadedProject / UnifiedFile), so promoting it is a relocation, not an invention. Because the methods are TYPED, a compiled-in placement pays no envelope tax; only a true out-of-module plugin marshals (the existing LoadedProject / UnifiedFile envelopes). loaderkit.LoadSeamsFromExecutor consumes this to build its internal LoadSeams.
type LoaderWalkRequest ¶
type LoaderWalkRequest struct {
Dir string `yaml:"dir,omitempty" json:"dir"`
RootData []byte `yaml:"root_data,omitempty" json:"root_data,omitempty"`
}
#LoaderWalkRequest is the "loader-walk" host-builder envelope (K1-LOADER RELOCATION, Unit B/D): a plugin driving loaderkit.LoadUnified plugin-side asks the HOST to run the kind-blind import/discover/namespace walk for a project dir over the ALREADY bootstrap-transformed root bytes. RootData is the raw (bootstrap-phase-transformed) YAML, carried as base64-over-JSON []byte (NOT RawBody — it is YAML, not JSON). The reply is a spec.LoadedProject marshalled directly (no reply envelope needed). Every OTHER loader leg carries an existing type verbatim over the []byte wire: loader-bootstrap ([]byte→[]byte), loader-threaded (∅→spec.Threaded), loader-materialize (spec.LoadedProject→loaderkit.UnifiedFile). The former loader-android-validate / loader-preempt-validate legs dissolved — a plugin-side loader now self-serves those validators over InvokeProvider(kind, OpResolve).
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 LocalPkgInstallStep ¶ added in v0.2026210.2308
type LocalPkgInstallStep struct {
PkgbuildRef string // the layer's `localpkg:` value (e.g. "pkg/arch") — a hint, resolved at emit
CandyName string
CandyDir string // layer source dir — one anchor for the relative PKGBUILD search
ProjectDir string // the deploy project dir (os.Getwd() at deploy time) — the other anchor
// Format is the package-format name whose `local_pkg:` config drives this
// step (e.g. "pac"). "" when the target distro declares no localpkg-capable
// format — the executor then treats the step as a clean no-op.
Format string
// LocalPkg is the format's localpkg contract resolved from the embedded build vocabulary (charly/charly.yml) at
// compile time (DistroDef.LocalPkgFormat). It carries the build/install
// templates, package glob, source-dir sentinel, and probe command — so the
// executor renders every package-manager command from config instead of
// hardcoding build/install/glob literals. The install command auto-resolves
// the package's dependencies from the target's repos (pacman -U / dnf
// install / apt-get install), so there is no dep-closure builder. Nil when
// Format == "".
LocalPkg *LocalPkg
}
--------------------------------------------------------------------------- LocalPkgInstallStep — build a bundled PKGBUILD on the host (makepkg) and install the resulting `.pkg.tar.zst` onto a pac-based deploy target. ---------------------------------------------------------------------------
LocalPkgInstallStep is the IR form of a candy's `localpkg:` field — a pointer at a bundled Arch PKGBUILD directory (relative to the candy dir or the project root). It is the proper-package counterpart of the charly candy's ad-hoc curl-a-binary `cmd:` task: on an Arch/CachyOS DEPLOY target the package is built from the repo's bundled PKGBUILD on the HOST (`makepkg`), the resulting artifact is transferred to the target, and `pacman -U`-installed — so `charly` lands as the tracked `opencharly-git` package at /usr/bin/charly rather than an untracked binary at /usr/local/bin/charly.
Like ApkInstallStep, the step is compiled REGARDLESS of target and each DeployTarget decides whether to execute or skip:
- the external local deploy (Arch/CachyOS host) and external vm deploy (Arch/CachyOS guest) EXECUTE it via the RunHostStep host-engine leg (build on host → transfer → pacman -U on the target).
- On a NON-pac deploy target the executor records a clean skip (a Fedora / Debian host has no pacman; the candy's own `cmd:` task curls the binary there as the documented fallback).
- The IMAGE build (generate.go writeCandySteps) AND the pod-overlay build-emit RENDER the localpkg IMAGE install via RenderLocalPkgImageInstall (localpkg.go, a PURE sdk/deploykit function since W3): a PRODUCTION box DOWNLOADS the published release, a DISPOSABLE check bed BUILDS the in-development package on the host and COPYs it in (both install via the SAME dep-resolving install template). The pod-overlay build-emit routes through candy/plugin-installstep's `step:local-pkg-install` OpEmit, which STILL calls back the host `step-emit` seam (stepEmitLocalPkgInstall) — not because the render needs core, but to thread the shared `buildEngineContext` alongside its genuinely host-coupled siblings (system-packages/builder/op) on the same seam. A distro with no localpkg-capable format (LocalPkg==nil) renders nothing; the candy's own COPY/curl `cmd:` task is the fallback there.
- the android / k8s substrates (external) SKIP it (no Arch package surface).
The PKGBUILD location is resolved at EMIT time (not compile time), so the step carries only the author's hint (`PkgbuildRef`) plus the candy's source dir + the deploy project dir for the walk-up search. When no PKGBUILD is found the step is a no-op (the candy's existing curl/COPY task is the fallback).
func (*LocalPkgInstallStep) Kind ¶ added in v0.2026210.2308
func (s *LocalPkgInstallStep) Kind() StepKind
func (*LocalPkgInstallStep) RequiresGate ¶ added in v0.2026210.2308
func (s *LocalPkgInstallStep) RequiresGate() Gate
func (*LocalPkgInstallStep) Reverse ¶ added in v0.2026210.2308
func (s *LocalPkgInstallStep) Reverse() []ReverseOp
Reverse returns no ledger ops — the package is the deploy substrate's own pacman-tracked package, removed (if ever) via `pacman -R opencharly-git` by the operator, not by deploy teardown. Mirrors ApkInstallStep's empty Reverse.
func (*LocalPkgInstallStep) Scope ¶ added in v0.2026210.2308
func (s *LocalPkgInstallStep) Scope() Scope
Scope is system — installing a pacman package mutates global package state.
func (*LocalPkgInstallStep) Venue ¶ added in v0.2026210.2308
func (s *LocalPkgInstallStep) Venue() Venue
Venue is host-native — makepkg runs on the host (like the aur builder), then the artifact is shipped to the target and installed via pacman.
type LocalResolveInput ¶
type LocalResolveInput struct {
Local RawBody `yaml:"local,omitempty" json:"local"`
}
#LocalResolveInput / #AndroidResolveInput carry one opaque template body to project.
type LocalResolveReply ¶
type LocalResolveReply struct {
Resolved *ResolvedLocal `yaml:"resolved,omitempty" json:"resolved,omitempty"`
}
#LocalResolveReply / #AndroidResolveReply wrap the resolved template.
type MCPProvideEntry ¶
type MCPProvideEntry struct {
Name string `yaml:"name" json:"name"`
URL string `yaml:"url" json:"url"`
Transport string `yaml:"transport,omitempty" json:"transport,omitempty"`
Source string `yaml:"source" json:"source"`
}
MCPProvideEntry is a RESOLVED mcp_provide entry — a CandyMCPProvide (the raw baked label form) plus the Source deployment it came from. It is shared by charly's DEPLOY-time provides injection (config_image.go) AND the out-of-process mcp CHECK verb (candy/plugin-mcp), which both apply PodAwareMCPProvides; it lives here (ONE copy, R3) so it crosses the charly/plugin module boundary. charly aliases it as MCPProvideEntry; its GetName/GetSource satisfy charly's Named interface structurally.
func PodAwareMCPProvides ¶
func PodAwareMCPProvides(entries []MCPProvideEntry, consumerKey, ctrName string) []MCPProvideEntry
PodAwareMCPProvides rewrites same-deployment entries' URLs to localhost (a peer MCP server in the SAME pod is reached over loopback, not its container-DNS name) and prefers a local entry over a cross-deployment one of the same name. consumerKey is the consuming deployment's box name; ctrName is its running container name. Pure — moved verbatim from charly/provides.go.
func (MCPProvideEntry) GetName ¶
func (e MCPProvideEntry) GetName() string
GetName / GetSource let MCPProvideEntry satisfy charly's Named interface (provides.go).
func (MCPProvideEntry) GetSource ¶
func (e MCPProvideEntry) GetSource() string
type MatchOpMap ¶
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 ¶
Matcher is a goss-style matcher: a scalar (implicit equality) or an operator map ({equals: X}, {contains: […]}, {matches: R}, {lt: N}, …).
func (Matcher) MarshalJSON ¶
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 ¶
MarshalYAML emits the canonical operator-map form so round-tripping is stable.
func (*Matcher) UnmarshalJSON ¶
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 MaterializeProjectSeams ¶ added in v0.2026213.1726
type MaterializeProjectSeams struct {
// MaterializeProject folds ONE document's parsed entity nodes into uf via the registered
// Materializer (registry kind-decode + the per-node not-found policy). uf already carries the
// document's decoded reserved directives; this adds the Box/Candy/Bundle/PluginKinds entities,
// accumulating across the document's node list.
MaterializeProject func(pp *ParsedProject, uf *UnifiedFile) error
// FoldDiscoveredManifests folds every discovered manifest's parsed nodes into uf — a LAYER candy
// registers a lazy `From:` reference (the bootstrap-critical candyIsImage box⊻layer routing stays
// host-side), every other kind materializes via the registered Materializer. Shared host-side with
// charly's ApplyDiscover (R3), so it stays one host leaf.
FoldDiscoveredManifests func(dms []DiscoveredManifest, uf *UnifiedFile) error
// ApplyEmbeddedDefaults merges the binary-embedded build vocabulary + sidecar templates UNDER uf's
// own entries (project-wins). Host-resident: the embedded bytes and the host node-form parser are
// charly's.
ApplyEmbeddedDefaults func(uf *UnifiedFile) error
}
MaterializeProjectSeams bundles the host-coupled leaf legs the kind-blind whole-project MATERIALIZE orchestration (loaderkit.MaterializeLoadedProject) calls out to. Each is registry-/host-/bootstrap- coupled and CANNOT run kind-blind inside loaderkit; the host wrapper (charly's hostMaterializeProjectSeams) is the sole constructor and always populates every field. A nil field panics on use. Relocated to the dedicated spec module (#55 2b C3) so the host supplies it as a spec-typed seam via LoaderExecutor.MaterializeProjectSeams() — loaderkit drives the orchestration internally, and charly core stops importing loaderkit to load its own config. All fields are callbacks over spec types (the same injected-seam shape as WalkSeams / MaterializeSeams).
type MaterializeSeams ¶
type MaterializeSeams struct {
// DecodeEntity resolves pn's discriminator against the provider registry and, if a provider is
// found, folds the decoded entity into acc (mutating whichever field the kind belongs in) — the
// SAME dispatch the former in-core normalizeNodeInto/runPluginKind always performed. found=false
// (no error) means the registry has no provider for pn.Disc — the Materializer applies its OWN
// not-found policy from there, using Threaded + the callbacks below.
DecodeEntity func(pn ParsedNode, acc *MaterializedProject) (found bool, err error)
// BuildBundleEntity folds pn as a deploy-substrate entity into acc.Bundle — the fallback for a
// RECOGNIZED (Threaded.DeploySubstrates) but not-yet-connected external deploy substrate word.
BuildBundleEntity func(pn ParsedNode, acc *MaterializedProject) error
// InKindConnectPass reports whether the loader is inside the re-entrant connect-declared-kind
// pre-pass (a nested load triggered by connecting a plugin) — a still-unconnected declared kind
// is silently deferred (skip, no error) during this pass.
InKindConnectPass func() bool
// DeclaredKindConnectError returns the retained build/connect failure for a declared-but-
// unconnected kind word, or nil if it simply hasn't been reached yet.
DeclaredKindConnectError func(word string) error
}
MaterializeSeams is the set of host-supplied callbacks the per-node kind-decode DISPATCH needs for everything registry-coupled — resolving a node's discriminator word to its live Provider and invoking it stays host-side (boundary law clause M: provider_registry.go + provider_kind_invoke.go are the TRUE mechanism, same bucket as WalkSeams.Boundary/ResolveRef/GateDoc above); the Materializer NEVER touches the provider registry directly.
type MaterializedProject ¶
type MaterializedProject struct {
Box map[string]json.RawMessage
Candy map[string]json.RawMessage
Bundle map[string]BundleNode
PluginKinds map[string]map[string]json.RawMessage
}
MaterializedProject accumulates the kind-decoded ENTITY maps ONE document's or ONE discovered node's fold produces — the SAME fields charly-core's *UnifiedFile carries for this purpose (Box/Candy/Bundle/PluginKinds); the host copies them in before a Materializer call and back out after (cheap map-header copies — maps are reference types, so this is NOT a deep copy).
The 5 standalone-substrate-TEMPLATE kinds (vm/pod/k8s/local/android) do NOT get their own dedicated fields here — they fold into PluginKinds[disc][name] like every other templated kind (distro/builder/init/sidecar/resource/agent already do), so foldStandaloneTemplateReply (charly/node_normalize.go) needs NO per-kind-word switch to pick a destination field: the generic write `acc.PluginKinds[disc][name] = replyJSON` IS the fold, for any disc. charly-core's UnifiedFile.VM()/.Pod()/.K8s()/.Local()/.Android() are now DERIVED accessor methods reading PluginKinds, mirroring the established Distros()/Builders()/Inits() pattern — not stored fields — so there is nothing left to copy for them either.
SDD classification (hand-written, non-wire — precedent: ParsedProject/LoadedProject/CandyRefs/ ScannedCandy above, the established sibling family this type extends): same-process PIPELINE STATE crossing ONLY the compiled-in typed Materializer seam below — never marshaled, because the loader plugin is bootstrap-critical and ALWAYS compiled-in (see the package doc above). A live `cue exp gengotypes` spike on this exact shape (all fields already-portable map[string]json.RawMessage / map[string]BundleNode / map[string]map[string]json.RawMessage — zero disjunctions, zero open tails) would generate a faithful plain struct per the CAN/CANNOT quick reference in /charly-internals:go — CUE-sourcing it is NOT precluded by shape. It stays hand-written here instead because it is not a wire type at all: it never crosses a real marshal/unmarshal boundary (the compiled-in placement passes it as a live *MaterializedProject Go pointer, exactly like WalkSeams' live callbacks above) — the SAME reasoning CandyRefs/ ScannedCandy already document for this file, which the SDD "wire types are CUE-sourced without exception" mandate does not reach (it binds host↔plugin / render-context WIRE carriers; this is same-process pipeline state, the class loader_seam.go's own precedent already carves out).
type Materializer ¶
type Materializer interface {
MaterializeNode(pn ParsedNode, t Threaded, seams MaterializeSeams, acc *MaterializedProject) error
}
Materializer is the swappable per-node kind-decode DISPATCH seam (#46 unit 1, K1): the loader plugin candy implements it (candy/plugin-loader, delegating to loaderkit.Materialize), and the host resolves the registered loader provider to it and calls it once per parsed entity node — so an alternative loader plugin applies a different not-found/fallback policy by implementing this. Typed (no wire envelope), mirroring DocParser/ProjectWalker/CandyScanner above.
type MergeReply ¶
type MergeReply struct {
LayersBefore int `yaml:"layers_before,omitempty" json:"layers_before,omitempty"`
LayersAfter int `yaml:"layers_after,omitempty" json:"layers_after,omitempty"`
Skipped bool `yaml:"skipped,omitempty" json:"skipped,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
Notes []string `yaml:"notes,omitempty" json:"notes,omitempty"`
}
#MergeReply — the OCI plugin's merge outcome. layers_before/layers_after report the layer-count reduction; skipped is true when the image was too large or had nothing to merge; error carries a per-merge failure (the reply-error convention — e.g. the known podman-load EEXIST case — distinct from an infra Go error on Invoke); notes carries the human progress lines the host prints.
type MergeRequest ¶
type MergeRequest struct {
ImageRef string `yaml:"image_ref,omitempty" json:"image_ref"`
MaxMB int `yaml:"max_mb,omitempty" json:"max_mb"`
MaxTotalMB int `yaml:"max_total_mb,omitempty" json:"max_total_mb"`
Engine string `yaml:"engine,omitempty" json:"engine"`
DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,omitempty"`
}
#MergeRequest — the host resolves the box (image ref + merge limits + engine) and hands the OCI plugin everything it needs to run the go-containerregistry layer merge, so the plugin never imports charly core config-loading.
type NamespaceMount ¶
type NamespaceMount struct {
Alias string `yaml:"alias,omitempty" json:"alias,omitempty"`
Ref bool `yaml:"ref,omitempty" json:"ref,omitempty"`
RefID int64 `yaml:"refID,omitempty" json:"refID,omitempty"`
Project LoadedProject `yaml:"project,omitempty" json:"project,omitempty"`
}
#NamespaceMount — one namespaced `import:` (alias → an isolated child project). Two shapes:
- a DEFINITION (ref=false): the mount carries the child project inline (project), whose own `id` the host registers so a later reference to it shares the SAME materialized *UnifiedFile;
- a REFERENCE (ref=true): a repo-identity cycle-break or a diamond re-import — the child was already walked, so the mount carries NO inline project, only refID (the target project's id).
The host materialize resolves a reference to the pointer it registered for that id (register- before-recurse), preserving the original loader's pointer identity across the mutual-import cycle (main↔cachyos). Modeled as a LIST entry (not a self-recursive map) so `cue exp gengotypes` generates faithfully — the pointer slice `[]*NamespaceMount` on #LoadedProject breaks recursion.
type NamespaceScanEntry ¶ added in v0.2026213.1726
type NamespaceScanEntry struct {
Child string `yaml:"child,omitempty" json:"child"`
Scanned map[string]ScannedCandy `yaml:"scanned,omitempty" json:"scanned,omitempty"`
Downloads []RemoteDownload `yaml:"downloads,omitempty" json:"downloads,omitempty"`
}
#NamespaceScanEntry / #NamespaceScanReply — the `buildengine-namespaced` host-leg reply (K1 loader-cone fabric-tail, #55): the host recurses the project's import-namespace tree ONCE and emits a FLAT list of per-namespace scan inputs. The plugin (candy/plugin-build's FillNamespacedBoxes seam) iterates the list and runs the candy-scan fetch fix-point (loaderkit.ScanCandyFromLocal) + deploykit.RawCandyPair + deploykit.FillNamespaceBoxViews plugin-side — the deploykit calls left core when charly/resolved_project_host.go was deleted (the file's own L26-32 named exit). This leg is the LAST core↔plugin cut that keeps the namespaced-box resolve off sdk/deploykit in charly core (deploykit 2→1).
Per entry the host sends ONLY what the plugin cannot derive itself:
- child: the namespace-qualified box path ("fedora" or "ns1.ns2") — the plugin descends the root uf.Namespaces tree by this path to recover the namespace's *spec.Config for FillNamespaceBoxViews (sub is NOT serializable cleanly — Config is hand-written with a recursive Namespaces map — so it stays plugin-derived, never crosses the wire).
- scanned: the namespace's PRE-fix-point spec.ScannedCandy map (from projectCandiesScanned, reading subUF.Candy in-memory — the R1 fix; never a re-load).
- downloads: the namespace-scoped INITIAL RemoteDownload set (from CollectRemoteRefsOpts over the namespace's own cfg + localScanned raw refs) — the ONE cfg-coupled step. The plugin's ScanSeams.CollectRemoteRefs returns this verbatim; EnsureRepo/ScanRemote still round-trip to the cfg-agnostic host legs (buildengine-ensure-repo / buildengine-scan-remote) for the transitive fetch.
initCfg / calver / dir come from the plugin's own resolve context (the seam closure params) and are deliberately NOT duplicated here.
SDD: the scanned/downloads fields reference the HAND-WRITTEN spec.ScannedCandy / spec.RemoteDownload pipeline-state types via @go(,type=…) overrides (the RDD-proven gengotypes spike for this file: @go(Scanned,type=map[string]ScannedCandy) emits `Scanned map[string]ScannedCandy` referencing the hand-written type, NO duplicate generated type). The inline CUE shapes are structural placeholders for the override only — they are never generated as named Go types (conflict count 0, spiked). The existing buildengine-scan-local / buildengine-collect-remote-refs legs already marshal these same hand-written types over the wire; #NamespaceScanReply is the CUE-sourced envelope that carries them.
type NamespaceScanReply ¶ added in v0.2026213.1726
type NamespaceScanReply struct {
Entries []NamespaceScanEntry `yaml:"entries,omitempty" json:"entries,omitempty"`
}
#NamespaceScanReply is the flat list the host returns; the plugin folds each entry into rp.
type OCIEmitStepParams ¶ added in v0.2026211.549
type OCIEmitStepParams struct {
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
StepView InstallStepView `yaml:"step_view,omitempty" json:"step_view"`
PlanView InstallPlanView `yaml:"plan_view,omitempty" json:"plan_view"`
}
#OCIEmitStepParams is the plain-Go param struct the pod-overlay candy marshals into a HostBuild("step-emit", #StepEmitRequest{Word:"oci-emit-step", Payload:<#OCIEmitStepParams>}) per install step (promoted from sdk/deploykit — #55 import-purity, Cone V value-vocabulary). Dir is the project dir the host "oci-emit-step" emitter uses to look up the cached overlay buildEngineContext (a live *Generator/DistroDef/BuilderConfig/Box cannot cross the wire); StepView is the step's wire form (StepToView) and PlanView the step's plan wire form — the host reconstructs both (StepFromView/PlanFromView) then calls ociEmitStep, returning the rendered Containerfile fragment. StepView/PlanView are required (always set by the caller); Dir is optional (empty for the non-overlay per-step emit path).
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 ¶
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 ¶
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 ¶
StringFields returns pointers to every string-valued modifier field, for the in-place ${VAR} expansion driven by ExpandVars (free function in main).
type OpStep ¶ added in v0.2026210.2308
type OpStep struct {
Op *Op
CandyName string
CandyDir string
CtxPath string // absolute layer-dir path replacing "/ctx/" on host
ResolvedUser string // uid:gid or "root" after resolveUserSpec
// To is the home-resolved copy/download destination for the DEPLOY path.
// The compiler tokenizes the task's `to:` (~ / ${HOME} / $HOME → {{.Home}})
// here; InstallPlan.ResolveHome substitutes the destination's real home at
// emit. execTask uses this (not the raw Task.To) for PutFile, so a
// `copy: to: ${HOME}/...` lands in the actual guest/host home instead of a
// literal "${HOME}" directory under sudo (HOME=/root). Empty when the task
// has no `to:`. The OCI build emitter reads Task.To directly (build-time
// ENV expands ${HOME}), so it is unaffected by this field.
To string
// CandyVars are the candy manifest `vars:` map propagated into the task
// script as exports. Build-time gets these via Containerfile ENV
// directives (emitVarsEnv); host/local-deploy time has no equivalent
// mechanism, so the renderer emits `export K=V` lines from this field.
// Without this, candies like `kubernetes` whose download URLs reference
// ${K3D_VERSION} fetched an empty path-component at deploy time and
// curl 404'd.
CandyVars map[string]string
// Distros is the image's distro tag chain (img.Tags), carried so the act-emit
// enabler can pass it to a state-provision plugin verb's RenderProvisionScript
// (e.g. the cross-distro package resolution). Empty for non-plugin ops; the
// unix_group act ignores it, but a future package/service state-provision plugin
// reads it.
Distros []string
}
OpStep wraps a raw Op so the IR compiler doesn't have to transform it further. The OCI target and host target both understand Op shapes (cmd / mkdir / copy / write / link / download / setcap / build) and emit the appropriate directives. CtxPath substitutes for /ctx/ in cmd bodies on the host target (container RUN directives keep /ctx via bind-mount, which doesn't exist on the host).
func (*OpStep) RequiresGate ¶ added in v0.2026210.2308
type OverlayBuildReply ¶
type OverlayBuildReply struct {
OverlayRef string `yaml:"overlay_ref,omitempty" json:"overlay_ref,omitempty"`
BaseImage string `yaml:"base_image,omitempty" json:"base_image,omitempty"`
DeployName string `yaml:"deploy_name,omitempty" json:"deploy_name,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
// resolved_project is the overlay-scoped resolved-project envelope the
// candy constructs a deploykit.Generator from. Nil when there is no
// add_candy overlay to synthesize (the tag-only path).
ResolvedProject *ResolvedProject `yaml:"resolved_project,omitempty" json:"resolved_project,omitempty"`
// plans is the deployment's compiled InstallPlans serialized as
// InstallPlanViews. Empty for the no-overlay path.
Plans []InstallPlanView `yaml:"plans,omitempty" json:"plans,omitempty"`
// calver is the host's current CalVer.
CalVer string `yaml:"calver,omitempty" json:"calver,omitempty"`
// overlay_candy_security carries each overlay candy's own `security:`
// block.
OverlayCandySecurity map[string]*Security `yaml:"overlay_candy_security,omitempty" json:"overlay_candy_security,omitempty"`
// parent_volumes carries the PARENT deploy node's bind-mount volumes for a
// NESTED pod-in-pod overlay build.
ParentVolumes []DeployVolume `yaml:"parent_volumes,omitempty" json:"parent_volumes,omitempty"`
}
#OverlayBuildReply is what the "overlay" host-builder returns.
type OverlayBuildRequest ¶
type OverlayBuildRequest struct {
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
DeployName string `yaml:"deploy_name,omitempty" json:"deploy_name,omitempty"`
Image string `yaml:"image,omitempty" json:"image,omitempty"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,omitempty"`
AssumeYes bool `yaml:"assume_yes,omitempty" json:"assume_yes,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"`
WithServices bool `yaml:"with_services,omitempty" json:"with_services,omitempty"`
}
#OverlayBuildRequest is the BUILD-ENGINE DISPATCH envelope for the pod-overlay build — the F10 "overlay" host-builder.
type PackageItem ¶
type PackageItem struct {
Name string `yaml:"name" json:"name"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
}
--------------------------------------------------------------------------- PackageItem — #PackageItem. Bare scalar XOR object form; the bare-scalar shorthand is canonicalized to {name} by the loader normalizer. ---------------------------------------------------------------------------
type PackageSection ¶
type PackageSection struct {
FormatName string `yaml:"format_name,omitempty" json:"format_name,omitempty"`
Packages []string `yaml:"packages,omitempty" json:"packages,omitempty"`
Raw map[string]any `yaml:"raw,omitempty" json:"raw,omitempty"`
}
#PackageSection — a generic format-specific package section (rpm/deb/pac/aur). Raw carries the full YAML map for template rendering. Mirrors deploykit.PackageSection (which now aliases this).
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 ParsedCalVer ¶ added in v0.2026213.1726
type ParsedCalVer struct {
Year int // calendar year (e.g. 2026)
Day int // day of year, 1-366
HHMM int // hour*100 + minute, 0-2359
}
ParsedCalVer is a parsed YYYY.DDD.HHMM calendar version. The same format that ComputeCalVer emits for image tags is, since the 2026-05 schema-versioning cutover, the schema-version stamp carried by every versioned YAML config. The declarative migration table is ordered by ParsedCalVer, and the load-time gate compares a file's version against LatestSchemaVersion.
func LatestSchemaCalVer ¶ added in v0.2026213.1726
func LatestSchemaCalVer() ParsedCalVer
LatestSchemaCalVer is the HEAD schema CalVer (parsed) — every current-format versioned file is stamped to it and the load-time gate requires it. Named distinctly from the SchemaVersion string const it parses; sdk/kit re-exports it as kit.LatestSchemaVersion.
func MustCalVer ¶ added in v0.2026213.1726
func MustCalVer(s string) ParsedCalVer
MustCalVer parses a compile-time-constant CalVer literal, panicking on a malformed value. Used for the CUE-owned HEAD/floor consts (SchemaVersion / SchemaFloor), so a non-canonical literal that slipped past the strict #CanonCalVer CUE gate still fails fast at process start rather than silently mis-ordering the migration table.
func ParseCalVer ¶ added in v0.2026213.1726
func ParseCalVer(s string) (ParsedCalVer, bool)
ParseCalVer parses the CANONICAL CalVer string "YYYY.DDD.HHMM" — exactly a 4-digit year, a 3-digit zero-padded day-of-year, and a 4-digit zero-padded HHMM, separated by dots. It is EXTREMELY STRICT and has NO backward compatibility: every component must be the exact width, pure ASCII digits (no sign, no inner whitespace), within range (day 1-366, hour 0-23, minute 0-59). Anything else — the legacy integer "4", a non-padded "2026.45.830", an empty string, junk — returns ok=false. (Surrounding whitespace, a transport artifact of e.g. a `charly version` trailing newline, is trimmed before the format check.)
A false result is exactly what the schema gate and migration runner treat as "older than every real CalVer", so a non-canonical config flows into `charly migrate` and is re-stamped canonical.
Because the canonical form is fixed-width zero-padded, a plain alphanumeric (lexicographic) sort of CalVer strings is chronological (see ParsedCalVer.Less).
func SchemaFloorCalVer ¶ added in v0.2026213.1726
func SchemaFloorCalVer() ParsedCalVer
SchemaFloorCalVer is the oldest schema CalVer (parsed) `charly migrate` can migrate FROM. A config below it (or with a non-CalVer version) is unmigratable. Named distinctly from the SchemaFloor string const it parses; sdk/kit re-exports it as kit.SchemaFloor.
func (ParsedCalVer) Less ¶ added in v0.2026213.1726
func (c ParsedCalVer) Less(o ParsedCalVer) bool
Less reports whether c is chronologically before o. Because the canonical string form is fixed-width zero-padded, chronological order IS lexicographic order, so this is a plain string comparison.
func (ParsedCalVer) String ¶ added in v0.2026213.1726
func (c ParsedCalVer) String() string
String renders the canonical CalVer "YYYY.DDD.HHMM" — 4-digit year, 3-digit zero-padded day, 4-digit zero-padded HHMM. This is the ONLY form ParseCalVer accepts, so String∘Parse is the identity and a plain alphanumeric sort of these strings is chronological.
type ParsedNode ¶
type ParsedNode struct {
Name string `yaml:"name,omitempty" json:"name"`
Disc string `yaml:"disc,omitempty" json:"disc"`
Body RawBody `yaml:"body,omitempty" json:"body,omitempty"`
Children []*ParsedNode `yaml:"children,omitempty" json:"children,omitempty"`
}
#ParsedNode — one decomposed reserved-word node: its name, kind discriminator, the opaque entity body (the complete kind value as JSON, materialized per-kind by the host), and any sub-entity member children (deployable kinds only). The recursive Children mirror the deploy tree's member nesting the host folds into uf.Bundle.
type ParsedPortMapping ¶ added in v0.2026213.1726
type ParsedPortMapping struct {
BindAddr string // explicit bind prefix if present (e.g. "127.0.0.1" or "[::1]"); empty otherwise
Host int
Container int
Protocol string // "udp" / "tcp" / "" — extracted from /udp or /tcp suffix
}
ParsedPortMapping describes the four possible shapes podman accepts:
"P" -> {Host: P, Container: P}
"H:C" -> {Host: H, Container: C}
"IP:H:C" -> {Host: H, Container: C, BindAddr: "IP"}
"[v6]:H:C" -> {Host: H, Container: C, BindAddr: "[v6]"}
Any of those forms may carry a /tcp or /udp suffix on the trailing port.
func ParsePortMapping ¶ added in v0.2026213.1726
func ParsePortMapping(mapping string) (ParsedPortMapping, bool)
ParsePortMapping is the canonical port-mapping parser.
Returns ok=false on unparseable input. Callers that want a loud failure (warning logged, port skipped) should branch on ok.
All in-tree port handling routes through this — ParseHostPort, ParseContainerPort, parseHostPorts (tunnel.go), buildPortMapping (tunnel.go), and localizePort (shell.go) — so a single fix here covers every site that would otherwise mis-handle the IP:H:C form.
type ParsedProject ¶
type ParsedProject struct {
Version string `yaml:"version,omitempty" json:"version,omitempty"`
Nodes []ParsedNode `yaml:"nodes,omitempty" json:"nodes,omitempty"`
Imports []string `yaml:"imports,omitempty" json:"imports,omitempty"`
}
#ParsedProject — the whole parse result: the schema version (for the post-parse gate), the decomposed reserved-word nodes, and the resolved import refs (already fetched + merged by the plugin's import walk). Discover results are decomposed into the same nodes list.
type ParsedRef ¶
type ParsedRef struct {
Raw string // original string, e.g. "@github.com/org/repo/candy/name:v1.0.0"
RepoPath string // e.g. "github.com/org/repo"
SubPath string // e.g. "candy/name" (path within repo)
Name string // e.g. "name" (last segment)
Version string // e.g. "v1.0.0"
}
ParsedRef is a parsed remote reference: `@host/org/repo/sub/path:version`. Works for both candy refs and image refs.
func ParseRemoteRef ¶
ParseRemoteRef parses a remote reference into repo path, sub-path, name, and version. e.g. "@github.com/org/repo/candy/name:v1.0.0" -> ParsedRef{RepoPath: "github.com/org/repo", SubPath: "candy/name", Name: "name", Version: "v1.0.0"}
type Phase ¶
type Phase int
Phase identifies which template phase (prepare / install / cleanup) a rendered step belongs to. The phase lets targets treat repo-config mutation (prepare) distinctly from package install — which is exactly the granularity --allow-repo-changes gates. A single format section in the candy manifest typically emits three PhasePrepare/PhaseInstall/PhaseCleanup steps, and the compiler tags each with the appropriate phase.
type PhaseCarrier ¶
type PhaseCarrier interface {
PluginPhase() string
}
PhaseCarrier is implemented by a provider (grpcProvider out-of-proc, inprocProvider compiled-in) that carries its declared lifecycle PHASE (F9). A provider not implementing it (e.g. a builtin non-plugin provider) is treated as PhaseRuntime by the host's phaseOfProvider.
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 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 PodAttachOpts ¶
type PodAttachOpts struct {
Cmd []string `yaml:"cmd,omitempty" json:"cmd,omitempty"`
Tty bool `yaml:"tty,omitempty" json:"tty"`
Shell PodShellOpts `yaml:"shell,omitempty" json:"shell,omitempty"`
CmdOpts PodCmdOpts `yaml:"cmd_opts,omitempty" json:"cmd_opts,omitempty"`
}
#PodAttachOpts carries the F12 Attach op's parameters (K4/F12 inversion): tty selects the shell resolver (interactive `charly shell`) vs the cmd resolver (`charly cmd`'s non-interactive `-i` exec) — mirrors the former host-side resolvePodAttachPlan dispatch, now run plugin-side.
type PodCmdOpts ¶
type PodCmdOpts struct {
Sidecar string `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
}
#PodCmdOpts carries `charly cmd`'s per-invocation extra (--sidecar), replacing the former host-side resolvePodCmdPlan.
type PodCmdPayload ¶ added in v0.2026217.301
type PodCmdPayload struct {
Command string `yaml:"command,omitempty" json:"command,omitempty"`
Sidecar string `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
}
#PodCmdPayload — see #PodLifecycleRequest's header; op="cmd". Carries `charly cmd <box> <command>`'s per-invocation fields: the host does ONLY the irreducible dispatchLifecycleTarget("cmd") + LifecycleTarget.Attach step, mirroring op="shell" exactly — the interactive `-i` exec runs over the SAME host-held exec.RunInteractive leg (stdio never crosses the wire). The plugin owns the CLI grammar + the completion notification itself.
type PodConfigBoxEngineReply ¶
type PodConfigBoxEngineReply struct {
Engine string `yaml:"engine,omitempty" json:"engine"`
}
type PodConfigBoxEngineRequest ¶
type PodConfigBoxEngineRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
GlobalEngine string `yaml:"global_engine,omitempty" json:"global_engine"`
}
#PodConfigBoxEngineRequest / Reply: ResolveBoxEngineForDeploy(box,instance,globalEngine) — reads the per-host deploy config's Engine override. A thin wrapper distinct from #PodConfigLoadDeployRequest since callers here want only the resolved engine string, not the whole BundleConfig.
type PodConfigCleanDeployEntryReply ¶
type PodConfigCleanDeployEntryReply struct {
}
type PodConfigCleanDeployEntryRequest ¶
type PodConfigCleanDeployEntryRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
}
#PodConfigCleanDeployEntryRequest / Reply: deploykit.CleanDeployEntry(box, instance, marshalDeployNode) — the `charly remove` deploy-entry cleanup (Cutover B unit 2 remove-verb completion). Follows the {box!, instance?} → {} host-owns-load+lock+mutate+save shape — deliberately NOT the plugin-side deploykit.SaveDeployState/SaveBundleConfig write (bundle import/reset + deploy-state persist, #55 K4 — no host seam), which persists an ALREADY-LOADED, already-mutated whole BundleConfig with no internal load/lock/entry-removal logic — a genuinely different, narrower operation CleanDeployEntry's own internal file-lock + entry-removal + provides-cleanup + empty-file-delete logic cannot be reduced to.
type PodConfigContainerTunnelReply ¶
type PodConfigContainerTunnelReply struct {
TunnelJSON RawBody `yaml:"tunnel_json,omitempty" json:"tunnel_json,omitempty"`
}
type PodConfigContainerTunnelRequest ¶
type PodConfigContainerTunnelRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
}
#PodConfigContainerTunnelRequest / Reply: reads the RUNNING container's baked image ref (containerImage), extracts + merges its metadata, and resolves the tunnel config. Distinct from #PodConfigTunnelResolveRequest (which takes an already-resolved MetaJSON) — this seam resolves the image/metadata itself from a container name. candy/plugin-deploy-pod's start/stop path builds its own tunnel plan locally now (enc_tunnel_resolve.go, wave γ); this seam STAYS registered because candy/plugin-pod's `charly remove` teardown path (remove_tunnel.go) is a separate, still-live caller.
type PodConfigDetectDevicesReply ¶
type PodConfigDetectDevicesReply struct {
DetectedJSON RawBody `yaml:"detected_json,omitempty" json:"detected_json"`
}
type PodConfigDetectDevicesRequest ¶
type PodConfigDetectDevicesRequest struct {
NoAutoDetect bool `yaml:"no_auto_detect,omitempty" json:"no_auto_detect,omitempty"`
// engine, when set to "podman" alongside a GPU detection, triggers EnsureCDI() (the pod
// lifecycle's resolvePodRuntimeImage step) — bundled into this SAME seam call (R3) rather
// than a dedicated one.
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
}
#PodConfigDetectDevicesRequest / Reply: DetectHostDevices()+LogDetectedDevices() — registry-coupled (DetectHostDevices resolves+Invokes verb:gpu via the host provider registry, which a peer plugin cannot dial without the InvokeProvider rewrite this family defers).
type PodConfigListSidecarsReply ¶
type PodConfigListSidecarsReply struct {
Names []string `yaml:"names,omitempty" json:"names,omitempty"`
Descriptions map[string]string `yaml:"descriptions,omitempty" json:"descriptions,omitempty"`
BodiesJSON RawBody `yaml:"bodies_json,omitempty" json:"bodies_json,omitempty"`
}
#PodConfigListSidecarsReply: embeddedSidecarBodies()'s go:embed template names + descriptions — the `charly config --list-sidecars` introspection leaf (rare; kept as a narrow seam since the embedded data lives only in the charly binary).
type PodConfigLoadDeployReply ¶
type PodConfigLoadDeployReply struct {
ConfigJSON RawBody `yaml:"config_json,omitempty" json:"config_json,omitempty"`
}
type PodConfigLoadDeployRequest ¶
type PodConfigLoadDeployRequest struct {
Caller string `yaml:"caller,omitempty" json:"caller"`
}
#PodConfigLoadDeployRequest / Reply: deploykit.LoadDeployConfigForRead(caller) — the per-host charly.yml Bundle map. Genuinely loader-coupled: deploykit.SaveBundleConfig/ LoadDeployConfigForRead resolve through the package-var DeployStateHost seam, which is filled ONLY in the charly-core process's init() (charly/deploy_state_host.go) — an out-of-process plugin calling these directly would silently no-op (the kit's documented nil-safe degradation), so every load/save call site is a host seam, reusable across the whole ported flow.
type PodConfigMigrateSecretsRequest ¶
type PodConfigMigrateSecretsRequest struct {
ConfigJSON RawBody `yaml:"config_json,omitempty" json:"config_json"`
MetaJSON RawBody `yaml:"meta_json,omitempty" json:"meta_json"`
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
}
#PodConfigMigrateSecretsRequest / Reply: MigratePlaintextEnvSecret(dc, meta, box, instance) — the one-time plaintext-env → credential-store migration (file backup + DefaultCredentialStore + saveBundleConfigNodeForm, all FINAL/K5-deferred registry-coupled inventory per the ledger). config_json carries the ALREADY-LOADED dc (from #PodConfigLoadDeployRequest) so the host mutates + re-saves the SAME loaded structure the plugin is mid-flow with, never a stale reload.
type PodConfigRemoveReply ¶
type PodConfigRemoveReply struct {
}
#PodConfigRemoveReply is the "pod-config-remove" host-builder reply — empty.
type PodConfigRemoveRequest ¶
type PodConfigRemoveRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
}
#PodConfigRemoveRequest carries `charly config remove`'s flags (the former BoxConfigRemoveCmd's authored fields — distinct from `charly remove`/#PodLifecycleRequest op="remove"+#PodRemovePayload, which tears down the whole deploy; this removes only the quadlet + disables the service). Forwarded to HostBuild("pod-config-remove"), which runs the existing remove orchestration VERBATIM.
type PodConfigSaveBundleReply ¶
type PodConfigSaveBundleReply struct {
}
type PodConfigSaveBundleRequest ¶
type PodConfigSaveBundleRequest struct {
ConfigJSON RawBody `yaml:"config_json,omitempty" json:"config_json"`
}
#PodConfigSaveBundleRequest / Reply: saveBundleConfigNodeForm(dc) — persists a (plugin-mutated) *deploykit.BundleConfig back through the SAME loader-coupled seam.
type PodConfigScrubCliEnvRequest ¶
type PodConfigScrubCliEnvRequest struct {
CliEnv []string `yaml:"cli_env,omitempty" json:"cli_env,omitempty"`
MetaJSON RawBody `yaml:"meta_json,omitempty" json:"meta_json"`
}
#PodConfigScrubCliEnvRequest / Reply: scrubSecretCLIEnv(cliEnv, meta) — the credential-store Set() pre-scrub for `-e NAME=VAL` flags declared secret_accepts/secret_requires.
type PodConfigSetupReply ¶
type PodConfigSetupReply struct {
}
#PodConfigSetupReply is the "pod-config-setup" host-builder reply — empty, mirroring #PodLifecycleReply's empty-for-every-op-but-cmd shape.
type PodConfigSetupRequest ¶
type PodConfigSetupRequest struct {
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
Build bool `yaml:"build,omitempty" json:"build,omitempty"`
Env []string `yaml:"env,omitempty" json:"env,omitempty"`
Clean bool `yaml:"clean,omitempty" json:"clean,omitempty"`
EnvFile string `yaml:"env_file,omitempty" json:"env_file,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Port []string `yaml:"port,omitempty" json:"port,omitempty"`
KeepMounted bool `yaml:"keep_mounted,omitempty" json:"keep_mounted,omitempty"`
Password string `yaml:"password,omitempty" json:"password,omitempty"`
RefreshSecret []string `yaml:"refresh_secret,omitempty" json:"refresh_secret,omitempty"`
VolumeFlag []string `yaml:"volume_flag,omitempty" json:"volume_flag,omitempty"`
Bind []string `yaml:"bind,omitempty" json:"bind,omitempty"`
Encrypt []string `yaml:"encrypt,omitempty" json:"encrypt,omitempty"`
MemoryMax string `yaml:"memory_max,omitempty" json:"memory_max,omitempty"`
MemoryHigh string `yaml:"memory_high,omitempty" json:"memory_high,omitempty"`
MemorySwapMax string `yaml:"memory_swap_max,omitempty" json:"memory_swap_max,omitempty"`
Cpus string `yaml:"cpus,omitempty" json:"cpus,omitempty"`
Seed bool `yaml:"seed,omitempty" json:"seed,omitempty"`
ForceSeed bool `yaml:"force_seed,omitempty" json:"force_seed,omitempty"`
DataFrom string `yaml:"data_from,omitempty" json:"data_from,omitempty"`
UpdateAll bool `yaml:"update_all,omitempty" json:"update_all,omitempty"`
SshKey string `yaml:"ssh_key,omitempty" json:"ssh_key,omitempty"`
Sidecar []string `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
ListSidecars bool `yaml:"list_sidecars,omitempty" json:"list_sidecars,omitempty"`
NoAutoDetect bool `yaml:"no_autodetect,omitempty" json:"no_autodetect,omitempty"`
// explicit_ref is set programmatically (never authored) by `charly bundle from-box`'s
// source-less deploy path (bundle_from_box_cmd.go) — the P13-KERNEL direction-flip carries
// it across the wire now that the ORCHESTRATION (formerly reading the kong:"-" Go field
// directly) moved into the plugin.
ExplicitRef string `yaml:"explicit_ref,omitempty" json:"explicit_ref,omitempty"`
// host_env_json is the marshalled #HostEnv (CharlyBin/Home/Version) — the SAME R10
// bed-found bug class #DeployTargetDispatchRequest's own host_env_json field documents
// (unified_targets.go's hostEnvJSON(): os.Executable() resolves correctly to the charly
// binary only when called IN CORE — a plugin's os.Executable() resolves to the PLUGIN
// binary, wrong for an out-of-process placement). Setup's quadlet emission needs the REAL
// charly binary path for the encrypted-mount ExecStartPre line
// (deploykit.QuadletConfig.CharlyBin); the bug was dormant here until a deploy actually had
// an encrypted volume to mount (check-enc-pod's R10 first exercised it once the
// project-declared-volume fallback started resolving one). hostBuildPodConfigSetup
// populates this via the SAME core hostEnvJSON() helper the dispatch seam uses (R3 — one
// host-identity helper, not a second one invented here).
HostEnvJSON RawBody `yaml:"host_env_json,omitempty" json:"host_env_json,omitempty"`
}
#PodConfigSetupRequest carries the `charly config [setup]` command flags (the former BoxConfigSetupCmd's authored fields, PLUS explicit_ref — bundle_from_box_cmd.go's programmatically-set source-less-deploy field, below). P13-KERNEL direction-flip: forwarded from HostBuild("pod-config-setup") (host_build_pod_config.go's hostBuildPodConfigSetup) onward to the deploy:pod plugin's sdk.OpConfigSetup — the plugin now RUNS the former runConfig orchestration (candy/plugin-deploy-pod/config_setup.go), calling back the narrow "pod-config-*" seams below for the host/loader/registry/credential-coupled sub-steps.
type PodConfigTunnelResolveReply ¶
type PodConfigTunnelResolveReply struct {
TunnelJSON RawBody `yaml:"tunnel_json,omitempty" json:"tunnel_json,omitempty"`
}
type PodConfigTunnelResolveRequest ¶
type PodConfigTunnelResolveRequest struct {
MetaJSON RawBody `yaml:"meta_json,omitempty" json:"meta_json"`
}
#PodConfigTunnelResolveRequest / Reply: TunnelConfigFromMetadata(meta) — resolves the tunnel config (charly.yml overlay applied) from image labels.
type PodConfigWriteReply ¶
type PodConfigWriteReply struct {
WrittenPaths []string `yaml:"written_paths,omitempty" json:"written_paths,omitempty"`
}
#PodConfigWriteReply returns the paths the plugin actually wrote (deterministic; the host already knows them — used for the byte-parity assertion + teardown provenance).
type PodConfigWriteRequest ¶
type PodConfigWriteRequest struct {
PodConfigJSON RawBody `yaml:"pod_config_json,omitempty" json:"pod_config_json"`
ContainerPath string `yaml:"container_path,omitempty" json:"container_path"`
PodPath string `yaml:"pod_path,omitempty" json:"pod_path,omitempty"`
SidecarPaths map[string]string `yaml:"sidecar_paths,omitempty" json:"sidecar_paths,omitempty"`
TunnelPath string `yaml:"tunnel_path,omitempty" json:"tunnel_path,omitempty"`
CloudflaredCfgPath string `yaml:"cloudflared_cfg_path,omitempty" json:"cloudflared_cfg_path,omitempty"`
}
#PodConfigWriteRequest carries the POD config-WRITE (P11). Under Ruling C the config-WRITE (the quadlet/.pod/sidecar/tunnel file generation) moved to the deploy:pod plugin, while the RESOLVE + host side-effects (secret provisioning, saveDeployState, enc-mount, data-seed, systemctl) stay in the HOST `charly config` command (Q1=(a)). So this is HOST→PLUGIN: for a pod deploy, `charly config` resolves the full QuadletConfig + computes the exact target PATHS (the core filename helpers, unchanged) and PUSHES them to the plugin's config-write Op, which generates the file contents (deploykit.GenerateQuadlet + the pod/sidecar/tunnel generators) and os.WriteFiles them — byte-identical to the former core write phase (same paths, same content, same modes: .container/.pod/sidecar 0600, tunnel .service 0644).
PodConfigJSON is the resolved deploykit.QuadletConfig — a hand-written runtime type with no CUE def, so it travels as an opaque RawBody envelope (the VmJSON pattern; no new CUE wire struct). An optional path field being SET is the host's signal to write that file kind (pod_path/sidecar_paths present ⇒ sidecars configured; tunnel_path present ⇒ cloudflare tunnel) — the host owns the write conditionals, the plugin writes what it is told.
type PodDisposableReply ¶
type PodDisposableReply struct {
Disposable bool `yaml:"disposable,omitempty" json:"disposable,omitempty"`
}
#PodDisposableReply carries the single overlay-disposability bit.
type PodDisposableRequest ¶
type PodDisposableRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
}
#PodDisposableRequest asks the host whether a per-host POD deploy overlay entry is disposable (K5-U2/3). This is the ONE AI-harness check-project fact the resolved-project envelope cannot carry: the harness's iterate sandbox is an OPERATOR-provisioned per-host deploy (`charly bundle add <sandbox> <ref> --disposable`), so its disposability lives in the per-host overlay (LoadBundleConfig → ~/.config/charly/charly.yml), NOT the project charly.yml the resolved-project envelope projects (Mode Purity keeps the overlay out of the build-mode projection). The overlay read needs the core loader a plugin cannot import, and no deploy/status provider serves it, so it rides this THIN retained host seam. The host returns Bundle[Name].IsDisposable() (false when the sandbox has no entry — the harness then skips its fresh-per-run restart). Class-generic action noun "pod-disposable" (F11 — never a substrate word).
type PodExecReply ¶
type PodExecReply struct {
Output string `yaml:"output,omitempty" json:"output,omitempty"`
ExitCode int `yaml:"exit_code,omitempty" json:"exit_code,omitempty"`
}
#PodLifecyclePlan is the pod-lifecycle carrier (the K4 deep-body move). Formerly host-resolved and threaded on OpStart/OpStop; candy/plugin-deploy-pod now SELF-RESOLVES it (resolve.go's resolveStartQuadlet/resolveStopPlan, resolve_direct.go's resolveStartDirect) from the deploy key + the raw CLI opts (#PodStartOpts/#PodStopOpts), reaching the host only for genuinely host-only mechanisms (the deploy-overlay HostBuild seam, verb:credential, verb:enc, verb:tunnel) — this type is now built AND consumed entirely within the plugin process. It EXECUTES it — running the container start/stop over the served host executor and composing enc + tunnel via InvokeProvider(verb:enc/verb:tunnel), so the former podCli("start"/"stop"/…) `charly`-reentries are DELETED (bodies, not shells). The pre-built enc verb input (spec.EncExecInput — a hand-written wire type with no CUE def) rides as an opaque RawBody envelope (empty ⇒ that leg is skipped, the common plain-pod case) with its Method set per-op host-side; tunnel references the CUE-def'd #TunnelConfig directly and the plugin infers start-vs-stop from the op. The ARBITER claim is NOT threaded here — its CHARLY_PREEMPT_LEASE machinery is host-PROCESS state a placement-agnostic plugin cannot own, so the host proxy BRACKETS the plugin op (acquire before OpStart; release after OpStop + on the failure path). #PodExecReply is the reply from the pod plugin's OpShell CAPTURED-exec leg (the K4 `charly service` move — an in-container init-mgmt exec, non-interactive). The plugin RunCaptures the argv over the served executor and returns the combined Output + the exact ExitCode; the host reprints Output (placement-agnostic: an out-of-process plugin's stdout is NOT charly's) and propagates a non-zero ExitCode as *sdk.ExitCodeError so `charly service` preserves the container command's exit code exactly (the passthrough→capture semantics change the ruling requires be exit-code-faithful).
type PodLifecyclePlan ¶
type PodLifecyclePlan struct {
Mode string `yaml:"mode,omitempty" json:"mode"`
SvcName string `yaml:"svc_name,omitempty" json:"svc_name,omitempty"`
ContainerName string `yaml:"container_name,omitempty" json:"container_name"`
RunArgv []string `yaml:"run_argv,omitempty" json:"run_argv,omitempty"`
DirectDeploy bool `yaml:"direct_deploy,omitempty" json:"direct_deploy,omitempty"`
EngineBin string `yaml:"engine_bin,omitempty" json:"engine_bin"`
Unmount bool `yaml:"unmount,omitempty" json:"unmount,omitempty"`
Enc RawBody `yaml:"enc,omitempty" json:"enc,omitempty"`
Tunnel *TunnelConfig `yaml:"tunnel,omitempty" json:"tunnel,omitempty"`
}
type PodLifecycleReply ¶ added in v0.2026217.301
type PodLifecycleReply struct {
ExitCode int `yaml:"exit_code,omitempty" json:"exit_code,omitempty"`
}
#PodLifecycleReply is the "pod-lifecycle" host-builder reply. exit_code is populated only for op="cmd" — the container command's own exit code, so `charly cmd`'s process exit propagates it (the plugin reconstructs an *sdk.ExitCodeError from it) — it cannot ride the HostBuild ERROR return, which stringifies the typed error; it must ride a reply FIELD, exactly as the former __cmd/CliReply.ExitCode path did. Every other op's reply is empty; op-specific progress prints host-side (the compiled-in plugin's HostBuild runs in charly's own process) and failure signals via the error return.
type PodLifecycleRequest ¶ added in v0.2026217.301
type PodLifecycleRequest struct {
Op string `yaml:"op,omitempty" json:"op"`
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
// node is the per-host deploy overlay entry the command:pod / command:cmd plugin ALREADY
// resolved plugin-side (loaderkit.ResolveLifecycleDeployNodeViaExecutor, the cycle-free
// plugin-side overlay read) and threads as DATA — so the host's dispatchLifecycleTarget
// operates on the passed *spec.Deploy instead of re-reading the per-host config itself (the
// config-READ is a plugin loading capability, not a host M — #55 K4 seam-completion). Six of
// the eight ops carry it (start/stop/shell/logs/service/cmd); update threads a whole merged
// tree instead (#PodUpdatePayload.tree_json) and remove needs no node at all (it only
// releases the arbiter claim) — absent for those two.
Node *Deploy `yaml:"node,omitempty" json:"node,omitempty"`
// payload is the op-specific #PodXPayload, JSON-marshalled by the calling command plugin and
// re-decoded host-side once op is known (mirrors the plugin wire protocol's own
// Operation.Params json.RawMessage design — there is no parallel envelope-vs-payload type
// system, R3).
Payload RawBody `yaml:"payload,omitempty" json:"payload,omitempty"`
}
#PodLifecycleRequest is the ONE discriminated request every pod-lifecycle HostBuild op (start/stop/shell/logs/service/cmd/update/remove) sends over the single "pod-lifecycle" HostBuild kind (#55 W3 A10b). Converges the seam on the codebase's own established op-discriminated wire idiom — #ArbiterInvokeInput's flat action-multiplexed shape, charly/provider.go's own Operation.Params json.RawMessage envelope — which the former 8-per-verb #PodXRequest family (one CUE type + one HostBuild kind string per verb, each redeclaring box/instance/node) was the last outlier against. op selects which #PodXPayload type `payload` unmarshals into (host_build_pod_lifecycle_dispatch.go's hostBuildPodLifecycle switch); box/instance/node — common to nearly every op — hoisted OUT of the per-op payloads into this shared envelope (R3).
type PodLiveStdioPlan ¶
type PodLiveStdioPlan struct {
// resolved venue command:
//
// shell → `podman exec -it charly-<box> bash [-c cmd]` OR the ephemeral `podman run --rm -it … bash`
// cmd → `<engine> exec [-e env] charly-<box>[-<sidecar>] sh -c <command>` (no -t; stdin piped)
// logs → `<engine> logs [-f] [-n N] charly-<box>` OR quadlet `journalctl --user -u <svc> [-f] [-n N]`
Script string `yaml:"script,omitempty" json:"script"`
}
#PodLiveStdioPlan is the F12 LIVE-STDIO carrier — ONE carrier for shell + cmd + logs (identical shape, R3; the op + the executor method distinguish them). Formerly host-resolved and threaded on OpAttach/OpLogs op.Params; candy/plugin-deploy-pod now SELF-RESOLVES it (resolve_f12.go's resolveAttachPlan/resolveShellPlan/resolveCmdPlan/resolveLogsPlan) from the deploy key + #PodAttachOpts/#PodLogsOpts, so this type is now built AND consumed entirely within the plugin process — no wire crossing for the plan itself, only for the opts that drive it. The plugin EXECUTES it over the served host executor via exec.RunInteractive (OpAttach — inherited LIVE stdin/stdout/stderr; the child `podman exec -it`/`-i` owns the PTY + resize + Ctrl-C) / exec.RunStream (OpLogs — inherited LIVE stdout/stderr, no stdin). UNARY: the host reverse-server runs IN the charly process, so os.Stdin/os.Stdout = the operator's terminal — stdio NEVER crosses the wire, only the session exit code (the hostBuildCli doctrine). This takes the F12 exit for the shell/cmd/logs-follow rows of the #57 M-core register: the former inline `charly shell`/`cmd` bodies + the podCli("logs") reentry are DELETED (bodies, not shells).
type PodLogsOpts ¶
type PodLogsOpts struct {
Follow bool `yaml:"follow,omitempty" json:"follow,omitempty"`
Tail int `yaml:"tail,omitempty" json:"tail,omitempty"`
Sidecar string `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
}
#PodLogsOpts carries `charly logs [-f]`'s parameters (K4/F12 inversion), replacing the former host-side resolvePodLogsPlan. Mirrors charly-core's substrate-agnostic LogsOpts (Follow/Tail/ Sidecar) — a plugin-facing wire twin, since LogsOpts itself is a hand-written charly-core type with no CUE def.
type PodLogsPayload ¶ added in v0.2026217.301
type PodLogsPayload struct {
Follow bool `yaml:"follow,omitempty" json:"follow,omitempty"`
Sidecar string `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
}
#PodLogsPayload — see #PodLifecycleRequest's header; op="logs". The former LogsCmd's authored fields (F12 — the host resolves the journalctl/`<engine> logs` stream command, the owning plugin streams it live to the operator's stdio).
type PodRemovePayload ¶ added in v0.2026217.301
type PodRemovePayload struct {
Purge bool `yaml:"purge,omitempty" json:"purge,omitempty"`
KeepDeploy bool `yaml:"keep_deploy,omitempty" json:"keep_deploy,omitempty"`
Env []string `yaml:"env,omitempty" json:"env,omitempty"`
}
#PodRemovePayload — see #PodLifecycleRequest's header; op="remove". The former RemoveCmd's authored fields — the host orchestration this ONE op still performs is just the arbiter-release bracket (host_build_pod_lifecycle_dispatch.go's hostBuildPodRemove); the rest of remove's orchestration (quadlet/companion-service teardown, pre_remove hooks, purge, deploy-entry cleanup) runs entirely in candy/plugin-pod now.
type PodResolveInput ¶
type PodResolveInput struct {
Pod RawBody `yaml:"pod,omitempty" json:"pod"`
}
#PodResolveInput carries one opaque pod template body to project.
type PodResolveReply ¶
type PodResolveReply struct {
Resolved *ResolvedPod `yaml:"resolved,omitempty" json:"resolved,omitempty"`
}
#PodResolveReply wraps the resolved pod template.
type PodServicePayload ¶ added in v0.2026217.301
type PodServicePayload struct {
Argv []string `yaml:"argv,omitempty" json:"argv"`
}
#PodServicePayload — see #PodLifecycleRequest's header; op="service". Carries the FULLY plugin-resolved argv for `charly service start/stop/status/restart` (Cutover B unit 2 completion): the plugin now performs resolveServiceInit/validateServiceName/execInitCommand's argv-building itself (all portable — spec.ResolvedInit is already an sdk alias, buildkit.RenderTemplate is sdk-portable) and sends the FINAL `<engine> exec <container> <tool> <op> [svc]` argv; the host does ONLY the irreducible dispatchLifecycleTarget + LifecycleTarget.Shell step.
type PodShellOpts ¶
type PodShellOpts struct {
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
EnvFile string `yaml:"env_file,omitempty" json:"env_file,omitempty"`
Env []string `yaml:"env,omitempty" json:"env,omitempty"`
VolumeFlag []string `yaml:"volume_flag,omitempty" json:"volume_flag,omitempty"`
Bind []string `yaml:"bind,omitempty" json:"bind,omitempty"`
NoAutoDetect bool `yaml:"no_auto_detect,omitempty" json:"no_auto_detect,omitempty"`
Interactive bool `yaml:"interactive,omitempty" json:"interactive"`
WrapPTY bool `yaml:"wrap_pty,omitempty" json:"wrap_pty"`
}
#PodShellOpts carries `charly shell`'s per-invocation CLI extras (K4/F12 inversion): the plugin self-resolves the #PodLiveStdioPlan using these + the deploy key, replacing the former host-side resolvePodShellPlan/buildShellArgs/buildExecArgs. interactive/wrap_pty are HOST-RESOLVED booleans (interactive = force_tty || isTerminal(); wrap_pty = force_tty && !isTerminal()) — the plugin is a subprocess whose own stdout is not the operator's terminal, so the tty check MUST happen host-side at the moment of the real CLI invocation and cross the wire as data, never be re-derived plugin-side.
type PodShellPayload ¶ added in v0.2026217.301
type PodShellPayload struct {
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
Command string `yaml:"command,omitempty" json:"command,omitempty"`
Build bool `yaml:"build,omitempty" json:"build,omitempty"`
TTY bool `yaml:"tty,omitempty" json:"tty,omitempty"`
Env []string `yaml:"env,omitempty" json:"env,omitempty"`
EnvFile string `yaml:"env_file,omitempty" json:"env_file,omitempty"`
VolumeFlag []string `yaml:"volume_flag,omitempty" json:"volume_flag,omitempty"`
Bind []string `yaml:"bind,omitempty" json:"bind,omitempty"`
NoAutoDetect bool `yaml:"no_autodetect,omitempty" json:"no_autodetect,omitempty"`
}
#PodShellPayload — see #PodLifecycleRequest's header; op="shell". The former ShellCmd's authored fields (F12 — the host resolves the venue command, the owning plugin runs it over the served venue executor via RunInteractive, stdio host-held).
type PodSidecar ¶
#PodSidecar mirrors Go SidecarConfig: a single-key wrapper around a map of named sidecar templates. CLOSED (the struct has exactly one field).
type PodStartOpts ¶
type PodStartOpts struct {
Env []string `yaml:"env,omitempty" json:"env,omitempty"`
EnvFile string `yaml:"env_file,omitempty" json:"env_file,omitempty"`
Port []string `yaml:"port,omitempty" json:"port,omitempty"`
VolumeFlag []string `yaml:"volume_flag,omitempty" json:"volume_flag,omitempty"`
Bind []string `yaml:"bind,omitempty" json:"bind,omitempty"`
NoAutoDetect bool `yaml:"no_auto_detect,omitempty" json:"no_auto_detect,omitempty"`
}
#PodStartOpts carries `charly start`'s direct-mode CLI extras (K4 inversion, quadlet-mode first): the plugin now SELF-RESOLVES the #PodLifecyclePlan (over the deploy-overlay HostBuild seam + the already-portable sdk resolvers) using these opts + the deploy key already on lifecycleParams.Name — replacing the former host-side resolvePodStartPlan. The quadlet path ignores every field (mirrors the pre-inversion contract — CLI extras apply only to direct mode).
type PodStartPayload ¶ added in v0.2026217.301
type PodStartPayload struct {
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
Build bool `yaml:"build,omitempty" json:"build,omitempty"`
Env []string `yaml:"env,omitempty" json:"env,omitempty"`
EnvFile string `yaml:"env_file,omitempty" json:"env_file,omitempty"`
Port []string `yaml:"port,omitempty" json:"port,omitempty"`
VolumeFlag []string `yaml:"volume_flag,omitempty" json:"volume_flag,omitempty"`
Bind []string `yaml:"bind,omitempty" json:"bind,omitempty"`
NoAutoDetect bool `yaml:"no_autodetect,omitempty" json:"no_autodetect,omitempty"`
}
#PodStartPayload — see #PodLifecycleRequest's header; op="start". The former StartCmd's authored fields (DEPLOY-wave CLI-struct port): the command:pod plugin owns the CLI GRAMMAR but cannot drive the LifecycleTarget dispatch (ResolveTarget, the plugin loader — core Mechanisms), so `charly start`'s command is THIN — it forwards these flags, and the host runs the existing startViaLifecycle orchestration VERBATIM, exactly as `charly bundle add` stayed core behind HostBuild("resolve-target-add").
type PodStopOpts ¶
type PodStopOpts struct {
Unmount bool `yaml:"unmount,omitempty" json:"unmount,omitempty"`
}
#PodStopOpts carries `charly stop --unmount` (K4 inversion): the plugin self-resolves the STOP #PodLifecyclePlan using this + the deploy key, replacing the former host-side resolvePodStopPlan.
type PodStopPayload ¶ added in v0.2026217.301
type PodStopPayload struct {
Unmount bool `yaml:"unmount,omitempty" json:"unmount,omitempty"`
}
#PodStopPayload — see #PodLifecycleRequest's header; op="stop". The former StopCmd's authored fields.
type PodUpdatePayload ¶ added in v0.2026217.301
type PodUpdatePayload struct {
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
Build bool `yaml:"build,omitempty" json:"build,omitempty"`
Seed bool `yaml:"seed,omitempty" json:"seed,omitempty"`
ForceSeed bool `yaml:"force_seed,omitempty" json:"force_seed,omitempty"`
DataFrom string `yaml:"data_from,omitempty" json:"data_from,omitempty"`
// tree_json is the merged project+operator deploy tree command:update (plugin-pod) resolved
// PLUGIN-SIDE (loaderkit.ResolveMergedTreeViaExecutor) — threaded as DATA so the host
// dispatchByDeployTarget consumes it instead of re-loading the tree host-side (#55
// Cone A Unit 3b). Marshalled map[string]spec.Deploy; an absent tree yields the same
// "no charly.yml" error a nil host-tree-read result produced.
TreeJSON RawBody `yaml:"tree_json,omitempty" json:"tree_json,omitempty"`
}
#PodUpdatePayload — see #PodLifecycleRequest's header; op="update".
type PollClass ¶
type PollClass int
PollClass selects the tick-interval band only (never the deadline). Local = in-process/loopback probes (podman exec, libvirt domain-state, supervisord); Remote = one-ssh/network-round-trip probes; Heavy = full-suite subprocess probes (each tick runs a whole `charly check live` pass).
type PollCondition ¶
PollCondition is the caller's readiness probe, invoked once per tick under a per-attempt timeout context.
ready — the awaited state is reached; PollUntil returns nil.
progress — a MONOTONIC forward-motion score (higher == more progress, e.g. a
count or an AI score). The no-progress watchdog resets only when
this reaches a NEW HIGH-WATER value, so oscillation/regression
cannot masquerade as progress. Ignored in cap-only mode
(NoProgress<=0) — pass 0 for binary/edge sites.
err — wrap ErrPollFatal to abort IMMEDIATELY (unrecoverable: process
died, auth/permission error). Any other non-nil err is transient
(target not up yet): routed to OnTransient, treated as not-ready,
and does NOT advance the high-water mark.
type PollConfig ¶
type PollConfig struct {
Name string // label woven into log lines + errors, e.g. "ssh-ready check-k3s-vm"
Interval time.Duration // tick cadence (pacing only; never the deadline). >0 required.
PerAttempt time.Duration // per-cond context timeout (the never-hang kill-switch for a blocking probe). >0 required.
NoProgress time.Duration // monotonic stall window; 0 disables (cap-only mode).
AbsoluteCap time.Duration // generous never-hang ceiling; 0 means "no cap" (NoProgress must then be >0).
// OnTick (optional) is host-side observability per tick. NEVER AI-visible
// (matches check_watchdog.go doctrine). sinceAdvance is time since the last
// high-water advance (0 in cap-only mode).
OnTick func(elapsed time.Duration, progress float64, sinceAdvance time.Duration)
// OnTransient (optional) receives each transient (non-fatal) cond error.
OnTransient func(err error)
// contains filtered or unexported fields
}
PollConfig parameterizes one PollUntil call. Build it via ResolvedReadiness's Wait / WaitCapped / StopGate so no call site carries a magic literal.
type PortMapping ¶
type PortMapping struct {
HostIP string `yaml:"host_ip,omitempty" json:"host_ip,omitempty"`
HostPort int `yaml:"host_port,omitempty" json:"host_port"`
CtrPort int `yaml:"container_port,omitempty" json:"container_port"`
Proto string `yaml:"protocol,omitempty" json:"protocol"`
}
#PortMapping — one published port's structured runtime mapping (host IP/port -> container port/proto). Surfaces on #DeploymentStatus so renderers + host probes consume it without re-parsing.
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) MarshalJSON ¶
func (PortScope) MarshalYAML ¶
func (*PortScope) UnmarshalJSON ¶
type PortSpec ¶
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 ¶
type PostTeardownReply struct {
RemoveEntries []string `yaml:"remove_entries,omitempty" json:"remove_entries,omitempty"`
}
#PostTeardownReply is the OpPostTeardown reply: the host removes each named charly.yml deploy-entry key AFTER the plugin's teardown.
type PreemptLease ¶
type PreemptLease struct {
Claimant string `yaml:"claimant,omitempty" json:"claimant"`
Claim HolderAddr `yaml:"claim,omitempty" json:"claim"`
Tokens []string `yaml:"tokens,omitempty" json:"tokens"`
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
Transient bool `yaml:"transient,omitempty" json:"transient"`
Preempted []PreemptedHolder `yaml:"preempted,omitempty" json:"preempted"`
Created string `yaml:"created,omitempty" json:"created"`
// owner_pid/owner_start identify the OUTERMOST process that created the lease — the liveness
// signal a concurrent charly process's reconcile uses (leaseLive).
OwnerPID int64 `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,omitempty" json:"leases"`
}
#PreemptLedger is the on-disk lease set (~/.local/share/charly/preemption/leases.yml).
type PreemptedHolder ¶
type PreemptedHolder struct {
Addr HolderAddr `yaml:"addr,omitempty" json:"addr"`
Holds []string `yaml:"holds,omitempty" json:"holds"`
Restore string `yaml:"restore,omitempty" json:"restore"`
}
#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 ¶
type PrepareVenueReply struct {
Venue VenueDescriptor `yaml:"venue,omitempty" json:"venue"`
State RawBody `yaml:"state,omitempty" json:"state,omitempty"`
Notes []string `yaml:"notes,omitempty" 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; Notes are human-facing lines the host prints.
type Process ¶
type Process interface {
Stdin() io.WriteCloser
Stdout() io.ReadCloser
Stderr() io.ReadCloser
Wait() error
Close() error
}
Process is one exact-argv, full-duplex child running in a deployment venue. It is deliberately a behaviour contract rather than a wire shape: target routes and all data sent over the process are CUE-generated, while this seam only exposes the OS pipes that carry the generated gRPC protocol.
Close must be idempotent, close stdin, terminate the complete child process group when it has not exited, and reap it. Wait may be called more than once.
type ProcessExecutor ¶
type ProcessExecutor interface {
StartProcess(ctx context.Context, launch ProcessLaunch) (Process, error)
}
ProcessExecutor is an optional capability implemented by deployment executors that can host a bidirectional process. Keeping it separate from DeployExecutor preserves compatibility with capture-only executors and test doubles. Callers must return ErrNotSupported when this capability is absent.
type ProcessLaunch ¶
type ProjectLoader ¶ added in v0.2026213.1726
type ProjectLoader interface {
LoadUnified(dir string, exec LoaderExecutor) (*UnifiedFile, bool, error)
// DecodeEntityViaCUE is the kind-blind per-entity CUE decode mechanism (shorthand normalize +
// CUE-ingest + Decode), relocated to loaderkit (K1 unit 1): normalizes node against t's shape
// (shorthand expansion, scalar→string coercion), then CUE-decodes the result into out. node must
// BE the entity value (a candy body / a single kind entity / an assembled node-form body), not a
// kind-keyed wrapper; does not mutate the input node. Compiled-in only (the loader is
// bootstrap-critical), no wire envelope — every kind/candy/node-form decode in charly core
// routes through this seam instead of importing loaderkit directly.
DecodeEntityViaCUE(node *yaml.Node, t reflect.Type, out any, label string) error
// ValidateEntityClosedCUE unifies a single entity with #<Kind> (cs.KindDef(kind)) and validates
// it WITHOUT requiring concreteness — closedness violations (unknown keys) and type/enum/regex
// conflicts, but not missing-required fields (K1 unit 2 relocation).
ValidateEntityClosedCUE(cs CueSchema, kind, label string, entity cue.Value) error
// CueDocFromYAML ingests one YAML document into a cue.Value (the whole doc), built with cs.Ctx
// so the result can Unify against cs.Root's definitions (K1 unit 2 relocation).
CueDocFromYAML(cs CueSchema, path string, data []byte) (cue.Value, error)
// ValidateNodeDocCUE validates a unified node-form document (raw YAML bytes) by unifying EACH
// top-level entity node against #Node — the load-time "validate-before-execute" structural gate
// (K1 unit 2 relocation).
ValidateNodeDocCUE(cs CueSchema, label string, data []byte) error
// ApplyCueDefaults fills schema-declared defaults into an already-RESOLVED entity by unifying
// its marshaled form with #<Kind> (cs.KindDef(kind)) and decoding back (K1 unit 2 relocation).
ApplyCueDefaults(cs CueSchema, kind string, out any) error
// ResolveMergedDeployTree returns the top-level Bundle (deploy-node) map — the merged project
// charly.yml + per-host operator overlay, ready for dotted-path traversal — the host-side
// merged-tree read the check host seams (deployNodePluginContext + check_venue_resolve) need.
// It is the merged-tree sibling of LoadUnified: LoadUnified returns the PROJECT-only tree
// (loadmodel.go Bundle has no overlay field), so a caller that needs the per-host operator
// overlay merged in routes through THIS seam instead. The merge LOGIC (the loaderkit
// project+overlay projection+merge) stays in the ONE copy in sdk/loaderkit
// (loaderkit.ResolveMergedTreeViaExecutor); the host reaches it through this compiled-in seam
// instead of importing loaderkit directly (#55 coneA Q2(1) — charly core's check_cmd.go sheds
// its loaderkit import). The in-proc executor is threaded on ctx via sdk.ContextWithExecutor
// (the SAME in-proc reverse-channel path ExecutorForInvoke uses for Invoke) so the seam
// signature stays spec-typed — the plugin-side impl retrieves it via sdk.ExecutorFromContext.
// Compiled-in only (the loader is bootstrap-critical), no wire envelope.
ResolveMergedDeployTree(ctx context.Context, dir string) (map[string]BundleNode, error)
// MaterializeLoadedProject replays the whole-project per-document/per-namespace MATERIALIZE +
// root-wins MERGE over a walk envelope, driving loaderkit's kind-blind orchestration with the
// host-supplied per-node seams — so charly core reaches materialize WITHOUT importing loaderkit
// (#55 2b C3). The host's own LoadUnified path AND the loader-materialize HostBuild seam (serving a
// plugin-side loader) both route through here.
MaterializeLoadedProject(lp *LoadedProject, merged *UnifiedFile, byID map[int64]*UnifiedFile, seams MaterializeProjectSeams) error
// MarshalMaterialized marshals a materialized UnifiedFile into the wire envelope the
// loader-materialize HostBuild seam returns to a plugin-side loader (it captures the nested
// plugin-kind maps loaderkit-internally, so the host reaches it through this seam).
MarshalMaterialized(uf *UnifiedFile) ([]byte, error)
// ValidateAndroidDevices enforces the kind:android box⊻adb XOR; the host supplies the
// registry-resolve callback (the validation LOGIC stays in loaderkit).
ValidateAndroidDevices(uf *UnifiedFile, resolveAndroid func(json.RawMessage) (*ResolvedAndroid, error)) error
// ValidatePreemptible validates preemptible / requires_exclusive / requires_shared across the
// deploy map; the host supplies the registry-resolve callbacks (the validation LOGIC stays in
// loaderkit).
ValidatePreemptible(uf *UnifiedFile, resolveResource func(json.RawMessage) (*ResolvedResource, error), resolveVm func(json.RawMessage) (*ResolvedVm, error)) error
// ScanCandyFromLocal runs the candy-scan fetch fix-point (remote-ref collect, fetch, per-entity
// version arbitration, host-completion + finalize) over a local candy set, driving the host-coupled
// legs through the caller-supplied ScanSeams closures — so charly core reaches the scan MECHANISM
// through this compiled-in seam instead of importing loaderkit (#55 C3b-ii). The scan LOGIC stays in
// the ONE copy in sdk/loaderkit (candy/plugin-build reaches it directly, being a plugin).
ScanCandyFromLocal(localScanned map[string]ScannedCandy, initCfg *InitConfig, seams ScanSeams) (map[string]CandyReader, error)
// RunDiscover walks the flat generic discover: scan-spec list, parsing each discovered manifest via
// the host-supplied WalkSeams — the discover half of the loader mechanism, reached via the seam so
// charly core never imports loaderkit for it.
RunDiscover(rootDir string, specs []ScanSpec, seams WalkSeams) ([]DiscoveredManifest, error)
// FinalizeScannedCandies is the scan pipeline's finalize choke point (host-completion + bare-string
// the refs + wrap into the FINAL CandyReader). It is deploykit-coupled and stays in loaderkit; the
// host reaches it through this seam — the dominant shared choke point across charly's scan call sites.
FinalizeScannedCandies(scanned map[string]ScannedCandy, initCfg *InitConfig) map[string]CandyReader
// IsResourceDisc reports whether a discriminator names a deploy-substrate kind (the markers of a
// bundle member / bundle-shaped node) — the CUE-derived #ResourceKind vocab, OR a recognized
// external deploy substrate word (t.DeploySubstrates).
IsResourceDisc(d string, t Threaded) bool
// BundleTargetForDisc maps a node discriminator to the BundleNode Target — DATA-driven via
// t.DeployTraits: a word with no declared deploy traits is TARGETLESS (e.g. group).
BundleTargetForDisc(d string, t Threaded) string
// SetBundleCrossRef sets the deploy's cross-ref from a scalar discriminator value — DATA-driven
// via t.DeployTraits' ImageBacked trait (image-backed → dn.Image; otherwise → dn.From). A
// targetless word (no declared traits) sets neither.
SetBundleCrossRef(dn *BundleNode, disc, ref string, t Threaded)
// IsStandaloneResourceKind reports whether disc names one of the substrate kinds that are BOTH a
// standalone TEMPLATE and a deploy — DATA-driven via t.DeployTraits (same fact
// BundleTargetForDisc/SetBundleCrossRef resolve against).
IsStandaloneResourceKind(disc string, t Threaded) bool
// FoldStandaloneTemplateReply folds a standalone-template kind's echoed reply JSON into acc's
// generic PluginKinds[disc][name] map — the C2-substrate TEMPLATE fold arm, GENERIC by
// construction (no per-kind-word switch).
FoldStandaloneTemplateReply(disc, name string, replyJSON json.RawMessage, acc *MaterializedProject) error
// AssembleEntityBody returns the DOCUMENT-wrapped entity-body mapping to decode: pn's body
// value (an empty mapping when the value is null/absent or a scalar cross-ref).
AssembleEntityBody(pn ParsedNode) (*yaml.Node, error)
// DecodeNodeValue decodes pn's body via the shared CUE entity decoder into out (a *struct) —
// the SAME entity-body assembler + CUE decode every candy/kind/node-form decode goes through.
DecodeNodeValue(pn ParsedNode, out any) error
// EntityBodyJSON returns a node's kind-value mapping as canonical JSON, generically — with NO
// concrete-kind Go type.
EntityBodyJSON(pn ParsedNode) (json.RawMessage, error)
// BuildBundleNode recursively builds a BundleNode from a bundle/resource node.
BuildBundleNode(pn ParsedNode, t Threaded) (*BundleNode, error)
// BuildResourceMemberChildren decodes pn's RESOURCE-MEMBER entity children into a
// name→*BundleNode map via the SAME BuildBundleNode recursion — the SINGLE source of truth for
// authored member-tree decode.
BuildResourceMemberChildren(pn ParsedNode, t Threaded) (map[string]*BundleNode, error)
// BuildBundleNodeInto builds pn into a BundleNode and registers it in acc's Bundle map — the
// fallback for a recognized-but-not-yet-connected external deploy substrate word
// (MaterializeSeams.BuildBundleEntity's implementation).
BuildBundleNodeInto(pn ParsedNode, t Threaded, acc *MaterializedProject) error
// IsDeployShape reports whether a substrate node is a DEPLOY (vs a standalone template).
IsDeployShape(pn ParsedNode) bool
// DecodeStandaloneTemplateJSON canonicalizes pn (a substrate TEMPLATE node) to the JSON the
// host threads to the substrate plugin, GENERICALLY — with NO concrete-kind Go type.
DecodeStandaloneTemplateJSON(pn ParsedNode, t Threaded) (json.RawMessage, error)
// ResourceChildren returns pn's children whose discriminator is itself a resource/bundle kind
// (the CUE-derived #ResourceKind vocab).
ResourceChildren(pn ParsedNode) []ParsedNode
// ValidateCandyManifestCUE validates a candy manifest: the whole-document #NodeDoc structural
// gate, then the parsed+desugared entity-tree walk (ValidateNodeFormSteps).
ValidateCandyManifestCUE(path string, data []byte, t Threaded, parser DocParser, cs CueSchema) error
// ValidateNodeFormSteps parses a node-form document and validates EVERY entity's (and nested
// sub-entity's) assembled body against its closed per-kind def — the step-typo gate for
// candies, boxes, pods, deploys, and check beds alike.
ValidateNodeFormSteps(path string, data []byte, t Threaded, parser DocParser, cs CueSchema) error
// EnsureRepoDownloaded downloads repoPath@version if not already cached and returns the cache
// path, auto-migrating it to the latest schema CalVer via seams.MigrateCache.
EnsureRepoDownloaded(repoPath, version string, seams RefsCollectSeams) (string, error)
// CollectRemoteRefsOpts collects all unique remote refs reachable from cfg's build/deploy
// targets + layers' manifest depends/candy fields, grouped by (repoPath, version).
CollectRemoteRefsOpts(cfg *Config, layers map[string]CandyReader, opts ResolveOpts, seams RefsCollectSeams) ([]RemoteDownload, error)
}
ProjectLoader is the swappable whole-project LOAD-ENTRY seam (#55 loader-keystone) — the terminal loader endpoint every command reaches to load a project's charly.yml. The loader plugin candy implements it (candy/plugin-loader, delegating to loaderkit.LoadUnified over loaderkit.LoadSeamsFromExecutor), and the host resolves the registered loader provider to it and drives LoadUnified THROUGH it — so charly core imports ONLY the dedicated spec module, never the loaderkit mechanism, to load its own config. The host supplies the registry-/host-coupled legs as a LoaderExecutor; the plugin owns the kind-blind orchestration. Typed (no wire envelope), the LOAD-ENTRY sibling of DocParser/ProjectWalker/CandyScanner/Materializer above — the compiled-in placement (the loader MUST always resolve; it is the config front-end) calls it directly, resolved at init() before the first load so there is no bootstrap cycle.
type ProjectTemplates ¶
type ProjectTemplates struct {
Local map[string]RawBody `yaml:"local,omitempty" json:"local,omitempty"`
K8s map[string]RawBody `yaml:"k8s,omitempty" json:"k8s,omitempty"`
Pod map[string]RawBody `yaml:"pod,omitempty" json:"pod,omitempty"`
VM map[string]RawBody `yaml:"vm,omitempty" json:"vm,omitempty"`
Android map[string]RawBody `yaml:"android,omitempty" json:"android,omitempty"`
}
#ProjectTemplates — the bare pod:/vm:/local:/k8s:/android: template maps carried as OPAQUE payloads (the uf.Pod/VM/Local/K8s/Android raw bytes, verbatim). The host projector stays KIND-BLIND — it copies the raw template bytes with NO concrete-kind decode (a kernel that read spec.Local/#Pod/… would violate the boundary law + trip TestNoConcreteKindInKernel). The CONSUMING PLUGINS (validate localtemplates, check-include pod/vm arms, status k8s/adb) decode a RawBody into the concrete spec kind type themselves — a plugin MAY know kinds, the kernel may not.
func (*ProjectTemplates) ByKind ¶ added in v0.2026217.301
func (t *ProjectTemplates) ByKind(kind string) map[string]RawBody
ByKind returns t's namespace-qualified template map for kind (nil for an unknown kind or a nil receiver). This is the ONE place ProjectTemplates' named fields fold back to a kind-keyed lookup — kept here (spec, the shared vocabulary) rather than in the kernel: a kernel caller that needs "the template map for THIS kind" stays genuinely kind-blind by keying on the string it already carries instead of switching on it itself (host_build_deploy_entity_resolve.go, W0).
type ProjectWalker ¶
type ProjectWalker interface {
WalkProject(rootDir string, rootData []byte, rootIdentity string, seams WalkSeams) (LoadedProject, error)
}
ProjectWalker is the swappable WHOLE-PROJECT WALK seam: the loader plugin candy implements it (candy/plugin-loader, delegating to loaderkit.Walk), and the host resolves the registered loader provider to it and calls it once per project load — so an alternative loader plugin serves a different walk mechanism by implementing this. Typed (no wire envelope) since the compiled-in placement passes live Go callbacks (WalkSeams) that cannot cross a JSON envelope. rootData is the (possibly bootstrap-transformed) root document bytes; rootIdentity seeds the namespace cycle-break with the root project's own repo identity.
type ProvidesConfig ¶ added in v0.2026210.808
type ProvidesConfig struct {
Env []EnvProvideEntry `yaml:"env,omitempty" json:"env,omitempty"`
MCP []MCPProvideEntry `yaml:"mcp,omitempty" json:"mcp,omitempty"`
}
ProvidesConfig holds all resolved provides entries in charly.yml.
type RCARecord ¶
type RCARecord struct {
ID UUIDv7 `yaml:"id,omitempty" json:"id"`
IncidentID UUIDv7 `yaml:"incident_id,omitempty" json:"incident_id"`
State string `yaml:"state,omitempty" json:"state"`
Findings []string `yaml:"findings,omitempty" json:"findings,omitempty"`
RootCause string `yaml:"root_cause,omitempty" json:"root_cause,omitempty"`
CompletedAt string `yaml:"completed_at,omitempty" json:"completed_at,omitempty"`
}
type RawBody ¶
type RawBody = json.RawMessage
RawBody is an opaque JSON body the kernel stores and transports WITHOUT typing — a same-package alias for json.RawMessage so a CUE `@go(type=map[string]RawBody)` / `@go(type=RawBody)` override renders an opaque field/map in the generated types with no cross-package import (gengotypes would mis-import a qualified `json.RawMessage`). Moved here from the former hand-written deploy_wire.go (SDD conversion) — it is a scalar alias, not a wire struct, so it belongs beside the other scalar collapses in this file.
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 RebootStep ¶ added in v0.2026210.2308
type RebootStep struct {
CandyName string
}
RebootStep requests a reboot of the deploy target after this candy's steps. It is emitted (last in the candy) when a candy declares `reboot: true` — the canonical case is a kernel-module candy (e.g. nvidia-open-dkms) whose module only loads on a fresh boot with nouveau blacklisted.
Only a rebootable venue acts on it: the external vm deploy reboots the guest over SSH (the host's RunHostStep RebootStep handler, gated on the rebootable-venue flag the vm substrate sets) and waits for the boot_id to change. OCITarget / PodDeployTarget have no machine to reboot at build time and skip it (the external k8s substrate does not consume the IR at all); the external local deploy refuses to reboot the host venue unattended (skip + note). Mirrors the ApkInstallStep "only one target executes" pattern.
func (*RebootStep) Kind ¶ added in v0.2026210.2308
func (s *RebootStep) Kind() StepKind
func (*RebootStep) RequiresGate ¶ added in v0.2026210.2308
func (s *RebootStep) RequiresGate() Gate
func (*RebootStep) Reverse ¶ added in v0.2026210.2308
func (s *RebootStep) Reverse() []ReverseOp
Reverse is empty — a reboot is not a persistent artifact to undo.
func (*RebootStep) Scope ¶ added in v0.2026210.2308
func (s *RebootStep) Scope() Scope
func (*RebootStep) Venue ¶ added in v0.2026210.2308
func (s *RebootStep) Venue() Venue
type RecoveryDecision ¶
type RecoveryDecision struct {
ID UUIDv7 `yaml:"id,omitempty" json:"id"`
IncidentID UUIDv7 `yaml:"incident_id,omitempty" json:"incident_id"`
RCAID UUIDv7 `yaml:"rca_id,omitempty" json:"rca_id,omitempty"`
Action string `yaml:"action,omitempty" json:"action"`
AuthorizedEmergencyAbort bool `yaml:"authorized_emergency_abort,omitempty" json:"authorized_emergency_abort,omitempty"`
Params *RecoveryParams `yaml:"params,omitempty" json:"params,omitempty"`
State string `yaml:"state,omitempty" json:"state"`
DecidedAt string `yaml:"decided_at,omitempty" json:"decided_at"`
AppliedAt string `yaml:"applied_at,omitempty" json:"applied_at,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
type RecoveryParams ¶
type RecoveryParams struct {
RunID UUIDv7 `yaml:"run_id,omitempty" json:"run_id,omitempty"`
SessionID UUIDv7 `yaml:"session_id,omitempty" json:"session_id,omitempty"`
Target *TargetSpec `yaml:"target,omitempty" json:"target,omitempty"`
TerminalProfile *TerminalProfile `yaml:"terminal_profile,omitempty" json:"terminal_profile,omitempty"`
Provider string `yaml:"provider,omitempty" json:"provider,omitempty"`
Prompt string `yaml:"prompt,omitempty" json:"prompt,omitempty"`
Runtime string `yaml:"runtime,omitempty" json:"runtime,omitempty"`
Deployment string `yaml:"deployment,omitempty" json:"deployment,omitempty"`
Note string `yaml:"note,omitempty" json:"note,omitempty"`
}
type RefsCollectSeams ¶ added in v0.2026217.301
type RefsCollectSeams struct {
// Downloader is the registered remote-repo fetch backend (P7) — the host resolves it once
// (requireRefsDownloader()) and passes it in; a cache-miss download dispatches through it.
Downloader RefsDownloader
// MigrateCache brings a remote-repo cache's PROJECT files up to the head schema via the
// compiled-in command:migrate plugin — registry-coupled (resolves ClassCommand "migrate" +
// Invoke), so it stays a host-supplied callback.
MigrateCache func(path string) error
// ResolveLocal projects one opaque `kind:local` template body into a *ResolvedLocal via
// candy/plugin-substrate's OpResolve leg — registry-coupled (Invoke), so it stays a
// host-supplied callback.
ResolveLocal func(body json.RawMessage) (*ResolvedLocal, error)
// OverrideEnvValue is the raw CHARLY_REPO_OVERRIDE env value (RDD local-overrides) — the host
// reads os.Getenv once; this mechanism never touches the env var NAME (spec/proc.RepoOverrideEnv,
// #55 W3 B2-full — shared by charly core's plugin_loader.go and candy/plugin-check's
// bed_session.go, both of which read/write it independently).
OverrideEnvValue string
}
RefsCollectSeams is the set of host-supplied callbacks EnsureRepoDownloaded/ CollectRemoteRefsOpts need for everything registry-coupled — the host builds this value and hands it to the ProjectLoader seam call; the mechanism never touches the provider registry directly (boundary law clause M: the resolve+invoke dispatch stays host-side, reached through these callbacks, exactly like WalkSeams/MaterializeSeams above).
type RefsDownloader ¶
type RefsDownloader interface {
// Download fetches repoPath@version into the local repo cache and returns the cache path.
// Called only on a cache MISS (the host checks IsRepoCached first).
Download(repoPath, version string) (string, error)
}
RefsDownloader is the swappable remote-repo FETCH BACKEND seam (P7): the host dispatches every cache-miss download through a RefsDownloader; the DEFAULT (candy/plugin-refs, delegating to kit.DownloadRepo) fetches via git, and an alternative refs plugin can serve a different backend (OCI/S3-hosted candies) by registering a different RefsDownloader. Mirrors DocParser/ProjectWalker/ CandyScanner above: a typed interface a compiled-in plugin implements alongside its provider, so the host calls it in-proc with no wire envelope. Relocated from sdk/kit (FLOOR-SLIM axis-A mechanical batch) — the interface contract itself is kind-blind and registry-decoupled; the default git-fetch backend (kit.DefaultDownloader) stays in kit, wrapping the spec/refs.DownloadRepo git primitive (relocated to the spec/refs fabric slice, re-exported by kit for existing callers).
type RemoteDownload ¶ added in v0.2026213.1726
type RemoteDownload struct {
RepoPath string
Version string
Refs []string // bare refs to import (e.g. "github.com/org/repo/candy/name")
}
RemoteDownload represents a unique (repo, version) pair to download in the remote-layer resolver's candy-scan fix-point, plus the bare refs to import from it. A pure data descriptor over strings, relocated to the dedicated spec module (#55 2b Class A) so charly's remote-resolver files (refs.go) reach it without importing loaderkit; loaderkit aliases it for the scan mechanism that produces it.
type RemoteImageResolveReply ¶
type RemoteImageResolveRequest ¶
type RemoteImageResolveRequest struct {
Ref string `yaml:"ref,omitempty" json:"ref"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
}
#RemoteImageResolveRequest / #RemoteImageResolveReply — the "remote-image-resolve" HostBuild seam: clone/cache an @github.com/org/repo/box ref's source + return the cached dir + short box name. The host does ONLY the git clone/cache (EnsureRepoDownloaded, K1/B floor); the calling plugin resolves the registry pull ref itself via the K1 loader reverse legs (K1 loader wave — sheds deploykit.ResolveSpecBox from charly core; the former image_ref field is GONE).
type RenderSeamReply ¶
type RenderSeamReply struct {
Result []byte `yaml:"result,omitempty" json:"result,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
#RenderSeamReply carries the opaque JSON result + the reply-error convention.
type RenderSeamRequest ¶
type RenderSeamRequest struct {
Method string `yaml:"method,omitempty" json:"method"`
Params []byte `yaml:"params,omitempty" json:"params,omitempty"`
}
#RenderSeamRequest is the generic host↔plugin render-seam dispatch (#67 render-DRIVE move). plugin-build wires the deploykit.Generator seams via HostBuild("render-seam") with a method discriminator + opaque JSON params. The host dispatches by method to the corresponding core function. This is the SINGLE HostBuild kind for ALL render seams that need host callbacks (RenderService, ValidateEgress, EmitPluginOp, etc.) — one CUE type, many methods.
type RenderedService ¶
type RenderedService struct {
UnitText string `yaml:"unit_text,omitempty" json:"unit_text,omitempty"`
UnitPath string `yaml:"unit_path,omitempty" json:"unit_path,omitempty"`
DropinText string `yaml:"dropin_text,omitempty" json:"dropin_text,omitempty"`
DropinPath string `yaml:"dropin_path,omitempty" json:"dropin_path,omitempty"`
}
#RenderedService is the renderer output: the unit text, where it lands, and any drop-in for packaged-unit reuse.
type RepoChangeStep ¶ added in v0.2026210.2308
type RepoChangeStep struct {
Format string // "rpm" | "deb" | "pac"
File string // absolute path of the repo file
Content string // rendered repo file body
Checksum string // sha256 of Content, for idempotency check at install
CandyName string
}
RepoChangeStep records a repo config file mutation (e.g. adding rpmfusion-free.repo to /etc/yum.repos.d/). Distinct from SystemPackagesStep's PhasePrepare because the compiler often synthesizes these from `cmd:` tasks that happen to install a -release.rpm — we want them tracked separately so `charly bundle del` can reverse them precisely.
func (*RepoChangeStep) Kind ¶ added in v0.2026210.2308
func (s *RepoChangeStep) Kind() StepKind
func (*RepoChangeStep) RequiresGate ¶ added in v0.2026210.2308
func (s *RepoChangeStep) RequiresGate() Gate
func (*RepoChangeStep) Reverse ¶ added in v0.2026210.2308
func (s *RepoChangeStep) Reverse() []ReverseOp
func (*RepoChangeStep) Scope ¶ added in v0.2026210.2308
func (s *RepoChangeStep) Scope() Scope
func (*RepoChangeStep) Venue ¶ added in v0.2026210.2308
func (s *RepoChangeStep) Venue() Venue
type RepoSpec ¶ added in v0.2026210.2308
type RepoSpec struct {
// Fields captured verbatim from the candy manifest. Different formats use
// different subsets (rpm has url/id/key; deb has suite/components;
// pac has url/keys). Carried as map so the template-rendered host
// and container forms can pick what they need.
Raw map[string]any
}
type ResolveOpts ¶ added in v0.2026213.1726
type ResolveOpts struct {
IncludeDisabled bool // skip the `enabled: false` check
IncludeDisabledNames map[string]bool // when non-empty, scope IncludeDisabled to these names only
// RequestedBoxes are the explicit build targets (`charly box build <name>`). A qualified name
// here (e.g. `charly.arch-builder`) is pulled into the resolved set even when it isn't reachable
// as a base/builder of a root image — so a namespaced image can be an on-demand build target, not
// only a transitive base. Bare names are ignored here (they resolve through the root loop).
RequestedBoxes []string
// ExtraCandyRefs are candy refs to collect IN ADDITION to the image/builder/kind:local-template
// closure — specifically a DEPLOY's `add_candy:` candies. The image-closure walk (collectBox)
// never reaches them, so a bed that add_candy's a host-side PLUGIN candy must pass its add_candy
// refs here, or the plugin never enters the candy scan and loadProjectPlugins can't build/connect
// it. NEVER read by the buildkit resolvers — consumed solely by the candy scan.
ExtraCandyRefs []string
// InitCfg is the project init: vocabulary (W9), threaded through so the candy scan can run the
// cross-candy init-system host-completion pass (PopulateCandyInitSystem) BEFORE wrapping each
// candy into the FINAL CandyReader. A caller that leaves this nil skips the pass (correct only for
// a caller with no init-aware consumer downstream). NEVER read by the buildkit resolvers.
InitCfg *InitConfig
// DistroCfg / BuilderCfg are the project's build vocabulary (distro:/builder:), threaded through
// so a resolve does not re-run the project load on every call (a caller with the triple, or a
// multi-box loop, sets it once and skips the redundant reload; nil is byte-identical fallback).
DistroCfg *DistroConfig
BuilderCfg *BuilderConfig
}
ResolveOpts carries the scan/load options threaded through the candy scan + project resolution.
func (ResolveOpts) ShouldIncludeDisabled ¶ added in v0.2026213.1726
func (opts ResolveOpts) ShouldIncludeDisabled(name string) bool
ShouldIncludeDisabled reports whether name's disabled gate should be bypassed under opts. Centralizes the IncludeDisabled + IncludeDisabledNames interaction so call sites stay simple.
type ResolvedAndroid ¶
type ResolvedAndroid 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"`
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Adb *AdbEndpoint `yaml:"adb,omitempty" json:"adb,omitempty"`
Raw RawBody `yaml:"raw,omitempty" json:"raw,omitempty"`
}
#ResolvedAndroid is the resolve-to-envelope form of an `android:` device template.
func (*ResolvedAndroid) EffectiveSerial ¶
func (a *ResolvedAndroid) EffectiveSerial() string
EffectiveSerial returns the device serial, defaulting to the emulator serial.
func (*ResolvedAndroid) IsEndpoint ¶
func (a *ResolvedAndroid) IsEndpoint() bool
IsEndpoint reports whether the resolved device targets a remote adb endpoint.
type ResolvedBox ¶ added in v0.2026210.1533
type ResolvedBox struct {
Name string `yaml:"name,omitempty" json:"name"`
// version is the authored per-entity CalVer (the box config `version:`); optional.
Version string `yaml:"version,omitempty" json:"version,omitempty"`
// effective_version is the content-derived identity emitted as the ai.opencharly.version
// label: the dedicated version if set, else the highest candy version across the full
// chain. Stable across builds when no candy changed.
EffectiveVersion string `yaml:"effective_version,omitempty" json:"effective_version,omitempty"`
Status string `yaml:"status,omitempty" json:"status,omitempty"`
Info string `yaml:"info,omitempty" json:"info,omitempty"`
CheckLevel string `yaml:"check_level,omitempty" json:"check_level,omitempty"`
Base string `yaml:"base,omitempty" json:"base"`
// from mirrors BoxConfig.From after resolution. When non-empty (e.g. "builder:pacstrap"),
// the generator emits FROM scratch + ADD <staged-rootfs.tar.gz> instead of FROM <base>.
From string `yaml:"from,omitempty" json:"from,omitempty"`
BootstrapBuilderImage string `yaml:"bootstrap_builder_image,omitempty" json:"bootstrap_builder_image,omitempty"`
Platforms []string `yaml:"platforms,omitempty" json:"platforms"`
Tag string `yaml:"tag,omitempty" json:"tag"`
Registry string `yaml:"registry,omitempty" json:"registry"`
Pkg string `yaml:"pkg,omitempty" json:"pkg"`
Distro []string `yaml:"distro,omitempty" json:"distro"`
BuildFormats []string `yaml:"build_formats,omitempty" json:"build_formats"`
Tags []string `yaml:"tags,omitempty" json:"tags"`
Candy []string `yaml:"candy,omitempty" json:"candy"`
// User configuration.
User string `yaml:"user,omitempty" json:"user"`
UID int `yaml:"uid,omitempty" json:"uid"`
GID int `yaml:"gid,omitempty" json:"gid"`
Home string `yaml:"home,omitempty" json:"home"`
// user_adopted is true when the resolved user came from the distro's base_user
// declaration rather than being created by the bootstrap.
UserAdopted bool `yaml:"user_adopted,omitempty" json:"user_adopted"`
// Merge configuration — layer merge settings (nil means use CLI defaults).
Merge *BoxMerge `yaml:"merge,omitempty" json:"merge,omitempty"`
// Builder configuration (resolved: image -> base image -> defaults -> {}).
Builder BuilderMap `yaml:"builder,omitempty" json:"builder"`
// Builder capability declaration (image-specific, not inherited).
BuilderCapabilities []string `yaml:"builder_capabilities,omitempty" json:"builder_capabilities"`
Auto bool `yaml:"auto,omitempty" json:"auto"`
// Container network mode (e.g. "host", "none") — declaration of required/recommended
// network mode. Deployment overrides via MergeDeployOntoMetadata.
Network string `yaml:"network,omitempty" json:"network"`
DataImage bool `yaml:"data_image,omitempty" json:"data_image"`
// Derived fields.
IsExternalBase bool `yaml:"is_external_base,omitempty" json:"is_external_base"`
FullTag string `yaml:"full_tag,omitempty" json:"full_tag"`
}
#ResolvedBox — a fully resolved box configuration, the wire-clean subset. EXCLUDED here (both stay on the buildkit.ResolvedBox embedding wrapper, as hand `yaml:"-" json:"-"` fields — the gengotypes CANNOT-table's "json:"-" keep-in-Go, drop-from-wire" exception, spike-verified: no CUE construct emits a suppressed-from-marshal field):
- the three host-render-cache VOCABULARY pointers DistroConfig/DistroDef/BuilderConfig (+ InitSystem/InitDef) — buildkit-owned types spec must never import;
- the four RENDER-cache fields CandyCaps/BakedMetadata/RenderCandyOrder/ActiveInits — `yaml:"-" json:"-"` on ResolvedBox itself (the SEPARATE ResolvedBoxView wire type carries the actual wire copies of this same data, via NewSpecResolvedBox/ProjectResolvedBox).
Fields that carried NO json tag on the former hand struct (always emitted, no omitempty) are REQUIRED (`!`) here; fields that already carried `,omitempty` are OPTIONAL (`?`).
func (*ResolvedBox) SupportsBuild ¶ added in v0.2026210.1533
func (img *ResolvedBox) SupportsBuild(format string) bool
SupportsBuild returns true if this image has the given build format.
func (*ResolvedBox) SupportsTag ¶ added in v0.2026210.1533
func (img *ResolvedBox) SupportsTag(tag string) bool
SupportsTag returns true if this image has the given tag. Tags include format (rpm, deb, pac), distro (fedora, arch), version (fedora:43), and the implicit "all".
type ResolvedBoxView ¶
type ResolvedBoxView struct {
Name string `yaml:"name,omitempty" json:"name"`
Version string `yaml:"version,omitempty" json:"version,omitempty"`
EffectiveVersion string `yaml:"effective_version,omitempty" json:"effective_version,omitempty"`
Status string `yaml:"status,omitempty" json:"status,omitempty"`
Info string `yaml:"info,omitempty" json:"info,omitempty"`
CheckLevel string `yaml:"check_level,omitempty" json:"check_level,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:"platforms,omitempty" json:"platforms,omitempty"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
Registry string `yaml:"registry,omitempty" json:"registry,omitempty"`
Pkg string `yaml:"pkg,omitempty" json:"pkg,omitempty"`
Distro []string `yaml:"distro,omitempty" json:"distro,omitempty"`
BuildFormats []string `yaml:"build_formats,omitempty" json:"build_formats,omitempty"`
Tags []string `yaml:"tags,omitempty" json:"tags,omitempty"`
Candy []string `yaml:"candy,omitempty" json:"candy,omitempty"`
User string `yaml:"user,omitempty" json:"user,omitempty"`
UID int64 `yaml:"uid,omitempty" json:"uid,omitempty"`
GID int64 `yaml:"gid,omitempty" json:"gid,omitempty"`
Home string `yaml:"home,omitempty" json:"home,omitempty"`
UserAdopted bool `yaml:"user_adopted,omitempty" json:"user_adopted,omitempty"`
Merge *BoxMerge `yaml:"merge,omitempty" json:"merge,omitempty"`
Builder map[string]string `yaml:"builder,omitempty" json:"builder,omitempty"`
BuilderCapabilities []string `yaml:"builder_capabilities,omitempty" json:"builder_capabilities,omitempty"`
Auto bool `yaml:"auto,omitempty" json:"auto,omitempty"`
Network string `yaml:"network,omitempty" json:"network,omitempty"`
DataImage bool `yaml:"data_image,omitempty" json:"data_image,omitempty"`
IsExternalBase bool `yaml:"is_external_base,omitempty" json:"is_external_base,omitempty"`
FullTag string `yaml:"full_tag,omitempty" json:"full_tag,omitempty"`
// box-AGGREGATES — the cross-candy effective values `charly box inspect --format
// ports|volumes|aliases|engine` prints (the host projector runs CollectBoxPorts /
// CollectBoxVolume / CollectBoxAlias / ResolveBoxEngine into these). engine is the raw
// resolver result ("" → the command body renders "(global default)").
Ports []string `yaml:"ports,omitempty" json:"ports,omitempty"`
Volumes []ResolvedVolumeMount `yaml:"volumes,omitempty" json:"volumes,omitempty"`
Aliases []CandyAlias `yaml:"aliases,omitempty" json:"aliases,omitempty"`
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
// box-AUTHORED surfaces the validate ENGINE checks (task #60): the box's OWN authored `plan:`
// (validateOps box-arm — every Op validated for authoring errors) and `alias:` (validateAliases
// box-arm — name char-set + cross-entry dedup). Distinct from the resolved `aliases` AGGREGATE
// above (cross-candy CollectBoxAlias) — these are the box entity's raw authored entries the plugin
// re-validates, so the whole validateOps/validateAliases move to plugin-box (ruling-a: grow the
// envelope rather than keep the R-rule in core).
Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`
AuthoredAliases []BoxAlias `yaml:"authored_aliases,omitempty" json:"authored_aliases,omitempty"`
// box-AUTHORED deploy-overlay surfaces ExportAllBox reads (K5-Unit-1, the #67 keystone): the
// box entity's OWN authored env / env_file / security / network / raw description — the fields
// `charly bundle export --all` projects into a BundleConfig so the deploy-state model can be
// built from the RESOLVED-PROJECT ENVELOPE, not the live *Config graph. description is the RAW
// authored string (distinct from info above, which is its descriptionInfo first-line summary);
// env/env_file/security are the box-authored deploy-overlay defaults. network is already carried
// above; these three + description are the additions. Filled by projectBoxAggregates from
// cfg.BoxConfig(name), alongside plan/authored_aliases.
Description string `yaml:"description,omitempty" json:"description,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"`
// build-render label carriers (#67, the build_resolve RENDER-leg death). The host projector runs
// the label collectors + caps + init resolve + skill probe + status into a fully-baked #BakedLabelSet
// (the EXACT wire data writeLabels emits) so the deploykit render's WriteLabels — relocated out of
// core — FORMATS it byte-for-byte WITHOUT the live *Candy graph. #BakedLabelSet is the BUILD-side
// wire-form carrier (env map, volume LabelVolumeEntry, +init_label_key/oci_labels), distinct from
// #BoxMetadata (the deploy-side hub). caps carries the aggregated candy capability surface the
// render reads beyond the baked labels (the two render-gating booleans; oci_labels also ride the
// baked set for the formatter). Both filled ONLY in the build-render projection; empty for the
// resolved-project / validate path (which never renders), so that path stays lean.
BakedMetadata *BakedLabelSet `yaml:"baked_metadata,omitempty" json:"baked_metadata,omitempty"`
Caps *AggregatedCandyCapsView `yaml:"caps,omitempty" json:"caps,omitempty"`
// build-RENDER init/candy-order caches (#67, the build_resolve RENDER-leg death). The host
// render-prep computes these over the live *Candy/*Config graph (globalOrderForBox,
// AggregateCandyCapabilities, ActiveInit, ResolveInitSystem) and either sets them directly on
// the live ResolvedBox (the parity-test live path) or carries them through this envelope (the
// plugin-build drive path, re-attached by NewSpecResolvedBox) so the deploykit render reads
// RESOLVED caches WITHOUT the live graph. Filled ONLY in the build-render projection; empty for
// the resolved-project / validate path. render_candy_order is the per-box cache-optimal candy
// sequence; init_system/init_def the active init; active_inits the full active-init map
// (EmitInitFragmentStages + EmitInitAssembly read it).
RenderCandyOrder []string `yaml:"render_candy_order,omitempty" json:"render_candy_order,omitempty"`
InitSystem string `yaml:"init_system,omitempty" json:"init_system,omitempty"`
InitDef *ResolvedInit `yaml:"init_def,omitempty" json:"init_def,omitempty"`
ActiveInits map[string]*ResolvedInit `yaml:"active_inits,omitempty" json:"active_inits,omitempty"`
}
#ResolvedBoxView — the resolved box METADATA a consumer reads: EXACTLY the non-json:"-" fields of buildkit.ResolvedBox, in declaration order, so this view is the wire-safe half of what `charly box inspect` already serializes (json.MarshalIndent(*ResolvedBox) — which never reaches the 6 json:"-" compute-cache pointers). Field order mirrors ResolvedBox so a future inspect relocation projects into this view byte-faithfully. Every field optional (a projection envelope); name is the stable key.
type ResolvedDistro ¶
type ResolvedDistro 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"`
Raw RawBody `yaml:"raw,omitempty" json:"raw,omitempty"`
}
--- resolve-to-envelope wire type (Cutover M, the long pole; SDD conversion, per the standing operator directive: a hand-written wire struct not yet CUE-sourced is conversion-in-progress, never a sanctioned exception). candy/plugin-distro resolves an authored `distro:` build-vocabulary entity into a ResolvedDistro the kernel's build engine consumes without importing the concrete spec.Distro. Written out explicitly (not embedding #Distro) so every field's required/optional state is independently auditable against the former hand type. The host keeps RenderTemplate + the cache-mount vocab (per the plan); the plugin owns the distro KNOWLEDGE (schema/typed shape/validation). PrimaryFormat()/LocalPkgFormat() are pure Go METHODS — CUE cannot express them — and stay hand-written in spec/distro_methods.go (mirrors Op.Kind() in spec/charly_methods.go: a method, not a type).
func (*ResolvedDistro) LocalPkgFormat ¶
func (d *ResolvedDistro) LocalPkgFormat(primaryFormat string) (string, *LocalPkg)
LocalPkgFormat picks the format whose local_pkg block builds the charly toolchain: the caller's primary format, then PrimaryFormat, then any localpkg-capable format (mirrors spec.Distro.LocalPkgFormat).
func (*ResolvedDistro) PrimaryFormat ¶
func (d *ResolvedDistro) PrimaryFormat() string
PrimaryFormat returns the distro's primary (non-secondary) build format — the deterministic first non-secondary Format name (mirrors spec.Distro.PrimaryFormat).
type ResolvedGpuSelector ¶
type ResolvedGpuSelector struct {
Vendor string `yaml:"vendor,omitempty" json:"vendor,omitempty"`
}
type ResolvedInit ¶
type ResolvedInit 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"`
Model string `yaml:"model,omitempty" json:"model,omitempty"`
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"`
// Raw is the opaque init body — threaded to the service-render leg (leg 1).
Raw RawBody `yaml:"raw,omitempty" json:"raw,omitempty"`
}
#ResolvedInit is the resolve-to-envelope form of an init system the kernel consumes for stage/assembly emission, capability labels, and the entrypoint contract (legs 2–4 of the init de-type). It carries the init system's build + runtime VALUES + templates the host reads generically — the kernel never imports the concrete spec.Init. Raw is the opaque init body threaded to the plugin's service-render leg (leg 1). candy/plugin-init produces it from spec.Init.
type ResolvedK8s ¶
type ResolvedK8s struct {
KubeconfigContext string `yaml:"kubeconfig_context,omitempty" json:"kubeconfig_context,omitempty"`
Raw RawBody `yaml:"raw,omitempty" json:"raw,omitempty"`
}
#ResolvedK8s is the resolve-to-envelope form of a `k8s:` cluster template. The kernel reads only KubeconfigContext (the deploy preresolver); the full cluster model rides opaquely in Raw and is decoded by candy/plugin-k8sgen, never the kernel.
type ResolvedLocal ¶
type ResolvedLocal struct {
Candy []CandyRef `yaml:"candy,omitempty" json:"candy,omitempty"`
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"`
// raw is the opaque authored body (the kernel passes it back unread where
// a verbatim template is needed).
Raw RawBody `yaml:"raw,omitempty" json:"raw,omitempty"`
}
#ResolvedLocal is the resolve-to-envelope form of a `local:` template — a candy-stack applied to a host. The kernel reads it (candy stack / install opts / plan), never spec.Local.
type ResolvedPod ¶
type ResolvedPod struct {
Box CandyRef `yaml:"box,omitempty" json:"box,omitempty"`
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"`
Raw RawBody `yaml:"raw,omitempty" json:"raw,omitempty"`
}
#ResolvedPod is the resolve-to-envelope form of a `pod:` template — a box + sidecar + plan bundle. The kernel reads it (currently the include-spliced Plan), never spec.Pod.
type ResolvedProject ¶
type ResolvedProject struct {
Version string `yaml:"version,omitempty" json:"version,omitempty"`
Boxes map[string]ResolvedBoxView `yaml:"boxes,omitempty" json:"boxes,omitempty"`
Candies map[string]CandyView `yaml:"candies,omitempty" json:"candies,omitempty"`
// candy_models — the serializable candy BUILD models (validate, the plan-include splicer, K3-D)
// keyed by name, distinct from candies (identity/graph). See candymodel.cue.
CandyModels map[string]CandyModel `yaml:"candy_models,omitempty" json:"candy_models,omitempty"`
Deploy map[string]*Deploy `yaml:"deploy,omitempty" json:"deploy,omitempty"`
// build vocabulary (the validate ENGINE consumer): distro/init PIN the hand wire structs
// (spec.ResolvedDistro/ResolvedInit — the distro/init de-type OpResolve envelopes, no #CUE def);
// #Builder is a clean def. Pointer maps for parity with DistroConfig/BuilderConfig/InitConfig.Init.
Distro map[string]*ResolvedDistro `yaml:"distro,omitempty" json:"distro,omitempty"`
Builder map[string]*Builder `yaml:"builder,omitempty" json:"builder,omitempty"`
Init map[string]*ResolvedInit `yaml:"init,omitempty" json:"init,omitempty"`
// kind templates (validate localtemplates + check-include pod/vm arms + status k8s/adb enumeration).
Templates *ProjectTemplates `yaml:"templates,omitempty" json:"templates,omitempty"`
// kind:agent catalog (the harness AI-CLI pick — plugin-check reads it off this envelope; charly feature list-agent).
AgentBodies map[string]RawBody `yaml:"agent_bodies,omitempty" json:"agent_bodies,omitempty"`
// build order + auto-intermediates (charly box list targets).
BuildTargets []BuildTarget `yaml:"build_targets,omitempty" json:"build_targets,omitempty"`
// validate D-data word-sets (task #60, ruling: host projects the registry facts into the envelope
// so the validate ENGINE moves to the plugin WITHOUT the plugin ever dialing the host registry).
// The host fills these in the validate-project path only (empty for the resolved-project path);
// clause-D kind/word-recognition DATA consulted BY WORD, never a per-kind branch.
//
// provider_capabilities — every compiled-in provider as "<class>:<word>" (validatePluginCandy
// checks a `source: builtin` candy's declared providers are actually compiled in).
// act_capable_verbs — the plugin WORDS whose act form has a build/deploy install path (the host
// type-asserts ProvisionActor/TypedStepProvider/BuildEmitter + connected/declared externals +
// command, exactly as core's opActsInBuildDeploy does), so validateCheck's act-form rule keeps
// builtin rejection behaviour without the registry.
ProviderCapabilities []string `yaml:"provider_capabilities,omitempty" json:"provider_capabilities,omitempty"`
ActCapableVerbs []string `yaml:"act_capable_verbs,omitempty" json:"act_capable_verbs,omitempty"`
// box_plans — the include-ready FLATTENED acceptance plan per box (the `include: box:<name>`
// arm of the plan-composition splicer). The host projector runs the SAME base-chain walk
// CollectDescriptions uses (candy-chain bakeable steps + the box-level bakeable plan) and
// flattens the three sections into one ordered []Step, keyed by the QUALIFIED box name
// (namespaced boxes like `fedora.jupyter` included) — the exact result the former in-core
// box arm produced. A plugin cannot recompute it (base-chain + candy-order + bakeable filter
// are host resolve Mechanisms over the runtime Candy), so the resolve engine ships it. Only
// boxes with a non-empty flattened plan appear.
BoxPlans map[string][]Step `yaml:"box_plans,omitempty" json:"box_plans,omitempty"`
// global_order — the popularity-weighted GLOBAL candy order (GlobalCandyOrder) the
// build RENDER reads (deploykit Generator.GlobalOrderForBox → per-box candy sequence for
// cache-optimal layering). A host resolve Mechanism over the runtime Candy graph, so the
// resolve engine ships it (the plugin-side render can't recompute it). Filled in the
// build-render projection (#67); empty for the resolved-project/validate path.
GlobalOrder []string `yaml:"global_order,omitempty" json:"global_order,omitempty"`
// externalized_builders — the registry D-FACT: which detection-builder WORDS
// (pixi/npm/aur/cargo) are externalized (their inline render crosses the OpResolve seam vs
// an in-core vocabulary). The render selects the branch by word (deploykit
// DetectExternalizedBuilders + candy_steps builder arm). clause-D word-recognition DATA
// consulted BY WORD; filled in the build-render projection (#67).
ExternalizedBuilders map[string]bool `yaml:"externalized_builders,omitempty" json:"externalized_builders,omitempty"`
// resources — the resolved `resource:` kind entities (K1-unblock wave 1), keyed by name,
// mirroring `deploy` byte-for-byte: the host decodes each project resource:` entity via the
// SAME kind-blind resource-kind plugin resolve it always used (never a kernel decode of the
// concrete #Resource shape — clause-D/M unaffected), so this is a plain data projection, not a
// new engine. Consumer: the resource arbiter (candy/plugin-preempt), which previously reached
// this ONLY via the bespoke ExecutorService.HostArbiter "resources" seam
// (charly/arbiter_host.go) — the last of its 2 genuinely-LoadUnified-coupled host seams. Reading
// it off THIS envelope instead retires that seam, leaving `resources` (this field) as the single
// source both the arbiter plugin and any other resolved-project consumer share (R3).
Resources map[string]*ResolvedResource `yaml:"resources,omitempty" json:"resources,omitempty"`
// primaries — the plugin-verb PRIMARY-field D-fact: a plugin verb WORD → its declared primary
// input field (`cdp` → `method`, `file` → `file`), the SAME registry-derived kind-recognition
// DATA the LOAD path already snapshots as spec.Threaded.Primaries (loaderThreaded), carried here
// SAVE-side too. The deploy-state WRITER's plan RESUGAR (deploy_nodeform.go's resugarPlan —
// collapsing an internal plugin/plugin_input pair back to the authored `<word>: <input>` sugar)
// needs this map to collapse a single-primary input to its scalar shorthand; carrying it on the
// envelope lets a plugin holding this projection resugar a plan WITHOUT dialing the host provider
// registry (the K4 keystone that unblocks moving the deploy-state marshal plugin-side). clause-D
// word-recognition DATA consulted BY WORD, never a per-kind branch; filled on every resolve path.
Primaries map[string]string `yaml:"primaries,omitempty" json:"primaries,omitempty"`
}
#ResolvedProject — the whole resolved projection: the schema version, the resolved boxes keyed by name, the resolved candy graph keyed by name, and the deploy tree (uf.Bundle verbatim — already map[string]spec.Deploy). The deploy map is @go-pinned to a pointer map so `cue exp gengotypes` generates map[string]*Deploy (recursive tree, faithful). provides/sidecar are additive later members of this same envelope (added by the consumer unit that first needs them).
type ResolvedProjectRequest ¶
type ResolvedProjectRequest struct {
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
IncludeDisabled bool `yaml:"include_disabled,omitempty" json:"include_disabled,omitempty"`
// Check commands resolving a distro-submodule project must see the parent
// superproject's in-development candies before bed classification. The host
// applies the same local override used by the later R10 session only for the
// duration of this projection.
LocalSuperproject bool `yaml:"local_superproject,omitempty" json:"local_superproject,omitempty"`
// A caller compiling against an `add_candy:`/`--add-candy` ref (a candy the project's own
// image-closure walk never reaches — the reachability-scoped remote-ref collection is
// deliberately narrower than "every candy anywhere," R1) widens the scan the SAME way
// ScanAllCandyWithConfigOpts' ResolveOpts.ExtraCandyRefs already does for a check bed's own
// add_candy: (deploy_add_shared.go's deployNodePluginContext) — without this, the compile
// plugin's OWN independent resolved-project re-fetch (candy/plugin-bundle's compile.go)
// never discovers a remote add-candy ref the HOST's separate scanCandiesForRef call already
// pulled in via its own synthetic-augmented scan, and BuildDeployPlan fails "candy not in
// resolved-project envelope" (RCA'd K1-alpha regression, check-addcandy-pod/check-stepkind-
// emit-pod).
ExtraCandyRefs []string `yaml:"extra_candy_refs,omitempty" json:"extra_candy_refs,omitempty"`
// requested_boxes — the explicit build/generate targets (`charly box generate <name>`),
// buildkit.NormalizeBoxArgs-normalized (task #17 fix). The reachability-scoped remote-ref
// COLLECTION walk (loaderkit.CollectRemoteRefsOpts) only follows base/builder edges from
// ROOT-owned images by default — a namespace-qualified on-demand target
// (`charly box generate fedora.check-pod`) that is not itself a base/builder of any root
// image is otherwise never visited, so its own remote candy refs are silently never
// collected/fetched, and the later candy-order RESOLVE step then fails "unknown candy" for a
// ref the fetch step skipped. Mirrors spec.ResolveOpts.RequestedBoxes, which already pulls a
// qualified on-demand target into buildkit.ResolveAllBox's RESOLVE set the identical way —
// this closes the matching gap on the COLLECT half.
RequestedBoxes []string `yaml:"requested_boxes,omitempty" json:"requested_boxes,omitempty"`
// boxes — the resolved-box set the PLUGIN-SIDE build-engine resolve (candy/plugin-build's
// resolveBuildEngine) pushes to the host's `buildengine-prep` leg so the host's render-seam-floor
// Generator cache stores wire-clean *spec.ResolvedBox WITHOUT the host re-resolving via
// deploykit.ResolveAllSpecBoxes — the plugin already resolved them (buildkit.ResolveAllBox, the
// SAME primitive ResolveAllSpecBoxes wraps). Pointer map for parity with the host Generator.Boxes
// field (map[string]*spec.ResolvedBox); the render-seam floor's 2 consumers read only Name/Tags
// off these. Empty for the other buildengine-* legs that don't touch box-resolve data (and for
// the loadRenderGen defensive cache-miss fallback, which is provably unreachable in production).
// #55 coneB2 Class B — sheds the deploykit import from charly/generate.go.
Boxes map[string]*ResolvedBox `yaml:"boxes,omitempty" json:"boxes,omitempty"`
}
#ResolvedProjectRequest — the `resolved-project` HostBuild request: which project dir to resolve (empty = the host's cwd) and whether to include enabled:false boxes. The reply is #ResolvedProject.
type ResolvedReadiness ¶
type ResolvedReadiness struct {
IntervalLocal time.Duration
IntervalRemote time.Duration
IntervalHeavy time.Duration
PerAttempt time.Duration
PerAttemptHeavy time.Duration
NoProgress time.Duration
AbsoluteCap time.Duration
StopGrace time.Duration
}
ResolvedReadiness is the load-time-materialized, validated bounds set (resolveReadiness in build.go merges flag/env → defaults.readiness → the named fallback constants, then ReadinessConfig.Validate enforces ordering). The zero value is SAFE — every builder falls back to the compiled-in fallback constants — so a site that has not yet been threaded the resolved set still gets sane, never-hang bounds (it just isn't operator-tunable until threaded).
func ResolveReadiness ¶
func ResolveReadiness(rc *ReadinessConfig) (ResolvedReadiness, error)
ResolveReadiness materializes the validated ResolvedReadiness from a `defaults.readiness:` block plus CHARLY_READINESS_* env overrides plus the named fallbacks. Nil-safe (nil → all fallbacks/env). The SINGLE resolver — charly core (loadedReadiness, from the project config) AND the out-of-process plugins (nil + the host-threaded env) both call it.
func (ResolvedReadiness) PerAttemptFor ¶
func (rr ResolvedReadiness) PerAttemptFor(class PollClass) time.Duration
PerAttemptFor returns the effective per-attempt never-hang bound for a poll class (the field value, or the built-in fallback when unset). The unexported perAttempt/perAttemptHeavy accessors collide on export with the PerAttempt/PerAttemptHeavy struct fields, so consumers reach the bound through this class-keyed facade.
func (ResolvedReadiness) PluginEnv ¶
func (rr ResolvedReadiness) PluginEnv() []string
PluginEnv emits the CHARLY_READINESS_* entries (KEY=VALUE) for this RESOLVED readiness, so an out-of-process plugin — which cannot LoadUnified — re-reads the host-resolved bounds via its own ResolveReadiness. The host appends these to a plugin's spawn env (charly's LocalTransport.Connect). Durations round-trip through String() ⟷ resolveReadinessDuration.
func (ResolvedReadiness) StopGate ¶
func (rr ResolvedReadiness) StopGate(name string) PollConfig
StopGate builds a settle/teardown PollConfig: cap-only at StopGrace (a clean timeout for the binary "is it down yet?" — the caller performs the force fallback on ErrPollCapExceeded). Replaces the 60s/180s/10s stop magic numbers with one config-sourced grace; no 2× multiplier.
func (ResolvedReadiness) ValidateOrdering ¶
func (rr ResolvedReadiness) ValidateOrdering() error
ValidateOrdering enforces the bound ordering on the RESOLVED set (must run post-env, not on the raw YAML — an env override must not slip a bad bound through): each interval <= no_progress; no_progress <= absolute_cap; poll_interval_local <= stop_grace <= absolute_cap.
func (ResolvedReadiness) Wait ¶
func (rr ResolvedReadiness) Wait(name string, class PollClass) PollConfig
Wait builds a MONOTONIC readiness PollConfig: the no-progress watchdog is active (the load-robust early-out), with a generous absolute cap as the never-hang backstop. Use ONLY where cond reports a genuinely advancing high-water progress score (supervisord settled-count, check-live pass tally, AI score). For binary/edge markers use WaitCapped instead.
func (ResolvedReadiness) WaitCapped ¶
func (rr ResolvedReadiness) WaitCapped(name string, class PollClass, cap time.Duration) PollConfig
WaitCapped builds a CAP-ONLY PollConfig (NoProgress disabled): wait until cond is ready or the generous cap elapses. Use for BINARY/EDGE readiness (ssh-up, boot_id, cloud-init-done, file-present, channel-up) where the marker is frozen until the end, AND for AUTHOR/CALLER-declared deadlines (eventually:, WaitSeconds, guest-exec maxWait) which must be preserved exactly. cap<=0 falls back to the generous config absolute_cap.
func (ResolvedReadiness) WatchProgress ¶
func (rr ResolvedReadiness) WatchProgress(name string, class PollClass, noProgress time.Duration) PollConfig
WatchProgress builds a NO-CAP monotonic watchdog (NoProgress active, no absolute cap) for an intentionally unbounded long-runner: the AI iteration loop, whose run must NOT be killed at a wall-clock cap as long as the score keeps improving. noProgress<=0 falls back to the config no_progress.
type ResolvedResource ¶
type ResolvedResource struct {
Gpu *ResolvedGpuSelector `yaml:"gpu,omitempty" json:"gpu,omitempty"`
}
type ResolvedSidecar ¶
type ResolvedSidecar struct {
Name string `yaml:"name,omitempty" json:"name"`
Image string `yaml:"image,omitempty" json:"image,omitempty"`
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
Secret []ResolvedSidecarSecret `yaml:"secret,omitempty" json:"secret,omitempty"`
Volume []ResolvedSidecarVolume `yaml:"volume,omitempty" json:"volume,omitempty"`
Security *Security `yaml:"security,omitempty" json:"security,omitempty"`
}
#ResolvedSidecar is a fully-resolved co-deployed container the host consumes for quadlet generation — the resolve-to-envelope form of a sidecar (NO Sidecar / SidecarDef fields survive; the plugin already merged + resolved everything).
type ResolvedSidecarSecret ¶
type ResolvedSidecarSecret struct {
Name string `yaml:"name,omitempty" json:"name"`
Env string `yaml:"env,omitempty" json:"env,omitempty"`
HostEnv string `yaml:"host_env,omitempty" json:"host_env,omitempty"`
SecretName string `yaml:"secret_name,omitempty" json:"secret_name,omitempty"`
}
#ResolvedSidecarSecret is a resolved sidecar secret (the sidecar-scoped subset of the host's CollectedSecret): the podman secret name + the container/host env-var names + the original manifest secret name.
type ResolvedSidecarVolume ¶
type ResolvedSidecarVolume struct {
VolumeName string `yaml:"volume_name,omitempty" json:"volume_name"`
ContainerPath string `yaml:"container_path,omitempty" json:"container_path"`
}
#ResolvedSidecarVolume is a resolved sidecar volume: the charly-scoped volume name + its container mount path.
type ResolvedVm ¶
type ResolvedVm 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 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"`
Raw RawBody `yaml:"raw,omitempty" json:"raw,omitempty"`
}
--- resolve-to-envelope wire type (Cutover L; SDD conversion, per the standing operator directive: a hand-written wire struct not yet CUE-sourced is conversion-in-progress, never a sanctioned exception). ResolvedVm mirrors #Vm's fields — written out explicitly rather than embedding #Vm, since the resolved envelope carries PLAIN post-default scalars, not #Vm's own enum/default machinery (firmware/backend/autostart are plain string/bool here, never the `*"bios"|...` disjunction). candy/plugin-substrate resolves an authored `vm:` template into this envelope; the kernel's vm build/deploy consumers read it without importing the concrete spec.Vm.
type ResolvedVolumeMount ¶
type ResolvedVolumeMount struct {
VolumeName string `yaml:"volume_name,omitempty" json:"volume_name,omitempty"`
ContainerPath string `yaml:"container_path,omitempty" json:"container_path,omitempty"`
}
#ResolvedVolumeMount — one entry of the box-aggregate volume list (CollectBoxVolume): the charly-<box>-<name> volume name + the home-expanded container path. Mirrors deploykit.VolumeMount (a permanent deploykit home, never wire-marshaled there — spec carries the envelope copy).
type Resource ¶
type Resource struct {
Gpu *GpuSelector `yaml:"gpu,omitempty" json:"gpu,omitempty"`
}
func (*Resource) HasSelector ¶
HasSelector reports whether the resource carries a GPU selector.
type ResourceResolveInput ¶
type ResourceResolveInput struct {
Resource RawBody `yaml:"resource,omitempty" json:"resource"`
}
type ResourceResolveReply ¶
type ResourceResolveReply struct {
Resolved *ResolvedResource `yaml:"resolved,omitempty" json:"resolved,omitempty"`
}
type RetentionReply ¶
type RetentionReply struct {
ImageRefs []string `yaml:"image_refs,omitempty" json:"image_refs,omitempty"`
DanglingIDs []string `yaml:"dangling_ids,omitempty" json:"dangling_ids,omitempty"`
StagingDirs []string `yaml:"staging_dirs,omitempty" json:"staging_dirs,omitempty"`
BuildDirs []string `yaml:"build_dirs,omitempty" json:"build_dirs,omitempty"`
CheckPaths []string `yaml:"check_paths,omitempty" json:"check_paths,omitempty"`
DeepIDs []string `yaml:"deep_ids,omitempty" json:"deep_ids,omitempty"`
DeepBytes int64 `yaml:"deep_bytes,omitempty" json:"deep_bytes,omitempty"`
TagGroups []TagInfo `yaml:"tag_groups,omitempty" json:"tag_groups,omitempty"`
KeepImages int `yaml:"keep_images,omitempty" json:"keep_images,omitempty"`
KeepCheckRuns int `yaml:"keep_check_runs,omitempty" json:"keep_check_runs,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
#RetentionReply is the verb:retention reply: the removed (or would-remove, under dry_run) image refs, build-candy dirs, and check-run paths, plus the effective retention counts, for the caller to present.
deep_ids/deep_bytes report the store-wide untagged-image purge (deep): the removed (or would-remove) image IDs and the sum of their reported storage Size in bytes. deep_bytes is an UPPER BOUND, not a prediction of actual freed disk: each image's reported Size counts every layer it references, and many of those layers are SHARED with images that remain (retained tags, or other still-referenced dangling images) — RDD-verified live on a real host, where a --deep purge removing 68 untagged images (3,552 -> 3,484) reported ~92.6 GiB via this sum but only ~4.6 GiB of disk was actually freed (132.6 GB -> 128 GB), because most layer bytes stayed shared with the ~3,400 remaining (largely stale-tagged) images. `charly clean --deep --dry-run` presents deep_bytes as "up to" for exactly this reason; pairing --deep with --invalidate (removing stale TAGS too, so their exclusively-held layers also become unreferenced) gets closer to the reported figure.
tag_groups is the `list` reply payload: every locally stored charly-labeled tag, newest-first per box.
error is a human-facing message on a non-recoverable failure.
type RetentionRequest ¶
type RetentionRequest struct {
Dir string `yaml:"dir,omitempty" json:"dir"`
DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,omitempty"`
Images bool `yaml:"images,omitempty" json:"images,omitempty"`
Check bool `yaml:"check,omitempty" json:"check,omitempty"`
Deep bool `yaml:"deep,omitempty" json:"deep,omitempty"`
List bool `yaml:"list,omitempty" json:"list,omitempty"`
BuildPrune bool `yaml:"build_prune,omitempty" json:"build_prune,omitempty"`
Keep int `yaml:"keep,omitempty" json:"keep,omitempty"`
KeepImages int `yaml:"keep_images,omitempty" json:"keep_images,omitempty"`
KeepCheckRuns int `yaml:"keep_check_runs,omitempty" json:"keep_check_runs,omitempty"`
Invalidate string `yaml:"invalidate,omitempty" json:"invalidate,omitempty"`
}
#RetentionRequest is the verb:retention request. dir is the project directory (os.Getwd()) — always present, never empty. keep=0 means "use the resolved defaults"; invalidate (non-empty) runs ONLY the targeted image-tag invalidation. deep runs the store-wide untagged/dangling-image purge category (`charly clean --deep`) — unlike images (which only ever touches charly-labeled tags/dangling ids), deep removes EVERY untagged image in local storage, including unlabeled multi-stage build intermediates the images category can never see. list runs the read-only tag inventory (`charly box list tags`) — no dir needed beyond resolving the engine, nothing is removed. build_prune is the narrow post-`charly box build` step: retention-prune image tags + stale .build/_candy staging dirs ONLY (the historic charly/retention.go pruneAfterBuild scope) — deliberately NOT the fuller images category (which also sweeps dangling images + buildah staging), so a build's own post-step behavior is unchanged by this relocation. keep_images/keep_check_runs are the caller's PRE-RESOLVED defaults.keep_images/ keep_check_runs (0 = disabled) — this engine never reads charly.yml itself; a core caller (already running LoadConfig in-process) resolves these directly, a plugin caller fetches them via the "retention-defaults" HostBuild seam first. keep (the CLI --keep override) wins over both when > 0.
type ReverseOp ¶
type ReverseOp struct {
Kind ReverseOpKind `yaml:"kind,omitempty" json:"kind"`
Format string `yaml:"format,omitempty" json:"format,omitempty"`
Targets []string `yaml:"targets,omitempty" json:"targets,omitempty"`
Scope Scope `yaml:"scope,omitempty" json:"scope,omitempty"`
Extra map[string]string `yaml:"extra,omitempty" json:"extra,omitempty"`
// uninstall_cmd is the rendered host-venue package-removal command for a
// ReverseOpPackageRemove op, filled at record time from the format's
// uninstall_template by fillReverseUninstallCmds.
UninstallCmd string `yaml:"uninstall_cmd,omitempty" 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 RouteConfig ¶
type RouteConfig struct {
Host string `yaml:"host,omitempty" json:"host,omitempty"`
Port string `yaml:"port,omitempty" json:"port,omitempty"`
}
#RouteConfig — a resolved route declaration (host + port-as-string). Mirrors deploykit.RouteConfig. Port is a STRING here (the resolved form), distinct from #CandyRoute.port (authored int).
type SSHTarget ¶ added in v0.2026210.1121
SSHTarget is the parsed form of an ssh-style target string. Examples:
- "host" → {User: $USER, Host: "host", Port: 22}
- "user@host" → {User: "user", Host: "host", Port: 22}
- "user@host:2222" → {User: "user", Host: "host", Port: 2222}
- "qemu+ssh://user@host/…" → parsed via ParseLibvirtSSHURI (not here)
func ParseSSHTarget ¶ added in v0.2026210.1121
ParseSSHTarget accepts "[user@]host[:port]" and fills defaults:
- User: $USER
- Port: 22
type SaveDeployStateInput ¶ added in v0.2026211.549
type SaveDeployStateInput struct {
Ports []string `yaml:"ports,omitempty" json:"ports,omitempty"`
// set_ports gates whether Ports is written to charly.yml at all: `charly config <name>` (no
// --port flags) and `charly update <name>` must not silently overwrite operator port overrides
// with image-label defaults.
SetPorts bool `yaml:"set_ports,omitempty" json:"set_ports,omitempty"`
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
CleanEnv bool `yaml:"clean_env,omitempty" json:"clean_env,omitempty"`
EnvFile string `yaml:"env_file,omitempty" json:"env_file,omitempty"`
Network string `yaml:"network,omitempty" json:"network,omitempty"`
Security *Security `yaml:"security,omitempty" json:"security,omitempty"`
Volume []DeployVolume `yaml:"volume,omitempty" json:"volume,omitempty"`
Sidecar map[string]RawBody `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
Tunnel *TunnelYAML `yaml:"tunnel,omitempty" json:"tunnel,omitempty"`
// secret_names lists env var names declared as secret_accepts / secret_requires on the image;
// saveDeployState defensively strips any matching KEY=VAL entries from both the input Env and
// the existing persisted entry before writing.
SecretNames []string `yaml:"secret_names,omitempty" json:"secret_names,omitempty"`
// Disposable + Lifecycle classification (see /charly-internals:disposable).
SetDisposable bool `yaml:"set_disposable,omitempty" json:"set_disposable,omitempty"`
Disposable bool `yaml:"disposable,omitempty" json:"disposable,omitempty"`
SetLifecycle bool `yaml:"set_lifecycle,omitempty" json:"set_lifecycle,omitempty"`
Lifecycle string `yaml:"lifecycle,omitempty" json:"lifecycle,omitempty"`
// Box + Target — the schema-required fields per the require-image cutover. Written when non-empty
// AND when the existing entry doesn't already have a value (don't clobber operator-authored refs).
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Target string `yaml:"target,omitempty" json:"target,omitempty"`
// resolved_image is the concrete overlay image ref produced by a pod deploy's add_candy: overlay
// build, persisted by PrepareVenue so config/start deploy EXACTLY that overlay.
ResolvedImage string `yaml:"resolved_image,omitempty" json:"resolved_image,omitempty"`
// vm_state + vm_cross_ref — the vm substrate's persisted runtime state + the kind:vm cross-ref,
// shipped by the externalized vm plugin's PrepareVenue reply as the generic State patch.
VmState *VmDeployState `yaml:"vm_state,omitempty" json:"vm_state,omitempty"`
VmCrossRef string `yaml:"vm_cross_ref,omitempty" json:"vm_cross_ref,omitempty"`
// Resource-arbitration axis: the holder-side Preemptible block + the claimant-side
// requires_exclusive / requires_shared token lists. Persisted so a deploy/bed MEMBER round-trips
// its arbiter role through the per-host overlay. Written when non-empty.
Preemptible *PreemptibleConfig `yaml:"preemptible,omitempty" json:"preemptible,omitempty"`
RequiresExclusive []string `yaml:"requires_exclusive,omitempty" json:"requires_exclusive,omitempty"`
}
#SaveDeployStateInput holds the deployment parameters SaveDeployState persists to charly.yml (promoted from sdk/deploykit — #55 import-purity, Cone V value-vocabulary; the SaveDeployState FUNCTION stays in deploykit). Every field is a spec/stdlib type. The deploy candies (plugin-deploy-pod, plugin-bundle) pass it DIRECTLY to deploykit.SaveDeployState plugin-side (#55 K4 — no host seam carries it across the wire anymore; the fields are ephemeral in-operation, never persisted as-is — the node-form marshal writes only the resulting entry).
type ScanSeams ¶ added in v0.2026213.1726
type ScanSeams struct {
// CollectRemoteRefs runs the reachability-scoped remote-ref walk over the project's boxes +
// this local candy set, returning each distinct (repo, git-tag) to fetch. Host closure:
// CollectRemoteRefsOpts(cfg, FinalizeScannedCandies(localScanned, nil), withLocalRawRefs(opts, localScanned)).
CollectRemoteRefs func(localScanned map[string]ScannedCandy) ([]RemoteDownload, error)
// EnsureRepo resolves a (repoPath, version) to a local cache directory, fetching + auto-migrating
// on a cache miss (host closure: EnsureRepoDownloaded).
EnsureRepo func(repoPath, version string) (string, error)
// ScanRemote scans the wanted bare refs out of a downloaded repo cache dir (host closure:
// requireCandyScanner().ScanRemoteCandy(cacheDir, repoPath, wantRefs, parseCandyYAML)).
ScanRemote func(cacheDir, repoPath string, wantRefs map[string]bool) (map[string]ScannedCandy, error)
}
scan_seams.go — the host-coupled legs the candy-scan fetch fix-point (loaderkit.ScanCandyFromLocal) reaches. Relocated from sdk/loaderkit/scan_orchestrate.go (#55 C3b-ii) so it can be the parameter type on the spec.ProjectLoader.ScanCandyFromLocal seam method — the interface lives in this dedicated spec module, so its param types must live here too. The caller (charly's scanSeamsFor, candy/plugin-build's scanSeamsLeg) builds these as closures capturing its config/opts + host mechanisms (registry, refs backend); the pure fix-point in loaderkit never inspects a package-main type. loaderkit keeps a `type ScanSeams = spec.ScanSeams` forwarder (mirroring its RemoteDownload alias) so its own signature + candy/plugin-build's call sites stay terse.
type ScanSpec ¶ added in v0.2026210.808
type ScanSpec struct {
Path string `yaml:"path" json:"path"`
Recursive bool `yaml:"recursive" json:"recursive"`
// Manifest is the per-directory manifest filename to look for. Empty
// defaults to UnifiedFileName; configurable per spec in charly.yml.
Manifest string `yaml:"manifest,omitempty" json:"manifest,omitempty"`
}
ScanSpec describes one discovery root. Accepts string shorthand ("candy" → {Path: "candy", Recursive: true}) or the explicit object form ({path: X, recursive: false}). Empty Path is invalid.
func AnchorScanSpecs ¶ added in v0.2026213.1726
AnchorScanSpecs returns a copy of `specs` with every relative Path resolved to an absolute path against `srcDir`. Absolute paths are kept verbatim. Empty srcDir leaves specs unchanged so the root-file merge (called with rootDir == workspace) is a no-op. Path-anchoring MECHANISM (filepath) over spec.ScanSpec, relocated from sdk/kit (#55 C3b) so MergeUnified — which itself moved into this spec module — can call it without a spec→sdk import inversion.
type ScannedCandy ¶
type ScannedCandy struct {
Model CandyModel
View CandyView
Refs CandyRefs
}
ScannedCandy bundles one candy's full scan result — the two resolved envelope views plus the rich pre-qualification refs.
SDD classification: same non-wire, same-process pipeline-state rationale as CandyRefs above (one note covers both) — it is the mutable intermediate the whole scan→fetch→qualify→arbitrate pipeline (charly's ScanAllCandy family) carries in place of the pre-move *Candy, until the FINAL step bare-strings the refs (FinalizeCandyRefs) and wraps (Model, View) into a spec.CandyReader via sdk/deploykit.NewSpecCandyModel.
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 living in 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 OpStepScope ¶ added in v0.2026210.2308
func ScopeFromName ¶
ScopeFromName parses a StepContract's authored scope NAME (the Describe-carried "user"/"user-profile" string; anything else, including empty, defaults to ScopeSystem) into a Scope.
type ScoreSummary ¶
type ScoreSummary struct {
Total int `yaml:"total,omitempty" json:"total"`
Pass int `yaml:"pass,omitempty" json:"pass"`
Fail int `yaml:"fail,omitempty" json:"fail"`
Skip int `yaml:"skip,omitempty" json:"skip"`
}
#ScoreSummary — the pass/fail/skip tally block (the former hand-written TestRunSummary). The counts are Go `int` (type=int override — the former hand type; CUE `int` defaults to int64), so every existing ++/compare call site compiles unchanged.
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 ServiceCustomStep ¶ added in v0.2026210.2308
type ServiceCustomStep struct {
Name string // e.g. "charly-ollama-ollama"
UnitText string // fully rendered unit content
UnitPath string // absolute install path
TargetScope Scope // ScopeSystem or ScopeUser
Enable bool
CandyName string
}
ServiceCustomStep writes and enables a full service definition. The unit text is already rendered by the compiler from the candy's generic `services:` entry via the target init system's service_template.
func (*ServiceCustomStep) Kind ¶ added in v0.2026210.2308
func (s *ServiceCustomStep) Kind() StepKind
func (*ServiceCustomStep) RequiresGate ¶ added in v0.2026210.2308
func (s *ServiceCustomStep) RequiresGate() Gate
func (*ServiceCustomStep) Reverse ¶ added in v0.2026210.2308
func (s *ServiceCustomStep) Reverse() []ReverseOp
func (*ServiceCustomStep) Scope ¶ added in v0.2026210.2308
func (s *ServiceCustomStep) Scope() Scope
func (*ServiceCustomStep) Venue ¶ added in v0.2026210.2308
func (s *ServiceCustomStep) Venue() Venue
type ServicePackagedStep ¶ added in v0.2026210.2308
type ServicePackagedStep struct {
Unit string // e.g. "postgresql.service"
TargetScope Scope // ScopeSystem → /etc/systemd/system/; ScopeUser → ~/.config/systemd/user/
Enable bool
OverridesText string // rendered drop-in (.conf) content; "" if no overrides
OverridesPath string // computed absolute path for the drop-in file
CandyName string // for ledger + drop-in naming
PriorEnabled bool // populated at install time (before enable); used for teardown restore
}
ServicePackagedStep enables a systemd unit that arrived via a distro package (e.g. postgresql.service). Optional drop-in overrides are written alongside the packaged unit without touching it. This step is only emitted when rendering to a systemd-based init system; supervisord targets emit a warning-and-skip.
func (*ServicePackagedStep) Kind ¶ added in v0.2026210.2308
func (s *ServicePackagedStep) Kind() StepKind
func (*ServicePackagedStep) RequiresGate ¶ added in v0.2026210.2308
func (s *ServicePackagedStep) RequiresGate() Gate
func (*ServicePackagedStep) Reverse ¶ added in v0.2026210.2308
func (s *ServicePackagedStep) Reverse() []ReverseOp
func (*ServicePackagedStep) Scope ¶ added in v0.2026210.2308
func (s *ServicePackagedStep) Scope() Scope
func (*ServicePackagedStep) Venue ¶ added in v0.2026210.2308
func (s *ServicePackagedStep) Venue() Venue
type ServiceRenderContext ¶
type ServiceRenderContext struct {
Name string `yaml:"name,omitempty" json:"name,omitempty"`
Candy string `yaml:"candy,omitempty" json:"candy,omitempty"`
Exec string `yaml:"exec,omitempty" json:"exec,omitempty"`
Env map[string]string `yaml:"env,omitempty" json:"env,omitempty"`
EnvList []EnvKV `yaml:"env_list,omitempty" json:"env_list,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"`
PackagedUnit string `yaml:"packaged_unit,omitempty" json:"packaged_unit,omitempty"`
Home string `yaml:"home,omitempty" json:"home,omitempty"`
SystemUnitDir string `yaml:"system_unit_dir,omitempty" json:"system_unit_dir,omitempty"`
UserUnitDir string `yaml:"user_unit_dir,omitempty" json:"user_unit_dir,omitempty"`
FragmentDir string `yaml:"fragment_dir,omitempty" json:"fragment_dir,omitempty"`
// Lifecycle directives (supervisord + systemd).
Kind string `yaml:"kind,omitempty" json:"kind,omitempty"`
Events string `yaml:"events,omitempty" json:"events,omitempty"`
AutoStart *bool `yaml:"auto_start,omitempty" json:"auto_start,omitempty"`
StartRetries int `yaml:"start_retries,omitempty" json:"start_retries,omitempty"`
StartSecs int `yaml:"start_secs,omitempty" json:"start_secs,omitempty"`
StopSignal string `yaml:"stop_signal,omitempty" json:"stop_signal,omitempty"`
ExitCodes string `yaml:"exit_codes,omitempty" json:"exit_codes,omitempty"`
Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
// Start hooks (systemd only — supervisord's service_template references
// neither, exactly as it already ignores wanted_by/before). Carried through
// from the entry's exec_start_pre/exec_start_post so the template emits one
// ExecStartPre=/ExecStartPost= line per element, in authored order.
ExecStartPre []string `yaml:"exec_start_pre,omitempty" json:"exec_start_pre,omitempty"`
ExecStartPost []string `yaml:"exec_start_post,omitempty" json:"exec_start_post,omitempty"`
// render_dropin is the host-precomputed drop-in decision (the entry
// carries Overrides). PackagedUnit != "" selects the packaged branch. The
// host derives both from the ServiceEntry so the plugin renders from the
// ctx alone.
RenderDropin bool `yaml:"render_dropin,omitempty" json:"render_dropin,omitempty"`
}
#ServiceRenderContext is the data an init system's service_template renders against — everything the renderer needs, nothing else reachable. The host computes it per service and hands it to candy/plugin-init's OpResolve.
func BuildServiceRenderContext ¶ added in v0.2026213.1726
func BuildServiceRenderContext(entry *ServiceEntry, ctx ServiceRenderContext) ServiceRenderContext
BuildServiceRenderContext fills the entry-derived, home-expanded render context (a pure ServiceEntry projection — no init-system knowledge). The plugin renders its templates against this; the packaged/drop-in branch decisions are precomputed here (PackagedUnit, RenderDropin) so the plugin renders from the ctx alone.
type ServiceRenderInput ¶
type ServiceRenderInput struct {
Init RawBody `yaml:"init,omitempty" json:"init"`
Ctx ServiceRenderContext `yaml:"ctx,omitempty" json:"ctx"`
}
#ServiceRenderInput is candy/plugin-init's OpResolve input for the service-render leg: the OPAQUE init body (the chosen init system's config) + the host-built render context (all entry-derived fields, home-expanded, with the packaged/drop-in branch decisions precomputed). The plugin renders from the ctx alone.
type ServiceRenderReply ¶
type ServiceRenderReply struct {
Rendered *RenderedService `yaml:"rendered,omitempty" json:"rendered,omitempty"`
}
#ServiceRenderReply wraps the rendered service.
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. ---------------------------------------------------------------------------
type ShellEntry ¶
type ShellEntry struct {
Origin string `yaml:"origin,omitempty" json:"origin"`
ID string `yaml:"id,omitempty" json:"id,omitempty"`
Generic *ShellSpec `yaml:"generic,omitempty" json:"generic,omitempty"`
ByShell map[string]*ShellSpec `yaml:"by_shell,omitempty" json:"by_shell,omitempty"`
Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
}
#ShellEntry — one origin's shell-init contribution (in ai.opencharly.shell). origin required.
type ShellHookStep ¶ added in v0.2026210.2308
type ShellHookStep struct {
CandyName string
EnvVars map[string]string
PathAdd []string // already {{.Home}}-substituted to absolute paths
EnvFile string // computed path (~/.config/opencharly/env.d/<candy>.env); populated at install
}
ShellHookStep records the env vars and PATH contributions a candy makes to the user's shell environment. On the OCI target these translate to `ENV K=V` directives in the Containerfile. On the host target they become `~/.config/opencharly/env.d/<candy>.env` plus a managed block in the user's shell init that sources the env.d directory.
func (*ShellHookStep) Kind ¶ added in v0.2026210.2308
func (s *ShellHookStep) Kind() StepKind
func (*ShellHookStep) RequiresGate ¶ added in v0.2026210.2308
func (s *ShellHookStep) RequiresGate() Gate
func (*ShellHookStep) Reverse ¶ added in v0.2026210.2308
func (s *ShellHookStep) Reverse() []ReverseOp
func (*ShellHookStep) Scope ¶ added in v0.2026210.2308
func (s *ShellHookStep) Scope() Scope
func (*ShellHookStep) Venue ¶ added in v0.2026210.2308
func (s *ShellHookStep) Venue() Venue
type ShellSnippetStep ¶ added in v0.2026210.2308
type ShellSnippetStep struct {
CandyName string // candy this snippet belongs to (also Marker source)
Origin string // "<candy>" or "box" or "deploy" (for ledger refcount + LabelShell origin)
Shell string // bash | zsh | fish | sh
Snippet string // rendered body, ${SHELL_NAME}-substituted, ready to write
PathAppend []string // already rendered into Snippet by the compiler; tracked here for label round-trip / overlay
Destination string // resolved per-target at compile time; absolute path on the target
Marker string // managed-block marker tag (= CandyName) — consumed by the kit walk's managed-block splice (sdk/kit/profile.go)
UseDropin bool // true: write whole file (fish, container drop-in); false: managed-block append into existing rc file
Priority int // load-order hint, 0 = default
}
ShellSnippetStep records a per-(candy, shell) init snippet emitted from a candy's `shell:` block. The compiler emits one step per (candy, shell) pair after applying the selection rule (per-shell ByShell entry wins over generic, with ${SHELL_NAME} substitution).
Per-target rendering:
- OCITarget: snippet bytes are content-address-staged and COPYed to a system-wide drop-in (/etc/profile.d/charly-<candy>.sh for bash/zsh/sh, /etc/fish/conf.d/charly-<candy>.fish for fish).
- the external local / vm deploy (kit.WalkPlans on the venue): managed-block append to the user's rc file (~/.bashrc, ~/.zshrc, ~/.profile) keyed by `# opencharly:begin <Marker>` fence; for fish, a per-candy drop-in at ~/.config/fish/conf.d/charly-<candy>.fish (no fence needed, file IS the unit). UseDropin discriminates the two paths.
- the external k8s substrate: does not consume the InstallPlan IR at all (the host generates a Kustomize tree separately — no shell snippet).
func (*ShellSnippetStep) Kind ¶ added in v0.2026210.2308
func (s *ShellSnippetStep) Kind() StepKind
func (*ShellSnippetStep) RequiresGate ¶ added in v0.2026210.2308
func (s *ShellSnippetStep) RequiresGate() Gate
func (*ShellSnippetStep) Reverse ¶ added in v0.2026210.2308
func (s *ShellSnippetStep) Reverse() []ReverseOp
func (*ShellSnippetStep) Scope ¶ added in v0.2026210.2308
func (s *ShellSnippetStep) Scope() Scope
Scope: SnippetStep is system-wide for build-mode (Container drop-ins live under /etc/profile.d/ — root-owned), and user-profile for host/vm deploy- mode (managed block in ~/.bashrc etc.). The compiler picks the right Scope when constructing the step based on the destination path; the step records the choice so each emitter doesn't have to re-derive it.
func (*ShellSnippetStep) Venue ¶ added in v0.2026210.2308
func (s *ShellSnippetStep) Venue() Venue
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 SidecarResolveInput ¶
type SidecarResolveInput struct {
EmbeddedTemplates map[string]RawBody `yaml:"embedded_templates,omitempty" json:"embedded_templates,omitempty"`
ProjectTemplates map[string]RawBody `yaml:"project_templates,omitempty" json:"project_templates,omitempty"`
DeployOverrides map[string]RawBody `yaml:"deploy_overrides,omitempty" json:"deploy_overrides,omitempty"`
CliEnv []string `yaml:"cli_env,omitempty" json:"cli_env,omitempty"`
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
}
#SidecarResolveInput is the input to candy/plugin-sidecar's OpResolve leg (the host-side sidecar de-type): the three sidecar-def layers to merge (embedded template base, project-root templates, per-deploy overrides — each a name→Sidecar map the host keeps OPAQUE) plus the CLI -e flags to route and the box/instance for name scoping.
type SidecarResolveReply ¶
type SidecarResolveReply struct {
Sidecars []ResolvedSidecar `yaml:"sidecars,omitempty" json:"sidecars,omitempty"`
AppEnv []string `yaml:"app_env,omitempty" json:"app_env,omitempty"`
PersistOverrides map[string]RawBody `yaml:"persist_overrides,omitempty" json:"persist_overrides,omitempty"`
}
#SidecarResolveReply is candy/plugin-sidecar's OpResolve reply: the resolved, generation-ready sidecars the host feeds to quadlet gen, the CLI env flags NOT routed to any sidecar (app-only), and the per-deploy sidecar overrides with any routed env folded in.
type SidecarSecret ¶
type SidecarVolume ¶
type StandaloneLoad ¶
type StandaloneLoad struct {
Shape string `yaml:"shape,omitempty" json:"shape"`
Deploy *Deploy `yaml:"deploy,omitempty" json:"deploy,omitempty"`
Template RawBody `yaml:"template,omitempty" json:"template,omitempty"`
Box *Box `yaml:"box,omitempty" json:"box,omitempty"`
Candy *Candy `yaml:"candy,omitempty" json:"candy,omitempty"`
}
#StandaloneLoad carries a structural kind's host-pre-decoded canonical node. Exactly one of Deploy / Template / Box / Candy is set, matching Shape.
type Status ¶
type Status int
status_result.go — the check-engine's pass/fail/skip verdict enum (FLOOR-SLIM Unit 4, moved from sdk/kit as part of the CheckResult wire-envelope split). HAND-WRITTEN — a distinct named int type with the iota consts + String() method, NOT emitted by `task cue:gen` (gengotypes has no construct for an iota-based enum + a Stringer method; CUE owns the wire VALUE SET via #CheckResult.status's plain `int` shape, referenced here via @go(Status,type=Status) — this file supplies the named Go type that reference points at). Mirrors the #SubstrateKind split in status_types.go (a string-backed enum suppressed via @go(-) with its Go type hand-written here-style) — Status is int-backed, so there is no separate disjunction def to suppress; the CUE field is a plain `int`.
type StatusNestedNode ¶
type StatusNestedNode struct {
Key string `yaml:"key,omitempty" json:"key"`
Path string `yaml:"path,omitempty" json:"path"`
Kind SubstrateKind `yaml:"kind,omitempty" json:"kind"`
HasChildren bool `yaml:"has_children,omitempty" json:"has_children"`
MatchKeys []string `yaml:"match_keys,omitempty" json:"match_keys,omitempty"`
LiveStatus string `yaml:"live_status,omitempty" json:"live_status,omitempty"`
Children []*StatusNestedNode `yaml:"children,omitempty" json:"children,omitempty"`
}
#StatusNestedNode — one pre-resolved node of the DECLARED nested tree, now built PLUGIN-SIDE (candy/plugin-status/nested_tree.go, K5) directly from the resolved-project envelope + deploykit.LoadBundleConfig/MergeDeployConfigs/ClassifyTarget/ResolveDeployChain — all sdk-portable, so this is no longer a host-computed wire payload; it stays a CUE def only because #StatusNestedNode is self-recursive and the plugin still needs the generated Go type. key is the declared child key (the Image cell). match_keys index the flat rows; for a ROOT node key itself is the flat match. RECURSIVE self-reference mirrors the deploy tree nesting.
type StatusSubstrateReply ¶
type StatusSubstrateReply struct {
Rows []DeploymentStatus `yaml:"rows,omitempty" json:"rows,omitempty"`
Single DeploymentStatus `yaml:"single,omitempty" json:"single,omitempty"`
}
#StatusSubstrateReply — the host-collection result: the flat rows (all substrates, already probed) and — on the single path — the one detail row. The declared nested roots are no longer carried here (K5: resolved plugin-side, see #StatusNestedNode) — the candy computes its own roots and applies the PURE overlay(rows, roots) then renders.
type StatusSubstrateRequest ¶
type StatusSubstrateRequest struct {
Single bool `yaml:"single,omitempty" json:"single,omitempty"`
IncludeAll bool `yaml:"include_all,omitempty" json:"include_all,omitempty"`
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
}
#StatusSubstrateRequest — the host-collection request the command:status plugin sends over HostBuild("status-substrate"). single=true selects the pod-scoped detail path (box+instance); otherwise the full multi-substrate fan-out (include_all mirrors --all). The declared nested tree (formerly resolved here via `nested`) is now resolved PLUGIN-SIDE (candy/plugin-status's own buildStatusRootsTree, K5) directly off the resolved-project envelope + deploykit.LoadBundleConfig — no host round-trip, so this request carries no nested flag anymore.
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) KeywordText ¶
KeywordText returns the populated keyword's prose regardless of which discriminator holds it.
func (*Step) Mutates ¶
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 StepBatch ¶
type StepBatch struct {
Scope Scope
Venue Venue
Steps []InstallStep
}
StepBatch is a contiguous run of steps sharing the same (Scope, Venue). Emitted together: one sudo heredoc, one user heredoc, or one podman run per batch.
type StepContract ¶
type StepContract struct {
Scope Scope
Venue Venue
Gate Gate
// Emits is the F-STEP-EMIT flag: the step produces a build-context Containerfile FRAGMENT
// (the serving plugin answers Invoke(OpEmit) → EmitReply.Fragment). The pod-overlay
// deploykit.OCITarget consults it via the open external-step arm — Emits=true → bake the
// fragment; Emits=false → skip (a deploy-only external step, like apk on an image build).
// Advisory for the DEPLOY leg (executeExternalStep ignores it); load-bearing for the BUILD
// leg (ociEmitStep).
Emits bool
}
StepContract is a class:step plugin's DECLARED install-step contract (F3), decoded from its Describe capability (pb.StepContract / sdk.StepContract). compileActOp reads it (via StepContractCarrier) to build an externalStep carrying the plugin-declared Scope/Venue/Gate — the contract the host applies via the open default arm with NO compiled-in case.
type StepContractCarrier ¶
type StepContractCarrier interface {
DeclaredStepContract() (StepContract, bool)
}
StepContractCarrier is implemented by a provider (grpcProvider out-of-proc, inprocProvider compiled-in) that carries a class:step capability's declared StepContract. A false return means the provider declares no step contract (every non-step capability).
type StepEmitRequest ¶
type StepEmitRequest struct {
Word string `yaml:"word,omitempty" json:"word"`
Payload RawBody `yaml:"payload,omitempty" json:"payload,omitempty"`
Distros []string `yaml:"distros,omitempty" json:"distros,omitempty"`
}
#StepEmitRequest is the F-STEP-EMIT HostBuild envelope for a HOST-COUPLED external step kind's build-context fragment.
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 StepKind ¶
type StepKind string
StepKind names the concrete type behind an InstallStep. Used for ledger serialization and target-specific dispatch (e.g. HostDeploy knows to invoke the pixi builder differently than cargo).
const ( StepKindSystemPackages StepKind = "SystemPackages" StepKindBuilder StepKind = "Builder" StepKindOp StepKind = "Op" StepKindFile StepKind = "File" StepKindServicePackaged StepKind = "ServicePackaged" StepKindServiceCustom StepKind = "ServiceCustom" StepKindShellHook StepKind = "ShellHook" StepKindShellSnippet StepKind = "ShellSnippet" StepKindRepoChange StepKind = "RepoChange" StepKindApkInstall StepKind = "ApkInstall" StepKindLocalPkgInstall StepKind = "LocalPkgInstall" StepKindReboot StepKind = "Reboot" StepKindExternalPlugin StepKind = "ExternalPlugin" )
type StepPass ¶ added in v0.2026213.1726
type StepPass struct {
Stdout string `yaml:"stdout,omitempty" json:"stdout,omitempty"`
Stderr string `yaml:"stderr,omitempty" json:"stderr,omitempty"`
ExitCode int `yaml:"exit_code,omitempty" json:"exit_code,omitempty"`
}
#StepPass is the verbatim stdout/stderr/exit-code of a host-delegated guest sub-invocation (the nested pod-in-VM check-live delegation, runVm's guestNestedCheckCmd path). The plugin writes Stdout/Stderr and returns ExitCode unchanged, so a guest-run check reports byte-identically to a direct one. CUE-sourced (SDD): plain carrier, no json:"-".
type StepRecord ¶ added in v0.2026215.1748
type StepRecord struct {
Kind StepKind `json:"kind"`
Scope Scope `json:"scope,omitempty"`
Venue Venue `json:"venue,omitempty"`
Summary string `json:"summary,omitempty"`
CompletedAt string `json:"completed_at"`
Extra map[string]string `json:"extra,omitempty"`
}
StepRecord is a thin summary of a completed InstallStep that the ledger keeps for audit. Kept intentionally small — the ReverseOps list on CandyRecord is the source of truth for teardown.
type StepResult ¶ added in v0.2026213.1726
type StepResult struct {
Keyword string `yaml:"keyword,omitempty" json:"keyword"`
Text string `yaml:"text,omitempty" json:"text"`
Origin string `yaml:"origin,omitempty" json:"origin,omitempty"`
StepID string `yaml:"step_id,omitempty" json:"step_id"`
Result CheckResult `yaml:"result,omitempty" json:"result"`
}
#StepResult is one plan step's outcome — the step's identity (keyword/text/origin/step_id) plus the CheckResult of running it; the result reporters consume a []StepResult. CUE-sourced (SDD): a plain carrier — every field is a string or the CUE-sourced #CheckResult by value, with NO json:"-" and NO disjunction, so gengotypes generates it faithfully (byte-identical JSON to the former hand-written spec.StepResult). The P12 gengotypes exception (cited at sdk/kit/checkrun_seam.go + schema/seam.cue) covers ONLY kit.CheckResult's engine-internal `DeadlineExceeded bool json:"-"` — a field that exists in memory but is excluded from marshaling — which lives on the kit-internal ENGINE wrapper (struct { spec.CheckResult; DeadlineExceeded bool json:"-" }), NOT on this wire type: StepResult.Result is spec.CheckResult (the CUE-sourced base, no json:"-"), so the wire form carries only spec.CheckResult fields and the kit wrapper adds DeadlineExceeded in memory only, dropped at the StepResult boundary.
type StepScore ¶
type StepScore struct {
ID string `yaml:"id,omitempty" json:"id"`
Origin string `yaml:"origin,omitempty" json:"origin,omitempty"`
Text string `yaml:"text,omitempty" json:"text,omitempty"`
Tag []string `yaml:"tag,omitempty" json:"tag,omitempty"`
Keyword string `yaml:"keyword,omitempty" json:"keyword,omitempty"`
Verb string `yaml:"verb,omitempty" json:"verb,omitempty"`
Status string `yaml:"status,omitempty" json:"status"`
SkippedReason string `yaml:"skipped_reason,omitempty" json:"skipped_reason,omitempty"`
}
#StepScore — the scorer's verdict for one check:/agent-check: step, keyed by step id.
type StrMap ¶
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 StructuralKindCarrier ¶
type StructuralKindCarrier interface {
IsStructuralKind() bool
}
StructuralKindCarrier is implemented by a provider (grpcProvider out-of-proc, inprocProvider compiled-in) that carries a class:kind capability's STRUCTURAL flag (F5). true → the kind's OpLoad returns a Deploy member tree the host folds into uf.Bundle; false (or not implemented) → the flat F4 path (opaque body → uf.PluginKinds).
type StructuralKindLoadEnv ¶
type StructuralKindLoadEnv struct {
Members map[string]*Deploy `yaml:"members,omitempty" json:"members,omitempty"`
// standalone is the host-pre-decoded CANONICAL node channel (candy/plugin-substrate,
// candy/plugin-candy).
Standalone *StandaloneLoad `yaml:"standalone,omitempty" 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).
type SubstrateKind ¶
type SubstrateKind string
SubstrateKind identifies which deployment substrate a DeploymentStatus row came from — the discriminator that lets `charly status` present pod, VM, k8s, local, and android deployments side-by-side from one unified table. The command:status plugin (fan-out + render) and every substrate plugin's status-collect Op share this type; #DeploymentStatus.kind references it via @go(Kind,type=SubstrateKind).
HAND-WRITTEN — a distinct named string type with the substrate consts, NOT emitted by `task cue:gen` (gengotypes references it from the generated DeploymentStatus.Kind field but does not define it; a CUE string enum would degrade to a plain string and lose the typed consts the collectors switch on).
const ( SubstratePod SubstrateKind = "pod" SubstrateVM SubstrateKind = "vm" SubstrateK8s SubstrateKind = "k8s" SubstrateLocal SubstrateKind = "local" SubstrateAndroid SubstrateKind = "android" )
type SubstrateStatusReply ¶
type SubstrateStatusReply struct {
Rows []DeploymentStatus `yaml:"rows,omitempty" json:"rows,omitempty"`
Single DeploymentStatus `yaml:"single,omitempty" json:"single,omitempty"`
}
#SubstrateStatusReply — the per-substrate COLLECTOR result: the LIVE rows the substrate plugin produced (pod: snapshot-derived + live mounts + tool probes; local: install-ledger rows). The host applies the deploy enrichment (tunnel + image-label fallback) to the pod rows after they cross this seam; the single field carries the one pod detail row.
type SubstrateStatusRequest ¶
type SubstrateStatusRequest struct {
IncludeAll bool `yaml:"include_all,omitempty" json:"include_all,omitempty"`
RunMode string `yaml:"run_mode,omitempty" json:"run_mode"`
QuadletDir string `yaml:"quadlet_dir,omitempty" json:"quadlet_dir"`
EngineBin string `yaml:"engine_bin,omitempty" json:"engine_bin"`
Single bool `yaml:"single,omitempty" json:"single,omitempty"`
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
}
#SubstrateStatusRequest — the per-substrate COLLECTOR request the host sends to the substrate plugin's OpStatusCollect (P14a: the cleanly-movable collectors — pod live + local + the probes — relocated into candy/plugin-substrate, served on the kind provider's Invoke by word pod/vm/k8s/local/android). The host passes the scalar inputs a sdk-only candy cannot derive: the engine binary name (engine_bin), the run mode, the quadlet dir (pod's quadlet-description enrichment + enabled-but-not-running append), include_all (--all), and — on the single path — box+instance. NO deploy-cone (BundleConfig/UnifiedFile) crosses this seam: the deploy enrichment stays host-side until K5, applied to the live rows this reply returns. vm/k8s/android are deferred to K5 (their collectors are deploy-cone-coupled); the plugin returns no rows for those words until then.
type SubstrateTemplateResolveRequest ¶
type SubstrateTemplateResolveRequest struct {
Local *LocalResolveInput `yaml:"local,omitempty" json:"local,omitempty"`
Android *AndroidResolveInput `yaml:"android,omitempty" json:"android,omitempty"`
Pod *PodResolveInput `yaml:"pod,omitempty" json:"pod,omitempty"`
K8s *K8sResolveInput `yaml:"k8s,omitempty" json:"k8s,omitempty"`
Vm *VmResolveInput `yaml:"vm,omitempty" json:"vm,omitempty"`
}
#SubstrateTemplateResolveRequest is the discriminated OpResolve request for the substrate-template de-type: exactly one of Local / Android / Pod is set.
type SystemPackagesStep ¶ added in v0.2026210.2308
type SystemPackagesStep struct {
Format string // "rpm" | "deb" | "pac"
Phase Phase // PhasePrepare | PhaseInstall | PhaseCleanup
Packages []string // package names (for Reverse)
Repos []RepoSpec // repository entries from the candy manifest (drives PhasePrepare)
Options []string // format-specific install flags
Copr []string // RPM-only: COPR repos to enable/disable
Modules []string // RPM-only: DNF modules to enable
Exclude []string // RPM-only: packages to exclude
Keys []string // PAC-only: GPG keys to trust
// CacheMounts and RawInstallContext are passed to template rendering.
// These are populated by the compiler and consumed by the OCI target;
// the host target ignores CacheMounts (no BuildKit outside containers).
CacheMount []CacheMountSpec
RawInstallContext map[string]any
}
SystemPackagesStep installs packages via a distro package manager. One step typically expands to (PhasePrepare + PhaseInstall + PhaseCleanup) stages; the compiler emits one SystemPackagesStep per phase so the host target can gate PhasePrepare on --allow-repo-changes.
func (*SystemPackagesStep) Kind ¶ added in v0.2026210.2308
func (s *SystemPackagesStep) Kind() StepKind
func (*SystemPackagesStep) RequiresGate ¶ added in v0.2026210.2308
func (s *SystemPackagesStep) RequiresGate() Gate
func (*SystemPackagesStep) Reverse ¶ added in v0.2026210.2308
func (s *SystemPackagesStep) Reverse() []ReverseOp
func (*SystemPackagesStep) Scope ¶ added in v0.2026210.2308
func (s *SystemPackagesStep) Scope() Scope
func (*SystemPackagesStep) Venue ¶ added in v0.2026210.2308
func (s *SystemPackagesStep) Venue() Venue
type TagInfo ¶
type TagInfo struct {
Box string `yaml:"box,omitempty" json:"box"`
Ref string `yaml:"ref,omitempty" json:"ref"`
Version string `yaml:"version,omitempty" json:"version"`
InUse bool `yaml:"in_use,omitempty" json:"in_use"`
}
#TagInfo is one locally stored image tag, as presented by `charly box list tags` (verb:retention list=true): version is "-" when the image carries no parseable ai.opencharly.version label.
type TagPkgConfig ¶
type TagPkgConfig struct {
Package []string `yaml:"package,omitempty" json:"package,omitempty"`
Raw map[string]any `yaml:"raw,omitempty" json:"raw,omitempty"`
}
#TagPkgConfig — a distro/version-specific package section (debian:13, ubuntu:24.04, fedora:43…). Raw captures the full YAML so tag sections carry repos:/options:/keys:. Mirrors deploykit.TagPkgConfig.
type TargetHop ¶
type TargetHop struct {
Transport string `yaml:"transport,omitempty" json:"transport"`
Address string `yaml:"address,omitempty" json:"address,omitempty"`
User string `yaml:"user,omitempty" json:"user,omitempty"`
Port int64 `yaml:"port,omitempty" json:"port,omitempty"`
IdentityFile string `yaml:"identity_file,omitempty" json:"identity_file,omitempty"`
Command []string `yaml:"command,omitempty" json:"command,omitempty"`
Env StrMap `yaml:"env,omitempty" json:"env,omitempty"`
// Transport-native argv options expressed as deterministic key/value
// data. SSH renders these as repeated `-o key=value` arguments; no shell
// interpolation is involved.
Options StrMap `yaml:"options,omitempty" json:"options,omitempty"`
}
type TargetSpec ¶
type TargetSpec struct {
// Ordered outer-to-inner route after placement. An empty route is the
// selected process. deployment/instance first place that process in any
// Charly deployment; the remaining route composes identically there.
Hops []TargetHop `yaml:"hops,omitempty" json:"hops,omitempty"`
Deployment string `yaml:"deployment,omitempty" json:"deployment,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
WorkingDir string `yaml:"working_dir,omitempty" json:"working_dir,omitempty"`
}
type TerminalExit ¶
type TerminalFrame ¶
type TerminalFrame struct {
RunID UUIDv7 `yaml:"run_id,omitempty" json:"run_id"`
Sequence int64 `yaml:"sequence,omitempty" json:"sequence"`
Kind string `yaml:"kind,omitempty" json:"kind"`
Stream string `yaml:"stream,omitempty" json:"stream,omitempty"`
Data []byte `yaml:"data,omitempty" json:"data,omitempty"`
Snapshot string `yaml:"snapshot,omitempty" json:"snapshot,omitempty"`
Status string `yaml:"status,omitempty" json:"status,omitempty"`
ExitCode *int `yaml:"exit_code,omitempty" json:"exit_code,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
type TerminalInput ¶
type TerminalInput struct {
Kind string `yaml:"kind,omitempty" json:"kind"`
Text string `yaml:"text,omitempty" json:"text,omitempty"`
Key string `yaml:"key,omitempty" json:"key,omitempty"`
Cols int64 `yaml:"cols,omitempty" json:"cols,omitempty"`
Rows int64 `yaml:"rows,omitempty" json:"rows,omitempty"`
Signal string `yaml:"signal,omitempty" json:"signal,omitempty"`
}
type TerminalKey ¶
type TerminalKey struct {
Key string `yaml:"key,omitempty" json:"key"`
}
type TerminalProfile ¶
type TerminalProfile struct {
Name string `yaml:"name,omitempty" json:"name"`
Entrypoint []string `yaml:"entrypoint,omitempty" json:"entrypoint"`
WorkingDir string `yaml:"working_dir,omitempty" json:"working_dir,omitempty"`
Env StrMap `yaml:"env,omitempty" json:"env,omitempty"`
Cols int64 `yaml:"cols,omitempty" json:"cols"`
Rows int64 `yaml:"rows,omitempty" json:"rows"`
Readiness map[string]any `yaml:"readiness,omitempty" json:"readiness,omitempty"`
SemanticAdapter string `yaml:"semantic_adapter,omitempty" json:"semantic_adapter,omitempty"`
Keys []string `yaml:"keys,omitempty" json:"keys,omitempty"`
Signals []string `yaml:"signals,omitempty" json:"signals,omitempty"`
Persistence string `yaml:"persistence,omitempty" json:"persistence,omitempty"`
Transcript string `yaml:"transcript,omitempty" json:"transcript,omitempty"`
}
type TerminalResize ¶
type TerminalSnapshot ¶
type TerminalSnapshot struct {
RunID UUIDv7 `yaml:"run_id,omitempty" json:"run_id"`
Sequence int64 `yaml:"sequence,omitempty" json:"sequence"`
Cols int64 `yaml:"cols,omitempty" json:"cols"`
Rows int64 `yaml:"rows,omitempty" json:"rows"`
Screen string `yaml:"screen,omitempty" json:"screen"`
CursorCol int64 `yaml:"cursor_col,omitempty" json:"cursor_col,omitempty"`
CursorRow int64 `yaml:"cursor_row,omitempty" json:"cursor_row,omitempty"`
}
type Threaded ¶
type Threaded struct {
Kinds map[string]bool // recognizedKind
DeploySubstrates map[string]bool // recognizedDeploySubstrate
StructuralKinds map[string]bool // externalKindMayNestMembers
Primaries map[string]string // pluginPrimaryFor: verb word → scalar-sugar primary field
// DeployTraits is the substrate word → DECLARED #DeployTraits map (K1-LOADER RELOCATION): the
// descent stamp reads THIS registry-derived DATA snapshot instead of querying the provider
// registry live, so the venue-hop descent stamp (loaderkit.StampBundleDescents) is registry-free
// and can run plugin-side. The host fills it (deployTraitsFor per recognized kind/substrate word)
// exactly as the former live stampBundleDescents did; a word absent from the map resolves to the
// external-in-place default, matching deployTraitsFor's nil-for-unrecognized-word semantics.
DeployTraits map[string]*DeployTraits
// ExternalDeploySubstrates is the EXACT set of words for which the host's registry-live
// isExternalDeploySubstrate returns true (K1-LOADER RELOCATION): the host fills it by EVALUATING
// its own isExternalDeploySubstrate over every recognized substrate word, so a plugin-side
// validator (loaderkit.ValidateCheckBeds) checks membership here instead of RECONSTRUCTING the
// host's resourceKindSet ∧ externalizedDeploySubstrates ∧ recognizedDeploySubstrate decision —
// the byte-exact predicate is threaded, never approximated. A word absent from the set is
// NOT an external deploy substrate (isExternalDeploySubstrate would return false for it).
ExternalDeploySubstrates map[string]bool
}
Threaded is the host-computed, registry-derived DATA the per-document parse consults instead of querying the provider registry (boundary law clause D): which words are recognized kinds / deploy substrates, which external kinds may nest sub-entity members, and each plugin verb's scalar-sugar primary field. The host snapshots it before the parse; the parse never touches the registry.
type ToolStatus ¶
type ToolStatus struct {
Name string `yaml:"name,omitempty" json:"name"`
Status string `yaml:"status,omitempty" json:"status"`
Port int `yaml:"port,omitempty" json:"port,omitempty"`
Detail string `yaml:"detail,omitempty" json:"detail,omitempty"`
}
#ToolStatus — one live tool-probe result row. status "-" means the tool is not configured in this deployment; it is filtered before render.
type TunnelConfig ¶
type TunnelConfig struct {
// provider — "tailscale" or "cloudflare".
Provider string `yaml:"provider,omitempty" json:"provider,omitempty"`
// tunnel_name — cloudflare: tunnel name.
TunnelName string `yaml:"tunnel_name,omitempty" json:"tunnel_name,omitempty"`
// hostname — cloudflare: default hostname (from the image dns field).
Hostname string `yaml:"hostname,omitempty" json:"hostname,omitempty"`
// box_name — for PID file naming / cloudflare tunnel-name default.
BoxName string `yaml:"box_name,omitempty" json:"box_name,omitempty"`
// ports — all tunneled ports with their access scope.
Ports []TunnelPort `yaml:"ports,omitempty" json:"ports,omitempty"`
}
#TunnelConfig — the resolved, ready-to-execute tunnel configuration.
func ResolveTunnelConfig ¶ added in v0.2026213.1726
func ResolveTunnelConfig(t *TunnelYAML, boxName string, dns string, _ map[string]CandyReader, _ []string, portProtos map[string]string, boxPorts []string) *TunnelConfig
ResolveTunnelConfig resolves a TunnelYAML into a TunnelConfig with defaults applied. portProtos maps container port -> protocol ("http" or "tcp") from candy PortSpec data. boxPorts is the list of image port mappings (e.g. "18789:18789", "443:18789").
func TunnelConfigFromMetadata ¶ added in v0.2026213.1726
func TunnelConfigFromMetadata(meta *BoxMetadata) *TunnelConfig
TunnelConfigFromMetadata creates a TunnelConfig from image label metadata. Unlike ResolveTunnelConfig, this doesn't need candy access since the tunnel configuration is already stored in the label.
type TunnelPort ¶
type TunnelPort struct {
// port — the tailscale HTTPS listen port (must be a valid serve/funnel port).
Port int `yaml:"port,omitempty" json:"port,omitempty"`
// backend_port — the localhost backend port (0 means same as port).
BackendPort int `yaml:"backend_port,omitempty" json:"backend_port,omitempty"`
// protocol — backend scheme: http | https | https+insecure | tcp |
// tls-terminated-tcp | ssh | rdp | smb (udp is skipped, never tunneled).
Protocol string `yaml:"protocol,omitempty" json:"protocol,omitempty"`
// public — true = internet-accessible (funnel), false = private (serve).
Public bool `yaml:"public,omitempty" json:"public,omitempty"`
// hostname — cloudflare: per-port hostname (from the map form).
Hostname string `yaml:"hostname,omitempty" json:"hostname,omitempty"`
}
#TunnelPort — a single port to tunnel with its protocol and access scope.
func (TunnelPort) Backend ¶
func (tp TunnelPort) Backend() int
Backend returns the localhost backend port for a tunneled port, defaulting to Port when BackendPort is unset (0). Pure structural method on the generated spec.TunnelPort wire type (schema/tunnel.cue) — the ONE home (R3) for the backend-port default the pod quadlet emitter (sdk/deploykit) consumes. Lives here beside the type it acts on (the charly_methods.go convention): touches ONLY the type's own fields + the stdlib.
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 UnifiedFile ¶ added in v0.2026210.808
type UnifiedFile struct {
Version string `yaml:"version,omitempty" json:"version,omitempty"`
// Repo is this project's canonical repo identity (e.g. "github.com/opencharly/charly").
// Optional; only meaningful on the ROOT file.
Repo string `yaml:"repo,omitempty" json:"repo,omitempty"`
// Import is the SINGLE composition statement. A list whose items are either a bare string
// (flat import into THIS root namespace) or a single-key map `alias: ref` (a namespaced
// child import). See ImportList (load_directives.go).
Import ImportList `yaml:"import,omitempty" json:"import,omitempty"`
Discover DiscoverConfig `yaml:"discover,omitempty" json:"discover,omitempty"`
// Defaults carries the `defaults:` block (BoxConfig-shaped inheritance defaults).
Defaults BoxConfig `yaml:"defaults,omitempty" json:"defaults,omitempty"`
// Box is the generic kind-keyed IMAGE map (P6): name → opaque marshaled BoxConfig.
Box BoxMap `yaml:"box,omitempty" json:"box,omitempty"`
// Candy is the generic kind-keyed LAYER map: name → opaque marshaled InlineCandy.
Candy map[string]json.RawMessage `yaml:"candy,omitempty" json:"candy,omitempty"`
// Bundle is the flat name-keyed deploy map (the canonical `deploy:` surface).
Bundle map[string]BundleNode `yaml:"deploy,omitempty" json:"deploy,omitempty"`
Provides *ProvidesConfig `yaml:"provides,omitempty" json:"provides,omitempty"`
// PluginKinds holds entities of KINDS contributed by plugins (a kind the core has no typed
// map for). NAME-KEYED: kind word → entity NAME → canonical body (opaque JSON). Built-in
// kinds decode into their typed maps above. Host-internal — never serialized.
PluginKinds map[string]map[string]json.RawMessage `yaml:"-" json:"-"`
// Namespaces holds child namespaces mounted by namespaced `import:` entries (alias →
// fully-resolved isolated UnifiedFile). NOT authored directly — populated by the
// materialize pass. Entries are referenced qualified, e.g. `base: cachyos.cachyos`.
Namespaces map[string]*UnifiedFile `yaml:"-"`
// RootDir is this UnifiedFile's OWN base directory — the dir its root document's SrcDir
// names. Set once per materialize, for both the top-level project and each mounted
// namespace. Empty for a project-less / synthetic UnifiedFile.
RootDir string `yaml:"-"`
}
UnifiedFile is the full schema of a single unified-format YAML document. Every field is optional — a file with only `distro:` is valid (typical for the embedded build vocabulary); a file with only `deploy:` is valid; etc.
func (*UnifiedFile) Android ¶ added in v0.2026210.808
func (uf *UnifiedFile) Android() map[string]json.RawMessage
func (*UnifiedFile) BoxConfig ¶ added in v0.2026210.808
func (uf *UnifiedFile) BoxConfig(name string) (BoxConfig, bool)
BoxConfig decodes the authored image config for name; ok=false when absent.
func (*UnifiedFile) BoxNames ¶ added in v0.2026210.808
func (uf *UnifiedFile) BoxNames() []string
BoxNames returns the image names, sorted.
func (*UnifiedFile) CheckBeds ¶ added in v0.2026210.808
func (uf *UnifiedFile) CheckBeds() map[string]BundleNode
CheckBeds returns the disposable R10 beds keyed by name. In the unified node-form model a bed IS a `disposable: true` bundle, so the bed set is derived directly from the disposable bundles in the Bundle map. Members are instruments (brought up alongside a driver), never standalone beds. Single enumeration source for `charly check run <bed>` (and the /verify-beds fan-out).
func (*UnifiedFile) HasBox ¶ added in v0.2026210.808
func (uf *UnifiedFile) HasBox(name string) bool
HasBox reports whether an image named name is present.
func (*UnifiedFile) K8s ¶ added in v0.2026210.808
func (uf *UnifiedFile) K8s() map[string]json.RawMessage
func (*UnifiedFile) Local ¶ added in v0.2026210.808
func (uf *UnifiedFile) Local() map[string]json.RawMessage
func (*UnifiedFile) Pod ¶ added in v0.2026210.808
func (uf *UnifiedFile) Pod() map[string]json.RawMessage
func (*UnifiedFile) ProjectConfig ¶ added in v0.2026210.808
func (uf *UnifiedFile) ProjectConfig() *Config
ProjectConfig returns the *Config equivalent of uf (the box config view).
func (*UnifiedFile) ProjectTemplates ¶ added in v0.2026210.808
func (uf *UnifiedFile) ProjectTemplates() *ProjectTemplates
ProjectTemplates decodes the uf.Local/K8s/Pod/VM/Android raw template maps (map[string]json.RawMessage) into the resolved kind-template maps validate/check-include/status read. Returns nil when no template kind is present. Recurses into uf.Namespaces, mirroring FillBoxPlans's prefix-accumulation pattern, so a namespace-qualified template ref (`local: <ns>.<tmpl>`, `kind:k8s` entity `<ns>.<name>`, …) is visible in the envelope too. Purely ADDITIVE (qualified keys never collide with a bare name, since a bare name can never contain "."), so every existing root-scoped consumer is unaffected.
This is UnifiedFile's own projection (relocated from loaderkit.ProjectTemplates as a METHOD — the free function's name collided with the ProjectTemplates TYPE; it is the sibling of ProjectConfig above).
func (*UnifiedFile) SetBox ¶ added in v0.2026210.808
func (uf *UnifiedFile) SetBox(name string, b BoxConfig)
SetBox stores an authored image config under name (marshaling it opaque).
func (*UnifiedFile) SetCandy ¶ added in v0.2026210.808
func (uf *UnifiedFile) SetCandy(name string, il *InlineCandy)
SetCandy stores a layer under name (marshaling it opaque).
func (*UnifiedFile) VM ¶ added in v0.2026210.808
func (uf *UnifiedFile) VM() map[string]json.RawMessage
VM/Pod/K8s/Local/Android are DERIVED accessors over uf.PluginKinds[disc] — the 5 standalone-substrate-TEMPLATE kinds fold into PluginKinds generically (no per-kind-word switch). Each returns the opaque name→body map for its kind (nil when none configured); the kernel never decodes the bodies itself — consuming PLUGINS decode a body into the concrete kind they need.
type UserInfo ¶
type UserInfo struct {
Found bool `yaml:"found,omitempty" json:"found"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
UID int `yaml:"uid,omitempty" json:"uid,omitempty"`
GID int `yaml:"gid,omitempty" json:"gid,omitempty"`
Home string `yaml:"home,omitempty" json:"home,omitempty"`
}
#UserInfo — one /etc/passwd entry (name:uid:gid:...:home). Empty reply (found=false) when no user with the requested uid exists in the image.
type VFIOGpu ¶
type VFIOGpu struct {
Addr string `yaml:"addr,omitempty" json:"addr"`
GroupMembers []VFIOPCIDevice `yaml:"group_members,omitempty" json:"group_members"`
VendorID string `yaml:"vendor_id,omitempty" json:"vendor_id"`
DeviceID string `yaml:"device_id,omitempty" json:"device_id"`
Class string `yaml:"class,omitempty" json:"class"`
ClassLabel string `yaml:"class_label,omitempty" json:"class_label"`
Driver string `yaml:"driver,omitempty" json:"driver"`
IOMMUGroup int `yaml:"iommu_group,omitempty" json:"iommu_group"`
}
#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.
FLATTENED (never a Go anonymous-embedded field — CUE unification has no concept of Go embedding; it always structurally merges fields into one flat struct). The former hand type embedded VFIOPCIDevice anonymously; Go's encoding/json ALREADY promotes an anonymous embedded struct's fields to the parent JSON object on the wire (Go's own embedding-promotion rule), so this flattened shape is BYTE-IDENTICAL JSON to the former embedded type — the only consumer-visible change is Go SOURCE: a composite literal naming the embedded field (`VFIOGpu{VFIOPCIDevice: d}`) must spread d's fields instead (`VFIOGpu{Addr: d.Addr, ...}`). Fixed at every call site in the same cutover (candy/plugin-gpu, charly test fixtures).
func NewVFIOGpu ¶
func NewVFIOGpu(d VFIOPCIDevice) VFIOGpu
gpu_methods.go — a pure Go CONSTRUCTOR for the CUE-generated, FLATTENED VFIOGpu (sdk/schema/gpu.cue -> spec/cue_types_gen.go). VFIOGpu used to embed VFIOPCIDevice anonymously (Go's own JSON-promotion rule already made the wire shape identical either way); CUE unification has no concept of Go embedding, so the SDD conversion flattened VFIOGpu's fields directly. NewVFIOGpu restores the one-line "build a VFIOGpu from a VFIOPCIDevice" ergonomic the former embedded-field composite literal (`VFIOGpu{VFIOPCIDevice: d}`) provided, so every construction site (charly, candy/plugin-gpu) names ONE spread point instead of repeating the 7-field copy (R3) — set GroupMembers on the returned value afterward, exactly as the former embedded-literal call sites already did.
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 `yaml:"addr,omitempty" json:"addr"`
VendorID string `yaml:"vendor_id,omitempty" json:"vendor_id"`
DeviceID string `yaml:"device_id,omitempty" json:"device_id"`
Class string `yaml:"class,omitempty" json:"class"`
ClassLabel string `yaml:"class_label,omitempty" json:"class_label"`
Driver string `yaml:"driver,omitempty" json:"driver"`
IOMMUGroup int `yaml:"iommu_group,omitempty" json:"iommu_group"`
}
#VFIOPCIDevice is a single PCI function discovered under sysfs.
type VFIOReport ¶
type VFIOReport struct {
IOMMUEnabled bool `yaml:"iommu_enabled,omitempty" json:"iommu_enabled"`
IOMMUKind string `yaml:"iommu_kind,omitempty" json:"iommu_kind"`
GPUs []VFIOGpu `yaml:"gpus,omitempty" json:"gpus"`
}
#VFIOReport summarizes host readiness for VFIO GPU passthrough.
type ValidateProjectReply ¶
type ValidateProjectReply struct {
Project *ResolvedProject `yaml:"project,omitempty" json:"project,omitempty"`
Diagnostics Diagnostics `yaml:"diagnostics,omitempty" json:"diagnostics,omitempty"`
}
#ValidateProjectReply — the host-side project-validation result: the error-TOLERANT #ResolvedProject projection (partial; only boxes/candies that resolved) + the host-anchored diagnostics (per-box resolve failures + CUE-conformance + resource-vocab + build-tunable/merge). The plugin merges these with its own pure-rule + resolution-graph findings for the final verdict.
type ValidateProjectRequest ¶
type ValidateProjectRequest struct {
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
IncludeDisabled bool `yaml:"include_disabled,omitempty" json:"include_disabled,omitempty"`
}
#ValidateProjectRequest — which project dir to validate (empty = the host's cwd) + whether to include enabled:false boxes. Mirrors #ResolvedProjectRequest (the sibling resolved-project seam).
type ValidatingKindCarrier ¶
type ValidatingKindCarrier interface {
IsValidatingKind() bool
}
ValidatingKindCarrier is implemented by a provider (grpcProvider out-of-proc, inprocProvider compiled-in) that carries a class:kind capability's VALIDATES flag (F7/C8). true → the host dispatches OpValidate to the kind at load (a deep plugin-owned check returning Diagnostics, beyond the static CUE input-def gate); false (or not implemented) → only the static gate runs.
type ValidationError ¶ added in v0.2026210.808
type ValidationError struct {
Errors []string
}
ValidationError accumulates project/candy validation error messages (the loader validation accumulator, distinct from the richer Diagnostics — a plain []string collector). Relocated from loaderkit (resolve_opts.go) with UnifiedFile so the type-only loader-result consumers share ONE definition.
func (*ValidationError) Add ¶ added in v0.2026210.808
func (e *ValidationError) Add(format string, args ...any)
Add adds an error to the collection.
func (*ValidationError) Error ¶ added in v0.2026210.808
func (e *ValidationError) Error() string
func (*ValidationError) HasErrors ¶ added in v0.2026210.808
func (e *ValidationError) HasErrors() bool
HasErrors returns true if there are any errors.
type Venue ¶
type Venue int
const ( // VenueHostNative runs commands directly on the host (or as a RUN in // the Containerfile for the container target). The step's shell // commands execute natively; no isolation container. VenueHostNative Venue = iota // VenueContainerBuilder runs the step inside the existing multi-stage // builder image (fedora-builder / arch-builder / ...). On the // host target this means `podman run --user <host-uid> -v <paths> // <builder> bash -c "..."`. On the OCI target this is a FROM + RUN // pair that becomes its own build stage, with copy_artifacts pulling // outputs into the final image. VenueContainerBuilder // VenueSkip records the step with a reason but doesn't execute. Used // for container-runtime-only fields (ports:, volumes:, tunnel:, …) // when compiling for the host target, and for aur: on non-Arch hosts. VenueSkip )
type VenueDescriptor ¶
type VenueDescriptor struct {
Kind string `yaml:"kind,omitempty" json:"kind"`
User string `yaml:"user,omitempty" json:"user,omitempty"`
Host string `yaml:"host,omitempty" json:"host,omitempty"`
Port int `yaml:"port,omitempty" json:"port,omitempty"`
Args []string `yaml:"args,omitempty" json:"args,omitempty"`
ConnectTimeout int `yaml:"connect_timeout,omitempty" json:"connect_timeout,omitempty"`
// engine/container_name are set ONLY for kind "container": the container engine
// ("podman"/"docker", selecting JumpPodmanExec vs JumpDockerExec) and the target container
// name ContainerChain jumps into.
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
ContainerName string `yaml:"container_name,omitempty" json:"container_name,omitempty"`
}
#VenueDescriptor is the SELF-CONTAINED, serializable description of a deploy venue's executor that a substrate LIFECYCLE plugin's OpPrepareVenue / OpTeardownExecutor returns (F6). K1-unblock W3 Unit B added the "container" kind (engine/container_name) so a plugin-constructed deploykit.ContainerChain venue — a single-hop *NestedExecutor{Parent:ShellExecutor{}, Jump:{Kind:JumpPodmanExec|JumpDockerExec}}, the MOST COMMON check-runner venue — round-trips through kit.DescriptorFromExecutor/VenueFromDescriptor exactly like "shell"/"ssh" already do. This does NOT generalize to arbitrary N-hop composition (a genuinely multi-hop NestedExecutor still degrades to the zero descriptor, unchanged) — it closes the one enumerable, well-known single-hop shape ContainerChain always produces.
type VerbSpec ¶
type VerbSpec struct {
Contexts []ExecContext
DefaultDo DoMode
Reversible bool
}
VerbSpec is the per-verb metadata in VerbCatalog. Contexts[0] is the canonical default context.
func (VerbSpec) HasContext ¶
func (s VerbSpec) HasContext(ctx ExecContext) bool
HasContext reports whether the verb is legal in ctx.
type VerifyChecksRequest ¶ added in v0.2026213.1726
type VerifyChecksRequest struct {
Plan []Step `yaml:"plan,omitempty" json:"plan,omitempty"`
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
Box string `yaml:"box,omitempty" json:"box,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
VerifyOnly bool `yaml:"verify_only,omitempty" json:"verify_only,omitempty"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
Venue VenueDescriptor `yaml:"venue,omitempty" json:"venue,omitempty"`
}
#VerifyChecksRequest is the command:check OpVerifyChecks envelope (#55 CHECK-ENGINE cone, Unit 2): the host threads a live venue executor — flattened to #VenueDescriptor, since a live executor cannot cross the wire, and re-materialized PLUGIN-SIDE via kit.VenueFromDescriptor (the SAME mechanism candy/plugin-bundle's resolveRootExecutor uses) — over the in-proc reverse channel and asks the COMPILED-IN command:check to DRIVE a deploy-scope check pass PLUGIN-SIDE. This sheds charly core's checkrun.go + planrun_adapter.go sdk/kit imports (the in-proc kit.Runner construction moved plugin-side).
ONE drive shape now: plan → the `target: local` --verify path (candy/plugin-bundle's verify_local.go, #55 W3 B3 — a PEER plugin now, not core): a PLUGIN-ASSEMBLED plan (kind:local template, resolved via node_resolve.go's lookupLocalTemplate — no LoadUnified — + deploy node; the per-host overlay merge happens on THIS side) driven via kit.RunPlan (verify-only/context/ keyword gating). The plugin rebuilds the runtime env (USER/HOME/IMAGE/INSTANCE) + ${HOST:} host-vars + the cross-deployment TargetResolver from {dir, box, instance} — plugin-check ALREADY does this for check-live (verb_resolver.go / members.go), so those never cross the wire.
The former SECOND drive shape (ops/only_ids — the deploy-lifecycle Test path, charly core's unified_targets.go runUnifiedTargetChecks feeding raw deploy-scope Op checks via kit.Runner.Run, no plan gating) is GONE (#55 W3 B3 remainder): its own sole production caller, pluginDeployTarget.Test (UnifiedDeployTarget's Test method), had ZERO real callers anywhere in the tree — `charly check live` reaches candy/plugin-check directly (live_gather.go) and never touches this interface method; the ONE caller was a unit test. Test()/runUnifiedTargetChecks/ TestOpts (charly), verifyChecksRunOps/filterOpsByID (candy/plugin-check), and the dead "test" op in #DeployTargetDispatchRequest's enum are all deleted together. The box-mode context-skip regression coverage (TestLiveVerb_SkipsUnderBoxMode, charly/checkrun_charly_verbs_test.go) moved onto the surviving plan shape — RunOne (sdk/kit/planrun.go) is the SAME per-step primitive both kit.Runner.Run and kit.RunPlan dispatched through, so the context-vs-mode gate (opInContext) is identically exercised either way; no coverage was lost.
The reply is []#StepResult (CUE-sourced in this file) — CONSUMED, not modified. All plain fields (plan/venue are spec envelope types; StepResult.Result is #CheckResult by value) → gengotypes-faithful, no @go(-).
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 VmBuildReply ¶
type VmBuildReply struct {
SourceKind string `yaml:"source_kind,omitempty" json:"source_kind"`
VmJSON RawBody `yaml:"vm_json,omitempty" json:"vm_json"`
DistroJSON RawBody `yaml:"distro_json,omitempty" json:"distro_json,omitempty"`
BuilderJSON RawBody `yaml:"builder_json,omitempty" json:"builder_json,omitempty"`
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
Rootful bool `yaml:"rootful,omitempty" json:"rootful,omitempty"`
BootcImageRef string `yaml:"bootc_image_ref,omitempty" json:"bootc_image_ref,omitempty"`
BuilderImageRef string `yaml:"builder_image_ref,omitempty" json:"builder_image_ref,omitempty"`
OutputDir string `yaml:"output_dir,omitempty" json:"output_dir"`
VmStateDir string `yaml:"vm_state_dir,omitempty" json:"vm_state_dir"`
ExistingState *VmDeployState `yaml:"existing_state,omitempty" json:"existing_state,omitempty"`
Force bool `yaml:"force,omitempty" json:"force,omitempty"`
}
#VmBuildReply is the resolveVmBuild reply (P8b-rest — the former "vm-build" host-builder is DELETED; candy/plugin-vm/vm_build_resolve.go resolves it plugin-side): everything the plugin needs to run the disk-build engine without importing the loader. VmJSON is the resolved+validated kind:vm entity (the #Vm-shaped value resolveVmViaPlugin already produces — opaque bytes, the SAME convention #ConfigResolveReply.vm_json uses for a #Vm-shaped payload) so the plugin decodes it into its own spec.Vm rather than re-parsing uf.VM[entity] itself (which needs LoadUnified, a core Mechanism). DistroJSON/BuilderJSON carry the matched *DistroDef/*BuilderDef (bootstrap source only) — hand-written runtime types with no CUE def, so they ride as opaque RawBody too (the established idiom this file documents at the top). Engine/Rootful are the resolved runtime settings (ResolveRuntime) the engine needs to pick `podman` vs `sudo podman`. BootcImageRef/BuilderImageRef are PRE-RESOLVED (and, for the builder image, pre-built via `charly box build`) — both need the local podman-storage + project-config lookup a plugin cannot do (resolveBootcImageRef / ensureBuilderImageBuilt stay host-side). OutputDir/VmStateDir are the resolved per-entity paths (vmshared.VmDiskDir is ALREADY plugin-importable, but the host still resolves+creates VmStateDir since it also reads the existing ledger state below). ExistingState is the entity's persisted VmDeployState (#VmDeployState already has a CUE def — a typed embed, not opaque) so the plugin reuses the same instance-id / regenerates the seed ISO idempotently.
type VmBuildRequest ¶
type VmBuildRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Size string `yaml:"size,omitempty" json:"size,omitempty"`
RootSize string `yaml:"root_size,omitempty" json:"root_size,omitempty"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
Type string `yaml:"type,omitempty" json:"type,omitempty"`
Transport string `yaml:"transport,omitempty" json:"transport,omitempty"`
Console bool `yaml:"console,omitempty" json:"console,omitempty"`
Force bool `yaml:"force,omitempty" json:"force,omitempty"`
}
#VmBuildRequest carries the `charly vm build` command flags (the former VmBuildCmd fields). candy/plugin-vm/vm_build_resolve.go resolves the kind:vm entity + the build vocabulary + the per-source-kind image refs into a #VmBuildReply envelope PLUGIN-SIDE (the former "vm-build" host-builder is DELETED — PREP+RESOLVE moved into the plugin, P8b-rest); the plugin's `charly vm build` command then runs the actual privileged-container / qemu-img / bootc-install / cloud-init disk-build ENGINE itself, exactly as candy/plugin-build's podman DRIVE runs behind HostBuild("buildengine-prep") (P8b) — the same inversion, applied to the VM disk-build engine. force skips the cloud_image content-freshness check, forcing a base-disk rebuild even when unchanged (P8b-rest: `--force` predates command:vm's P10 externalization but was dropped from this seam then — restored here since BuildCloudImage's force parameter is load-bearing).
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 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 VmCloudInitNetwork ¶
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"`
// PortForwards maps a guest port (string) to its auto-allocated host port for
// each `auto:<guest>` network.port_forwards entry — persisted at vm-create so
// the allocation is reused across create→deploy-add (the sibling of SshPort).
PortForwards map[string]int `yaml:"port_forwards,omitempty" json:"port_forwards,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 VmDisplayEndpoint ¶
type VmDisplayEndpoint struct {
Kind string `yaml:"kind,omitempty" json:"kind,omitempty"`
IsSocket bool `yaml:"is_socket,omitempty" json:"is_socket,omitempty"`
SocketPath string `yaml:"socket_path,omitempty" json:"socket_path,omitempty"`
Host string `yaml:"host,omitempty" json:"host,omitempty"`
Port int `yaml:"port,omitempty" json:"port,omitempty"`
Password string `yaml:"password,omitempty" json:"password,omitempty"`
TunnelNeeded bool `yaml:"tunnel_needed,omitempty" json:"tunnel_needed,omitempty"`
}
#VmDisplayEndpoint describes how to reach one graphics channel (SPICE or VNC) of a running VM. Shared by candy/plugin-vm's own DisplayEndpoint (a running-VM's resolved endpoint, kind="spice"| "vnc") and charly-core's decode of the resolve-spice/resolve-vnc RPC reply — ONE shape, R3.
type VmKeyInjection ¶
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"`
// Each entry is "<host>:<guest>". The host side may be a fixed port OR the
// literal `auto` sentinel (matching the pod `port: [auto]` word) — `auto`
// auto-allocates a free host port at vm-create (persisted in vm_state,
// reused across the create→deploy-add sequence), the sibling of ssh.port_auto.
PortForwards []string `yaml:"port_forwards,omitempty" json:"port_forwards,omitempty"`
}
type VmPluginEnv ¶
type VmPluginEnv struct {
VmOp string `yaml:"vm_op,omitempty" json:"vm_op"`
VmName string `yaml:"vm_name,omitempty" json:"vm_name,omitempty"`
URI string `yaml:"uri,omitempty" json:"uri,omitempty"`
Force bool `yaml:"force,omitempty" json:"force,omitempty"`
DeleteDisk bool `yaml:"delete_disk,omitempty" json:"delete_disk,omitempty"`
Snap *VmSnapInternalReq `yaml:"snap,omitempty" json:"snap,omitempty"`
}
#VmPluginEnv is the host→plugin env for an internal VM-resolution RPC (domain-state/ list-domains/resolve-spice/resolve-vnc/snapshot-internal). Matches the SUBSET of candy/plugin-vm's own (richer) internal vmEnv decode struct that charly-core's dispatch actually sends — plugin-vm's vmEnv carries an ADDITIONAL `create` field (the `charly vm create` CLI's own in-process construction, never sent by charly-core), which stays a plugin-local concern outside this shared def (containing *VmSpec/VmRuntimeParams, neither CUE-sourced yet). (A former `state_dir` field served ONLY the "qemu-shutdown" op, whose sole sender — charly/vm_qemu_client.go — was deleted in FLOOR-SLIM-proper Unit-8B alongside the arbiter's direct-QEMU stop path; the field was removed in the same cutover, confirmed zero remaining setters via `git grep 'VmPluginEnv{'` across charly/candy/sdk.)
type VmResolveInput ¶
type VmResolveInput struct {
Vm RawBody `yaml:"vm,omitempty" json:"vm"`
}
#VmResolveInput carries one opaque vm template body to project (Cutover L).
type VmResolveReply ¶
type VmResolveReply struct {
Resolved *ResolvedVm `yaml:"resolved,omitempty" json:"resolved,omitempty"`
}
#VmResolveReply wraps the resolved vm value envelope (#ResolvedVm lives in vm.cue).
type VmResolveResult ¶
type VmResolveResult struct {
Endpoint VmDisplayEndpoint `yaml:"endpoint,omitempty" json:"endpoint,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
TunnelTarget string `yaml:"tunnel_target,omitempty" json:"tunnel_target,omitempty"`
}
#VmResolveResult decodes a resolve-spice/resolve-vnc reply.
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 VmSnapInternalReq ¶
type VmSnapInternalReq struct {
SnapOp string `yaml:"snap_op,omitempty" json:"snap_op"`
VmName string `yaml:"vm_name,omitempty" json:"vm_name"`
Opts *VmSnapshotCreateOpts `yaml:"opts,omitempty" json:"opts,omitempty"`
Entry *VmSnapshotEntry `yaml:"entry,omitempty" json:"entry,omitempty"`
OutPath string `yaml:"out_path,omitempty" json:"out_path,omitempty"`
}
#VmSnapInternalReq is the snapshot-internal op payload (create/delete/revert/promote/ create-external/delete-external/revert-external), threaded via #VmPluginEnv.snap.
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 VmSnapshotCreateOpts ¶
type VmSnapshotCreateOpts struct {
VmName string `yaml:"vm_name,omitempty" json:"vm_name"`
SnapName string `yaml:"snap_name,omitempty" json:"snap_name"`
Mode string `yaml:"mode,omitempty" json:"mode,omitempty"`
Description string `yaml:"description,omitempty" json:"description,omitempty"`
Quiesce bool `yaml:"quiesce,omitempty" json:"quiesce,omitempty"`
LibvirtBackend string `yaml:"libvirt_backend,omitempty" json:"libvirt_backend,omitempty"`
}
#VmSnapshotCreateOpts parameterizes the creation of a snapshot — the host-resolved payload for the snapshot-internal "create"/"create-external" ops.
type VmSnapshotEntry ¶
type VmSnapshotEntry struct {
Name string `yaml:"name,omitempty" json:"name"`
Mode string `yaml:"mode,omitempty" 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,omitempty" json:"refcount"`
Quiesced bool `yaml:"quiesced,omitempty" json:"quiesced,omitempty"`
}
#VmSnapshotEntry is one snapshot record (mirrors sdk/vmshared's SnapshotEntry, the on-disk registry shape) — carried on delete/revert/promote ops so the plugin's go-libvirt call has the full record without re-reading the host-side registry itself.
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 VolumeMount ¶
type VolumeMount struct {
VolumeName string `yaml:"volume_name,omitempty" json:"volume_name,omitempty"`
ContainerPath string `yaml:"container_path,omitempty" json:"container_path,omitempty"`
}
#VolumeMount — a resolved named-volume mount (charly-<deploy>-<name> → container path). In-memory only (never marshaled) → tags wire-irrelevant. deploykit.VolumeMount aliases this.
type WalkSeams ¶
type WalkSeams struct {
// Parser is the per-document parse (the host passes the registered DocParser).
Parser DocParser
// Boundary runs at each PROJECT boundary (the root file AND each namespace root) BEFORE that
// boundary's documents parse: the host does the parse pre-scan + connect-declared-kind-plugins
// side effects (registry mutation). data = the boundary file bytes.
Boundary func(dir string, data []byte) error
// Threaded returns the current registry-derived kind-recognition snapshot. Called fresh per
// document parse (the host's loaderThreaded()).
Threaded func() Threaded
// ResolveRef resolves an import ref (local path OR remote "@host/org/repo[/sub]:ver") to a
// stable cache KEY + a concrete on-disk PATH. The host owns remote fetch + cache + auto-migration.
ResolveRef func(ref, baseDir string) (key, path string, err error)
// GateDoc runs the host #NodeDoc CUE validate-before-execute gate on one raw document's bytes.
GateDoc func(label string, raw []byte) error
// RepoIdentity returns the canonical repo identity of an import ref (for the cycle-break), or ""
// (the host's nsRepoIdentity). Empty → version-keyed fallback.
RepoIdentity func(ref, baseDir string) string
}
WalkSeams is the set of host-supplied callbacks the whole-project WALK needs for everything registry-coupled or host-coupled — the host builds this value and hands it to the registered ProjectWalker; the walk mechanism (sdk/loaderkit.Walk) calls each seam and never does the coupled work itself (boundary law clause D: the walk consults host-threaded DATA/mechanisms, never the provider registry directly).
Source Files
¶
- arbiter_consts.go
- box_name.go
- build_resolved_box.go
- builder_config_methods.go
- builder_preresolved.go
- builder_resolve.go
- buildwire_render.go
- bundle_config.go
- bundle_keys.go
- cache_mount_render.go
- calver.go
- calver_parse.go
- candy_capabilities.go
- candy_reader.go
- candy_ref_entry.go
- charly_methods.go
- charly_names.go
- checkcontext.go
- checklevel.go
- cloud_init_runtime_params.go
- config.go
- container_name.go
- cue_types_gen.go
- deploy_bundle_ops.go
- deploy_consts.go
- deploy_executor.go
- deploy_key.go
- deploy_methods.go
- deploy_tree_validate.go
- deployconfig.go
- descent.go
- distro_config_methods.go
- distro_methods.go
- enc_consts.go
- env_pairs_coneb.go
- env_path.go
- externalized_builders.go
- gpu_consts.go
- gpu_methods.go
- gpu_select.go
- hand_state_types.go
- host_context.go
- http_do.go
- image_errors.go
- init_config_methods.go
- injection_seams.go
- install_plan.go
- install_plan_view.go
- install_step.go
- install_step_view.go
- install_step_vocab.go
- ir_enums.go
- label_consts.go
- labelset_methods.go
- layout_paths.go
- ledger_records.go
- libvirt_uri.go
- load_directives.go
- loader_discover.go
- loader_seam.go
- loadmodel.go
- matchers.go
- materialize_project_seams.go
- mcp_provides.go
- merge_plugin_kinds.go
- merge_unified.go
- namespace_ref.go
- node_helpers.go
- op_input.go
- phase_template.go
- poll.go
- port_mapping.go
- preview.go
- provider_carriers.go
- quadlet_names.go
- quadlet_paths.go
- readiness.go
- readiness_seam.go
- ref_parse.go
- remote_download.go
- render_seam.go
- repo_identity.go
- resolve_opts.go
- resolved_box.go
- scalar_aliases.go
- scan_seams.go
- service_render_context.go
- shellquote.go
- sidecar_naming.go
- ssh_target.go
- status_result.go
- status_types.go
- substrate_template_methods.go
- tunnel_methods.go
- tunnel_resolve.go
- union_types.go
- verb_context.go
- version_gen.go
- vm_domain.go
- vocab_gen.go
- vocab_project.go
- wrap_container.go
- yaml_version_line.go