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 BoxNamesOf(m BoxMap) []string
- func CurrentUsername() string
- func DecodePluginKindMap[T any](uf *UnifiedFile, kind string) map[string]*T
- func EncodeBox(b BoxConfig) json.RawMessage
- func EncodeInlineCandy(il *InlineCandy) json.RawMessage
- func FinalizeCandyRefs(m *CandyModel, v *CandyView, refs CandyRefs)
- func IsRemoteCandyRefString(ref string) bool
- func IsRemoteImageRef(ref string) bool
- func LeafName(ref string) string
- func MappingRoot(n *yaml.Node) *yaml.Node
- func NormalizePCIVendor(s string) string
- func PackageNames(items []PackageItem) []string
- func PollUntil(ctx context.Context, cfg PollConfig, cond PollCondition) error
- func ResolvePluginKindViaPlugin[T any](uf *UnifiedFile, kind string, resolve func(json.RawMessage) (*T, error)) map[string]*T
- func ScalarNode(v string) *yaml.Node
- func ShellQuote(s string) string
- func SplitNamespaceRef(ref string) (ns, rest string, ok bool)
- func SplitRepoAndSubPath(ref string) (repoPath, subPath, name string)
- func StatusFromState(state string) string
- func StripCandyRefVersion(ref string) (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 VmDomainIdentity(deployName 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 AndroidEntityResolution
- type AndroidResolveInput
- type AndroidResolveReply
- type AndroidSpec
- type ApkPackageSpec
- type ArbiterBracketAcquireReply
- type ArbiterBracketAcquireRequest
- type ArbiterBracketReleaseReply
- type ArbiterBracketReleaseRequest
- type ArbiterInvokeInput
- type ArbiterInvokeReply
- type ArtifactView
- type BakePluginsReply
- type BakePluginsRequest
- 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 BuildStageContext
- type BuildTarget
- type BuildType
- type Builder
- type BuilderCollectInput
- type BuilderCollectReply
- type BuilderDef
- type BuilderMap
- type BuilderResolveInput
- type BuilderResolveReply
- type BuilderReverseInput
- type BuilderReverseReply
- type BuilderRunOpts
- type BundleNode
- type CLIArg
- type CLILeaf
- type CLIModel
- type CacheMount
- 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 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 CheckBedMember
- type CheckBedPrereqSkip
- type CheckBedReply
- type CheckBedRequest
- type CheckEnv
- type CheckLoadPluginsReply
- type CheckLoadPluginsRequest
- type CheckResult
- type CheckRunRequest
- type CheckRunResults
- type CheckVenueResolveReply
- type CheckVenueResolveRequest
- type CliReply
- type CliRequest
- 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 ConfigPersistReply
- type ConfigPersistRequest
- type ConfigResolveReply
- type ConfigResolveRequest
- type ConstructStepReply
- type ConstructStepRequest
- type Context
- type Copy
- type CredentialHealth
- type CredentialMount
- 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 DeployAddReply
- type DeployAddRequest
- type DeployArtifactsRetrieveReply
- type DeployArtifactsRetrieveRequest
- type DeployCandySecretsReply
- type DeployCandySecretsRequest
- type DeployCompileReply
- type DeployCompileRequest
- type DeployConfigSaveReply
- type DeployConfigSaveRequest
- type DeployConfigSaveStateReply
- type DeployConfigSaveStateRequest
- type DeployDelReply
- type DeployDelRequest
- type DeployDelResolveReply
- type DeployDelResolveRequest
- type DeployEntityResolveReply
- type DeployEntityResolveRequest
- type DeployExecutor
- type DeployExpose
- type DeployFromBoxReply
- type DeployFromBoxRequest
- type DeployMembersReply
- type DeployMembersRequest
- type DeployNodeDelDispatchReply
- type DeployNodeDelDispatchRequest
- type DeployOverlayReply
- type DeployOverlayRequest
- type DeployPluginsConnectReply
- type DeployPluginsConnectRequest
- type DeployProbes
- 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 DeployTargetTestOpts
- type DeployTraits
- type DeployTraitsCarrier
- type DeployVenue
- type DeployVolume
- type DeploymentStatus
- type DescentDescriptor
- type DetectedDevices
- type DevicePatternsReply
- type DevicePatternsRequest
- type Diagnostic
- type Diagnostics
- type DiscoverConfig
- type DiscoveredManifest
- type Distro
- 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 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 ExtractYAML
- type FeatureEntity
- type FeatureReply
- type FeatureRequest
- 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 HostEnv
- type HostProbeDevice
- type HostProbeDistro
- type HostProbeReply
- type HostProbeRequest
- type ImageUserInput
- type ImportEntry
- type ImportList
- type Incident
- type Init
- type InitDef
- type InitResolveInput
- type InitResolveReply
- type InitResolveRequest
- type InitServiceSchema
- 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 LibvirtUSB
- type LibvirtVCPUPin
- type LibvirtVendorID
- type LibvirtVideo
- type LibvirtVsock
- type LibvirtWatchdog
- type LifecycleOpts
- type LifecyclePrepareInput
- type LoadedDoc
- type LoadedProject
- type LoaderWalkRequest
- type Local
- type LocalPkg
- type LocalResolveInput
- type LocalResolveReply
- type LocalSpec
- type MCPProvideEntry
- type MCPServerYAML
- type MatchOpMap
- type Matcher
- type MatcherList
- type MaterializeSeams
- type MaterializedProject
- type Materializer
- type MergeConfig
- type MergeReply
- type MergeRequest
- type NamespaceMount
- type Op
- type OverlayBuildReply
- type OverlayBuildRequest
- type PackageItem
- type PackageSection
- type Pacstrap
- type PacstrapRepo
- type ParsedNode
- type ParsedProject
- type ParsedRef
- type Phase
- type PhaseCarrier
- type PhaseSet
- type PhaseTemplates
- type Plugin
- type PluginCapability
- type Pod
- type PodAttachOpts
- type PodCmdOpts
- type PodCmdReply
- type PodCmdRequest
- type PodConfigBoxEngineReply
- type PodConfigBoxEngineRequest
- type PodConfigCleanDeployEntryReply
- type PodConfigCleanDeployEntryRequest
- type PodConfigContainerTunnelReply
- type PodConfigContainerTunnelRequest
- type PodConfigDetectDevicesReply
- type PodConfigDetectDevicesRequest
- type PodConfigEnsureImageReply
- type PodConfigEnsureImageRequest
- type PodConfigListSidecarsReply
- type PodConfigLoadBundleReply
- type PodConfigLoadDeployReply
- type PodConfigLoadDeployRequest
- type PodConfigMigrateSecretsReply
- type PodConfigMigrateSecretsRequest
- type PodConfigProjectVolumeReply
- type PodConfigProjectVolumeRequest
- type PodConfigRemoveReply
- type PodConfigRemoveRequest
- type PodConfigResolveRefReply
- type PodConfigResolveRefRequest
- type PodConfigSSHKeyReply
- type PodConfigSSHKeyRequest
- 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 PodLiveStdioPlan
- type PodLogsOpts
- type PodLogsReply
- type PodLogsRequest
- type PodRemoveReply
- type PodRemoveRequest
- type PodResolveInput
- type PodResolveReply
- type PodServiceReply
- type PodServiceRequest
- type PodShellOpts
- type PodShellReply
- type PodShellRequest
- type PodSidecar
- type PodSpec
- type PodStartOpts
- type PodStartReply
- type PodStartRequest
- type PodStopOpts
- type PodStopReply
- type PodStopRequest
- type PodUpdateReply
- type PodUpdateRequest
- 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 ProjectTemplates
- type ProjectWalker
- type ProvidesConfig
- type RCARecord
- type RawBody
- type RawProject
- type RawProjectRequest
- type Readiness
- type ReadinessConfig
- type RecoveryDecision
- type RecoveryParams
- type RefsDownloader
- type RemoteImageResolveReply
- type RemoteImageResolveRequest
- type RenderSeamReply
- type RenderSeamRequest
- type RenderServiceReply
- type RenderServiceRequest
- type RenderedService
- type RepoBlock
- 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 ScanSpec
- type ScannedCandy
- type Scope
- type ScoreSummary
- type SecretYAML
- type Security
- type SecurityConfig
- type ServiceEntry
- type ServiceRenderContext
- type ServiceRenderInput
- type ServiceRenderReply
- type Shell
- type ShellEntry
- 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 StepScore
- type StrMap
- type StrVal
- type StructuralKindCarrier
- type StructuralKindLoadEnv
- type SubstrateKind
- type SubstrateStatusReply
- type SubstrateStatusRequest
- type SubstrateTemplateResolveRequest
- 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 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 ( 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 ( 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 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 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 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",
"device_descriptions",
"device_patterns",
"discover",
"distro_family_map",
"distro_package_managers",
"gpu_vendors",
"import",
"install_hints",
"ovmf_distro_aliases",
"ovmf_paths",
"pci_class_labels",
"providers",
"provides",
"repo",
"version",
}
DocDirectives is the reserved document directives (#NodeDoc top-level keys).
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 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 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 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 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 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 BoxNamesOf ¶
BoxNamesOf returns the image names in a generic image map, sorted for determinism.
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 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 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 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 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 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 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 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 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 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 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 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 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 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 kernel-floor charly files (host_build_check_bed.go) derive the domain identity without a vmshared import. stdlib-only (strings).
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.
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 AndroidEntityResolution ¶
type AndroidEntityResolution struct {
SpecJSON RawBody `yaml:"spec,omitempty" json:"spec,omitempty"`
}
#AndroidEntityResolution is the kind="android" payload carried OPAQUELY inside #DeployEntityResolveReply.entity (unit 6a): the resolved kind:android #ResolvedAndroid spec (CUE-sourced at schema/substrate_template.cue, SDD conversion — carried OPAQUELY here anyway, see the #DeployEntityResolveRequest doc below for why). The google-play credentials are NO LONGER threaded through this seam (deploy-cone cutover 1): candy/plugin-adb resolves them itself via a direct peer InvokeProvider(verb:credential) call — the same peer-to-peer pattern candy/plugin-vm already uses for verb:arbiter/verb:gpu/verb:egress — instead of the host pre-resolving them behind "deploy-entity-resolve". The former "credential STORE touch is core-only" justification was stale: InvokeProvider reaches ANY verb from ANY plugin.
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 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"`
}
#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 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 BakePluginsReply ¶
type BakePluginsReply struct {
Fragment string `yaml:"fragment,omitempty" json:"fragment,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
#BakePluginsReply carries the rendered Containerfile fragment (COPY + chmod lines for each bake_plugin binary) + the reply-error convention.
type BakePluginsRequest ¶
type BakePluginsRequest struct {
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
BoxName string `yaml:"box_name,omitempty" json:"box_name"`
CandyOrder []string `yaml:"candy_order,omitempty" json:"candy_order,omitempty"`
}
#BakePluginsRequest carries the inputs the host-side bake-plugins seam needs: the project dir (to load the live *Candy graph for SourceDir + buildPluginBinary), the box name (for the staging dir), and the candy order (the composition being baked). The host builds + stages each bake_plugin binary + returns the COPY/chmod fragment (#67).
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"`
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 build-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 HostBuild("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 build-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/build-prep.
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 BuilderMap ¶ added in v0.2026210.1533
#BuilderMap — a map of build type (pixi/npm/cargo/aur) → builder image name.
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 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.
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 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 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 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"`
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-runs AggregateCandyCapabilities over a box's candy order (a boolean OR of
// preserve_user, order-independent) + the PreserveUser rule (validateInitDependencies +
// validatePackagedServices box-arms). 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 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"`
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 deploy-add just built + persisted (resolved via
// resolveDeployResolvedImage, the overlay-plans ctx-seed fix's companion bug), 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 — the setup op returns the BedDescriptor (the node-derived shape the kind-blind plugin drives the sequence from — the substrate analogue of OpPrepareVenue's VenueDescriptor). All other ops return {} (errors ride the host-builder error return). PrereqSkip set ⇒ the bed is a clean SKIP (exit 3): the plugin writes the prereq-skip summary + returns CheckSkippedError, running NO other op (not even teardown — setup acquired nothing on the skip path).
type CheckBedRequest ¶
type CheckBedRequest struct {
Op string `yaml:"op,omitempty" json:"op"`
Bed string `yaml:"bed,omitempty" json:"bed"`
OK bool `yaml:"ok,omitempty" json:"ok,omitempty"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
}
#CheckBedRequest — the transitional check-bed host-session seam (P12 Wave-2, K5-mortal). A compiled-in plugin-check drives the R10 bed sequence over HostBuild("cli"), but the lock/lease/env lifecycle + the node-derived bed shape are core state a separate module cannot hold: this op-discriminated envelope opens/drives/closes a host-side session keyed by Bed. Class-generic action noun "check-bed" (F11 — never a substrate/provider word). The setup/teardown pair are two of its ops; members-up/members-down/wait-ready are the mid-sequence host-coupled helpers (they run AFTER the substrate deploys, so cannot fold into setup, and call saveDeployState+libvirt+SSHExecutor/podman polls with no `charly` verb, so cannot be cli-reentry). DIES at K5 (post-loaderkit the plugin self-orchestrates its own flock via statekit, computes the repo-override itself, and calls the arbiter over InvokeProvider).
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 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 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 forwards `vm build` to HostBuild("vm-build"). 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 NOT a CUE wire type: it is kit.CheckRunReply (sdk/kit/checkrun_seam.go), which carries []kit.StepResult verbatim 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. So StepResult's JSON output still rides kit's Go marshal (embedding flattens transparently), but the exception the wire mandate's spike-proven path authorizes is now narrowed to EXACTLY the one field that forced it, not the whole type.
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 HostBuild("resolved-project"), so this seam carries only name+instance. Class-generic action noun (F11).
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 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 sdk/buildkit's distroBuilderMap and from several charly consumers (host_build_feature.go, resolved_project_host.go, refs.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 ConfigPersistReply ¶
type ConfigPersistReply struct {
}
#ConfigPersistReply is the config-persist host-builder reply — empty; the host signals failure via its error return (surfaced to the plugin over the channel).
type ConfigPersistRequest ¶
type ConfigPersistRequest struct {
Key string `yaml:"key,omitempty" json:"key"`
Entity string `yaml:"entity,omitempty" json:"entity,omitempty"`
VmState *VmDeployState `yaml:"vm_state,omitempty" json:"vm_state,omitempty"`
Remove bool `yaml:"remove,omitempty" json:"remove,omitempty"`
}
#ConfigPersistRequest is the WRITE twin of config-resolve: a command plugin asks the host to persist (or remove) an entity's deploy-ledger entry. The host owns the ledger + its blocking acquireDeployConfigLock (a core Mechanism — the plugin is a separate module and MUST NOT hold a process-shared file lock across the boundary), so the plugin resolves its intent into this envelope and the host applies it under the lock. Key is the full deploy key ("vm:<name>"); Remove deletes the entry (destroy), else Entity + VmState are saved (create persist-auto-port). The action noun "config-persist" is class-generic (F11).
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"`
Claimant string `yaml:"claimant,omitempty" json:"claimant,omitempty"`
ClaimantNode *Deploy `yaml:"claimant_node,omitempty" json:"claimant_node,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. Claimant + ClaimantNode carry the exclusive-resource claimant (lookupVMClaimant) the handler acquires a preempt lease for. 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 "deploy-entity-resolve" call). VmState is the entity's persisted deploy-ledger runtime state (instance-id, ssh_port, disk path) — the READ half of the ledger dep (loadDeployConfigForRead → LookupKey "vm:<entity>") 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 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 DeployAddReply ¶
type DeployAddReply struct {
}
#DeployAddReply is the "deploy-add" host-builder reply — empty; the add prints its own progress + dry-run output to the shared stdio (the compiled-in plugin's HostBuild runs in charly's own process) and signals failure via the error return.
type DeployAddRequest ¶
type DeployAddRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
Ref string `yaml:"ref,omitempty" json:"ref,omitempty"`
AddCandy []string `yaml:"add_candy,omitempty" json:"add_candy,omitempty"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
DryRun bool `yaml:"dry_run,omitempty" json:"dry_run,omitempty"`
NodeOnly bool `yaml:"node_only,omitempty" json:"node_only,omitempty"`
Format string `yaml:"format,omitempty" json:"format,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"`
BuilderImage string `yaml:"builder_image,omitempty" json:"builder_image,omitempty"`
AssumeYes bool `yaml:"assume_yes,omitempty" json:"assume_yes,omitempty"`
Disposable bool `yaml:"disposable,omitempty" json:"disposable,omitempty"`
Lifecycle string `yaml:"lifecycle,omitempty" json:"lifecycle,omitempty"`
}
#DeployAddRequest carries the `charly bundle add` command flags (the former BundleAddCmd's authored fields). The command:bundle plugin (P13) owns the CLI GRAMMAR but cannot drive the deploy KERNEL — the loader, the InstallPlan compiler, ResolveTarget → externalDeployTarget, and the live-executor composition (which threads host objects that cannot cross the process boundary) are core Mechanisms. So the plugin's `charly bundle add` command is THIN — it forwards these flags to HostBuild("deploy-add"), and the host runs the existing add orchestration VERBATIM (Run → dispatchNode → compile → ResolveTarget → Add), exactly as the box-build engine stayed core behind HostBuild("image") in P8 and the VM-disk engine behind HostBuild("vm-build") in P10. The two per-node internal fields (vmEntity, builderImageOverride) are NOT carried — the host derives them during dispatch.
type DeployArtifactsRetrieveReply ¶
type DeployArtifactsRetrieveReply struct {
}
type DeployArtifactsRetrieveRequest ¶
type DeployArtifactsRetrieveRequest struct {
Dir string `yaml:"dir,omitempty" json:"dir"`
PlansJSON RawBody `yaml:"plans_json,omitempty" json:"plans_json"`
ArtifactKey string `yaml:"artifact_key,omitempty" json:"artifact_key"`
DeployName string `yaml:"deploy_name,omitempty" json:"deploy_name"`
ArtifactEnv map[string]string `yaml:"artifact_env,omitempty" json:"artifact_env,omitempty"`
VenueJSON RawBody `yaml:"venue_json,omitempty" json:"venue_json,omitempty"`
}
#DeployArtifactsRetrieveRequest/#DeployArtifactsRetrieveReply — the "deploy-artifacts-retrieve" HostBuild seam (Cone A shape 3): the genuine floor-M half of the former core-resident retrieveArtifactsAndK3s — re-scanning the project for the deploy's candies (same ScanAllCandyWithConfig coupling as the secrets seam above) and pulling back each one's declared `artifacts:` via deploykit.RetrieveCandyArtifacts over the deploy's OWN venue executor (re-materialized from venue_json — the SAME kit.VenueFromDescriptor conversion every other venue-consuming seam uses). Runs AFTER the substrate dispatch succeeds (the venue must already exist). The register-hint-driven k3s-post-provision DISPATCH itself is NOT here — that decision + the verb:kube InvokeProvider call happen plugin-side in handleDeployApply, using the register_hints the sibling #DeployCandySecretsReply already returned (one candy scan feeds both).
type DeployCandySecretsReply ¶
type DeployCandySecretsRequest ¶
type DeployCandySecretsRequest struct {
Dir string `yaml:"dir,omitempty" json:"dir"`
PlansJSON RawBody `yaml:"plans_json,omitempty" json:"plans_json"`
}
#DeployCandySecretsRequest/#DeployCandySecretsReply — the "deploy-candy-secrets" HostBuild seam (Cone A shape 3): the genuine floor-M half of the former core-resident prepareCandySecrets — scanning the project for the candies backing a compiled plan set (ScanAllCandyWithConfig, a K1/K4 loader-migration-inventory mechanism a plugin cannot run itself) and resolving their secret_requires:/secret_accepts: env against the credential store (itself already a core→plugin adapter to verb:credential). candy/plugin-bundle's handleDeployApply calls this ONCE, BEFORE the substrate dispatch, then injects the returned secret_env into its OWN in-proc plans via the already-portable deploykit.InjectSecretsIntoPlans — no plan mutation crosses the wire. register_hints is the set of distinct candy Artifact().Register values present in the resolved candy set (e.g. "kubeconfig") — computed here (same candy scan) so handleDeployApply can decide, AFTER the substrate dispatch + artifact retrieval, which handler (if any) to InvokeProvider — data-driven, never a per-candy-name special case.
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 HostBuild("resolved-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 HostBuild("resolved-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 HostBuild("resolved-project") (the established seam — 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 deploykit.HostContext (MachineVenue/Distro/Glibc/ BuilderImage + the preresolved BuilderContext map) — a hand-written sdk/deploykit type with no CUE def, so it rides as an opaque RawBody envelope (the VmJSON/PodConfigJSON idiom; the plugin unmarshals into deploykit.HostContext, which it imports via github.com/opencharly/sdk/deploykit) — ALWAYS host-computed for every shape (detectHostContext's MachineVenue probe + preresolveActiveInitInto's LoadUnified-coupled init lookup; only the box/candy SELECTION moved). Tag is the image CalVer pin (for the plan Version field when set). Dir is the project dir the plugin threads into its HostBuild("resolved-project") call (empty → plugin cwd).
type DeployConfigSaveReply ¶
type DeployConfigSaveReply struct {
}
#DeployConfigSaveReply is the "deploy-config-save" host-builder reply — empty (failure surfaces via the RPC error itself).
type DeployConfigSaveRequest ¶
type DeployConfigSaveRequest struct {
ConfigJSON RawBody `yaml:"config,omitempty" json:"config"`
}
#DeployConfigSaveRequest is the K4-C narrow seam for saveBundleConfigNodeForm — the `charly bundle import`/`reset` deploy-state WRITE step. command:bundle's show/export/status leaves moved to the plugin outright (deploykit.LoadBundleConfig/ExportAllBox/ParseDeployKey etc. are already sdk-portable, and export's project-load touch reuses the existing HostBuild("resolved-project") seam) — only the SAVE step still needs a seam: its per-entry marshal callback (marshalBundleNode, deploy_nodeform.go) resugars each plan step's internal plugin/plugin_input pair back to the authored `<word>: <input>` sugar via the host-owned pluginPrimaries registry (populated at compiled-in plugin init() + the byte-gated external prescan) — a live, in-process registry a separate-module plugin cannot reach directly. Config carries the marshalled deploykit.BundleConfig as an opaque RawBody envelope (a hand-written sdk/deploykit type with no CUE def, matching the DeployCompileRequest HostContextJSON idiom).
type DeployConfigSaveStateReply ¶
type DeployConfigSaveStateReply struct {
}
type DeployConfigSaveStateRequest ¶
type DeployConfigSaveStateRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
InputJSON RawBody `yaml:"input_json,omitempty" json:"input_json"`
}
#DeployConfigSaveStateRequest / Reply: deploykit.SaveDeployState(box,instance,input, marshalDeployNode) — the terminal per-deploy persist. input_json is the marshalled deploykit.SaveDeployStateInput (a hand-written sdk/deploykit type with no CUE def — the RawBody idiom, matching #PodConfigWriteRequest.pod_config_json). Renamed substrate-neutral (S3b, Q2): the seam started pod-only (candy/plugin-deploy-pod's config_setup.go/resolve.go) but candy/plugin-bundle's generic Add/Update apply body (deploy_target.go's persistDeployState) is now a THIRD caller across every substrate (pod/vm/local/k8s/android), so the "pod-config-*" family naming no longer fit — the underlying "deploy-config-save-state" host-builder kind renamed to match (was "pod-config-save-deploy-state").
type DeployDelReply ¶
type DeployDelReply struct {
}
#DeployDelReply is the "deploy-del" host-builder reply — empty (prints host-side, errors via the return).
type DeployDelRequest ¶
type DeployDelRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
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"`
}
#DeployDelRequest carries the `charly bundle del` command flags. The plugin's `charly bundle del` forwards these to HostBuild("deploy-del"); the host runs the existing del orchestration VERBATIM (resolveDelNode → ResolveTarget → Del, replaying the recorded ReverseOps). The live ReverseRunner is NOT carried — a programmatic teardown that needs a specific runner (the vm guest-SSH reverse runner) is a host-side path, resolved during dispatch, never authored on the CLI.
type DeployDelResolveReply ¶
type DeployDelResolveRequest ¶
type DeployDelResolveRequest struct {
Name string `yaml:"name,omitempty" json:"name"`
}
#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 DeployEntityResolveRequest ¶
type DeployEntityResolveRequest struct {
Kind string `yaml:"kind,omitempty" json:"kind"`
Name string `yaml:"name,omitempty" json:"name"`
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
}
#DeployEntityResolveRequest/#DeployEntityResolveReply — the F6-family GENERIC host-side entity-lookup seam (unit 6a, extended for unit 6b's k3s_post consumer, candy/plugin-vm's own vmConfiguredBackendPlugin (F6 vm-lifecycle move, coneB-vmlifecycle — formerly charly-core's vm_backend_lifecycle.go's vmConfiguredBackend, now plugin-side), and for W4's resolveNodeTemplate — candy/plugin-bundle's kind:local template lookup): a substrate PRERESOLVE body (k8s/vm/android, F6) OR a peer consumer resolving a cross-reference (k3s_post's deployVMForwards, vmConfiguredBackendPlugin, resolveNodeTemplate's kind:local merge) needs a LoadUnified-coupled lookup a plugin cannot do itself — EITHER (a) its own deploy-tree node by name (the Update-path re-resolve every preresolver does when node==nil, OR a bundle-key cross-reference's From-field hop — today: resolveTreeRoot) or (b) a referenced kind:<word> entity (k8s/android/vm/local) by name, returned as the WHOLE RESOLVED envelope so a caller just reads its fields (Backend, Network.PortForwards, Candy, …) without tracing the resolver's own portability (today: findK8sSpec / findAndroidSpec / a direct uf.VM[name] lookup + resolveVmViaPlugin / findLocalSpec). ONE discriminated request replaces per-purpose kinds: `kind` is DATA the host body dispatches on internally (clause-D) — never a compiled-in per-KIND HostBuild registration, so a new consumer needs no new wire shape, only a new `case` in the host handler (or reuse of an existing one — "bundle" and "deploy" share ONE case, both a deploy-tree node lookup by name). `entity` carries the kind-specific result OPAQUELY — ResolvedK8s/ ResolvedAndroid/the vm entity (ResolvedVm)/ResolvedLocal are ALL CUE-sourced (schema/substrate_template.cue, schema/vm.cue; SDD conversion), but this seam still carries them as opaque bytes rather than a typed field, because `kind` is DATA the host dispatches on internally (clause-D) and the caller already knows which kind it asked for and decodes accordingly — mirroring the DeployCompileReply / DeployConfigSaveRequest RawBody idiom used throughout this file for the same reason. The "local" case's EMPTY reply (no EntityJSON, no error) is itself meaningful — "no kind:local template by that name" — distinct from a genuine host-side load-failure error; the caller (resolveNodeTemplate) tells the two apart.
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 DeployMembersReply ¶
type DeployMembersReply struct {
}
type DeployMembersRequest ¶
type DeployMembersRequest struct {
Node *Deploy `yaml:"node,omitempty" json:"node,omitempty"`
}
#DeployMembersRequest/#DeployMembersReply — bring up / tear down a deployment's sibling members (bringUpMembers/tearDownMembers — providerRegistry + ledger + subprocess-dependent, stays host-side), reached once at the end of Run() / the start of `charly bundle del`.
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 prior "deploy-del" host-builder's tail, unchanged; 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 resolveTreeRoot 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 resolveTreeRoot 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 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"`
}
#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 DeployTargetTestOpts ¶
type DeployTargetTestOpts struct {
OnlyIDs []string `yaml:"only_ids,omitempty" json:"only_ids,omitempty"`
FormatJSON bool `yaml:"format_json,omitempty" json:"format_json,omitempty"`
StopOnFail bool `yaml:"stop_on_fail,omitempty" json:"stop_on_fail,omitempty"`
}
#DeployTargetTestOpts mirrors the former charly-core TestOpts (`charly check live`).
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.
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 DevicePatternsReply ¶
type DevicePatternsReply struct {
DevicePatterns []string `yaml:"device_patterns,omitempty" json:"device_patterns,omitempty"`
GpuVendors map[string]string `yaml:"gpu_vendors,omitempty" json:"gpu_vendors,omitempty"`
}
#DevicePatternsReply carries the two embedded tables verbatim (see charly/devices.go).
type DevicePatternsRequest ¶
type DevicePatternsRequest struct {
}
#DevicePatternsRequest is empty — the embedded device_patterns/gpu_vendors directives are baked into charly-core's binary (the embedded default charly.yml), not project- or host-scoped, so nothing varies per call. Asks the host for the tables candy/plugin-gpu's detect-host-devices action needs (K4: plugin-deploy-pod's device auto-detection reaching verb:gpu directly, the same dispatch charly-core's gpu_shim.go already does — mirrors candy/plugin-vm/vm_gpu_shim.go's existing InvokeProvider("verb","gpu",...) precedent). Class-generic action noun "device-patterns" (F11 — never a substrate word); any substrate resolving devices needs it.
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 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
// 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 --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 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, deploykit.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 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 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"`
DevicePatterns []string `yaml:"device_patterns,omitempty" json:"device_patterns,omitempty"`
GpuVendors map[string]string `yaml:"gpu_vendors,omitempty" json:"gpu_vendors,omitempty"`
PCIClassLabels map[string]string `yaml:"pci_class_labels,omitempty" json:"pci_class_labels,omitempty"`
}
#GpuProbeInput is the action-multiplexed input the host ships to verb:gpu over OpRun. Action selects the host probe; the three data tables are threaded in from charly's embedded charly.yml (they stay in core for `charly doctor`, R3).
type GpuProbeReply ¶
type GpuProbeReply struct {
// "bool" 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.
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 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 HostProbeDevice ¶
type HostProbeDevice struct {
Pattern string `yaml:"pattern,omitempty" json:"pattern"`
Path string `yaml:"path,omitempty" json:"path"`
Present bool `yaml:"present,omitempty" json:"present"`
Description string `yaml:"description,omitempty" json:"description"`
}
#HostProbeDevice is one host device-pattern probe result (the doctor "devices" section).
type HostProbeDistro ¶
type HostProbeDistro struct {
ID string `yaml:"id,omitempty" json:"id"`
Name string `yaml:"name,omitempty" json:"name"`
Manager string `yaml:"manager,omitempty" json:"manager"`
}
#HostProbeDistro is the host distro identity + package manager (drives install-hint rendering).
type HostProbeReply ¶
type HostProbeReply 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,omitempty"`
GPUFlags []string `yaml:"gpu_flags,omitempty" json:"gpu_flags,omitempty"`
Vfio *VFIOReport `yaml:"vfio,omitempty" json:"vfio,omitempty"`
MemlockSoft uint64 `yaml:"memlock_soft,omitempty" json:"memlock_soft"`
MemlockHard uint64 `yaml:"memlock_hard,omitempty" json:"memlock_hard"`
VfioPciAvailable bool `yaml:"vfio_pci_available,omitempty" json:"vfio_pci_available"`
GroupAccessible map[string]bool `yaml:"group_accessible,omitempty" json:"group_accessible,omitempty"`
Devices []HostProbeDevice `yaml:"devices,omitempty" json:"devices,omitempty"`
Distro HostProbeDistro `yaml:"distro,omitempty" json:"distro"`
InstallHints map[string]map[string]string `yaml:"install_hints,omitempty" json:"install_hints,omitempty"`
DistroFamilyMap map[string]string `yaml:"distro_family_map,omitempty" json:"distro_family_map,omitempty"`
ConfigPath string `yaml:"config_path,omitempty" json:"config_path,omitempty"`
Credential *CredentialHealth `yaml:"credential,omitempty" json:"credential,omitempty"`
CredentialErr string `yaml:"credential_err,omitempty" json:"credential_err,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
#HostProbeReply is the "hostprobe" HostBuild kind reply — RAW host facts the plugin renders into the report. All fields are best-effort (a probe failure leaves its field zero/empty, mirroring the shims).
group_accessible is RESHAPED from the former hand type's `map[int]bool` to a string-keyed map: `cue exp gengotypes` has no int-keyed-map construct (an int-keyed CUE map degrades to an empty struct — the documented CAN/CANNOT exception). encoding/json ALREADY converts a Go `map[int]bool`'s keys to their decimal string form on the wire (Go's own int->string key rule for map marshaling), so `map[string]bool` with the SAME decimal-string keys is BYTE-IDENTICAL JSON — a pure representation fix, zero wire-format change. The two Go call sites that constructed/read the int-keyed map (charly/host_build_hostprobe.go, candy/plugin-doctor/command.go) are updated in lockstep to key by strconv.Itoa(iommuGroup).
type HostProbeRequest ¶
type HostProbeRequest struct {
Engine string `yaml:"engine,omitempty" json:"engine,omitempty"`
}
#HostProbeRequest is the "hostprobe" HostBuild kind request. Engine hints which container engine's GPU run-flags to compute (empty → the host resolves it).
type 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 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 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 (*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.
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.
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.
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 "k8s-generate-kustomize" HostBuild seam (FINAL/K5 unit 6a): the deploy:k8s preresolve body (now plugin-side, candy/plugin-kube/preresolve.go) resolves the cluster template (via "deploy-entity-resolve", kind="k8s") + the image ref + capabilities itself (all sdk-portable — kit.ResolveLocalImageRef / deploykit.ExtractMetadata, no LoadUnified needed), then calls back HERE for the ONE genuinely core-only step: charly/k8s_generate.go's GenerateK8sKustomize (Invokes the compiled-in verb:k8sgen generator + the M16 egress gate + the disk I/O — all core-only glue, unchanged, STAYS in charly/ since `charly bundle from-box --target k8s` (k8s_deploy_from_box.go) is its OTHER, non-moving caller). 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) ---
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 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 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 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 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 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 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"`
// base_user is the base image's runtime USER — the candy emits the
// post-overlay `USER <base>` restore directive.
BaseUser string `yaml:"base_user,omitempty" json:"base_user,omitempty"`
// base_security is the base image's baked LabelSecurity.
BaseSecurity *Security `yaml:"base_security,omitempty" json:"base_security,omitempty"`
// base_registry is the base image's ai.opencharly.registry OCI label.
BaseRegistry string `yaml:"base_registry,omitempty" json:"base_registry,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 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 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 PodCmdReply ¶
type PodCmdReply struct {
ExitCode int `yaml:"exit_code,omitempty" json:"exit_code,omitempty"`
}
#PodCmdReply is the "pod-cmd" host-builder reply. It carries the container command's exit_code so `charly cmd`'s own non-zero exit propagates to the operator's process code (the plugin reconstructs an *sdk.ExitCodeError from it) — the exit code cannot ride the HostBuild ERROR return, which stringifies the typed *sdk.ExitCodeError; it must ride a reply FIELD, exactly as the former __cmd/CliReply.ExitCode path did. A genuine (non-exit-code) failure still propagates as the error.
type PodCmdRequest ¶
type PodCmdRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Command string `yaml:"command,omitempty" json:"command,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Sidecar string `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
}
#PodCmdRequest carries `charly cmd <box> <command>`'s per-invocation fields for the "pod-cmd" host-builder (candy/plugin-cmd drives it): the host does ONLY the irreducible dispatchLifecycleTarget("cmd") + LifecycleTarget.Attach step (host_build_pod_lifecycle_dispatch.go's hostBuildPodCmd), mirroring hostBuildPodShell 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). Mirrors #DeployConfigSaveStateRequest's shape ({box!, instance?} → {}, the host owns the entire load+lock+mutate+save internally) — deliberately NOT a reuse of #DeployConfigSaveRequest (the `deploy-config-save` seam), which persists an ALREADY-LOADED, already-mutated whole BundleConfig with no internal load/lock/entry-removal logic (bundle import/reset's use case) — 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 PodConfigEnsureImageReply ¶
type PodConfigEnsureImageReply struct {
MetaJSON RawBody `yaml:"meta_json,omitempty" json:"meta_json"`
}
type PodConfigEnsureImageRequest ¶
type PodConfigEnsureImageRequest struct {
ImageRef string `yaml:"image_ref,omitempty" json:"image_ref"`
BuildEngine string `yaml:"build_engine,omitempty" json:"build_engine"`
}
#PodConfigEnsureImageRequest: EnsureImage + ExtractMetadata bundle (registry/podman-store coupled — a plugin cannot resolve the local podman image store namespace itself).
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 PodConfigLoadBundleReply ¶
type PodConfigLoadBundleReply struct {
ConfigJSON RawBody `yaml:"config_json,omitempty" json:"config_json,omitempty"`
}
#PodConfigLoadBundleReply: deploykit.LoadBundleConfig() — the whole-project Bundle map (no per-deploy-key focus), used by updateAllDeployedQuadlets's cross-deploy loop.
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 PodConfigProjectVolumeReply ¶
type PodConfigProjectVolumeReply struct {
VolumeJSON RawBody `yaml:"volume_json,omitempty" json:"volume_json,omitempty"`
}
type PodConfigProjectVolumeRequest ¶
type PodConfigProjectVolumeRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
}
#PodConfigProjectVolumeRequest / Reply: resolves the deploy's PROJECT-declared `volume:` override — the entity as AUTHORED in the project's own charly.yml (e.g. a disposable check bed's `volume: [{name: enc-data, type: encrypted}]`), which the per-host overlay (#PodConfigLoadDeployRequest, ~/.config/charly/charly.yml) never carries on its own. Genuinely loader-coupled (LoadUnified is a core Mechanism a plugin cannot import): the host resolves the SAME merged project+operator tree `charly bundle add` walks (resolveTreeRoot — the per-host overlay wins over the project declaration only when it actually carries an override for this key, matching MergeDeployConfigs' existing precedence) and returns ONLY the resolved entry's Volume field, scoped to one deploy key. Setup (config_setup.go) calls this as the LAST fallback in its deployVolumes resolution chain (CLI flag > env > per-host overlay > project declaration) and persists a hit into the overlay exactly as a --volume flag would, so the declaration takes effect on every subsequent read without re-resolving the project each time.
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`/#PodRemoveRequest, 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 PodConfigResolveRefRequest ¶
type PodConfigResolveRefRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
ExplicitRef string `yaml:"explicit_ref,omitempty" json:"explicit_ref,omitempty"`
}
#PodConfigResolveRefRequest: resolveDeployBoxName/resolveDeployResolvedImage/ resolveShellImageRef bundle — reads the per-host charly.yml overlay + local podman image labels (loader + podman-store coupled).
type PodConfigSSHKeyReply ¶
type PodConfigSSHKeyReply struct {
Pubkey string `yaml:"pubkey,omitempty" json:"pubkey,omitempty"`
}
type PodConfigSSHKeyRequest ¶
type PodConfigSSHKeyRequest struct {
Flag string `yaml:"flag,omitempty" json:"flag"`
ContainerName string `yaml:"container_name,omitempty" json:"container_name"`
}
#PodConfigSSHKeyRequest / Reply: resolveSSHPubKey(flag, generateDir) + containerSSHKeyDir(name) bundle (the `--ssh-key generate` path is pure ed25519/golang.org/x/crypto/ssh keygen — kept as a narrow host seam rather than adding a crypto dependency to the plugin for a rarely-used flag).
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 #PodStartReply.
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 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 PodLogsReply ¶
type PodLogsReply struct {
}
#PodLogsReply is the "pod-logs" host-builder reply — empty, mirroring #PodStartReply.
type PodLogsRequest ¶
type PodLogsRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Follow bool `yaml:"follow,omitempty" json:"follow,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Sidecar string `yaml:"sidecar,omitempty" json:"sidecar,omitempty"`
}
#PodLogsRequest carries the `charly logs` command flags (the former LogsCmd's authored fields). Forwarded to HostBuild("pod-logs"), which runs the existing dispatchLifecycleTarget + LifecycleTarget.Logs orchestration VERBATIM (F12 — the host resolves the journalctl/`<engine> logs` stream command, the owning plugin streams it live to the operator's stdio).
type PodRemoveReply ¶
type PodRemoveReply struct {
}
#PodRemoveReply is the "pod-remove" host-builder reply — empty, mirroring #PodStartReply.
type PodRemoveRequest ¶
type PodRemoveRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
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"`
}
#PodRemoveRequest carries the `charly remove` command flags (the former RemoveCmd's authored fields). Forwarded to HostBuild("pod-remove"), which runs the existing remove orchestration VERBATIM (quadlet/companion-service teardown, pre_remove hooks, purge, deploy-entry cleanup — deeply core-type-coupled: BoxMetadata/ExtractMetadata/sidecar resolution/deploykit. CleanDeployEntry — not registry-bound, but not portable either).
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 PodServiceReply ¶
type PodServiceReply struct {
}
#PodServiceReply is the "pod-service" host-builder reply — empty, mirroring #PodStartReply.
type PodServiceRequest ¶
type PodServiceRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Argv []string `yaml:"argv,omitempty" json:"argv"`
}
#PodServiceRequest 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 (host_build_pod_lifecycle_dispatch.go's hostBuildPodService), mirroring start/stop/logs/update exactly.
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 PodShellReply ¶
type PodShellReply struct {
}
#PodShellReply is the "pod-shell" host-builder reply — empty, mirroring #PodStartReply.
type PodShellRequest ¶
type PodShellRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
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"`
Instance string `yaml:"instance,omitempty" json:"instance,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"`
}
#PodShellRequest carries the `charly shell` command flags (the former ShellCmd's authored fields). Forwarded to HostBuild("pod-shell"), which runs the existing dispatchLifecycleTarget + LifecycleTarget.Attach orchestration VERBATIM (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 PodStartReply ¶
type PodStartReply struct {
}
#PodStartReply is the "pod-start" host-builder reply — empty; the start prints its own progress to the shared stdio (the compiled-in plugin's HostBuild runs in charly's own process) and signals failure via the error return.
type PodStartRequest ¶
type PodStartRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
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"`
Instance string `yaml:"instance,omitempty" json:"instance,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"`
}
#PodStartRequest carries the `charly start` command flags (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 to HostBuild("pod-start"), and the host runs the existing startViaLifecycle orchestration VERBATIM, exactly as `charly bundle add` stayed core behind HostBuild("deploy-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 PodStopReply ¶
type PodStopReply struct {
}
#PodStopReply is the "pod-stop" host-builder reply — empty, mirroring #PodStartReply.
type PodStopRequest ¶
type PodStopRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Instance string `yaml:"instance,omitempty" json:"instance,omitempty"`
Unmount bool `yaml:"unmount,omitempty" json:"unmount,omitempty"`
}
#PodStopRequest carries the `charly stop` command flags (the former StopCmd's authored fields). Forwarded to HostBuild("pod-stop"), which runs the existing stopViaLifecycle orchestration VERBATIM.
type PodUpdateReply ¶
type PodUpdateReply struct {
}
#PodUpdateReply is the "pod-update" host-builder reply — empty, mirroring #PodStartReply.
type PodUpdateRequest ¶
type PodUpdateRequest struct {
Box string `yaml:"box,omitempty" json:"box"`
Tag string `yaml:"tag,omitempty" json:"tag,omitempty"`
Build bool `yaml:"build,omitempty" json:"build,omitempty"`
Instance string `yaml:"instance,omitempty" json:"instance,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"`
}
#PodUpdateRequest carries the `charly update` command flags (the former UpdateCmd's authored fields). Forwarded to HostBuild("pod-update"), which runs the existing dispatchByDeployTarget orchestration VERBATIM — resolveTreeRoot/loadDeployPlugins/ ResolveTarget are core Mechanisms (the project loader + provider registry) a plugin cannot import or hold.
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 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.
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 RawProject ¶
type RawProject struct {
Version string `yaml:"version,omitempty" json:"version,omitempty"`
// the bare pod/vm/local/k8s/android template maps (loaderkit.ProjectTemplates — a cheap raw-byte
// copy, NO ResolveBox); findK8sSpec / local-template resolvers read this.
Templates *ProjectTemplates `yaml:"templates,omitempty" json:"templates,omitempty"`
// the folded deploy tree (uf.Bundle verbatim, with stamped Descent traits) — deploy-key→box
// resolution, the trait/tree resolvers, and member bring-up read this (the Descent is DATA already
// in the fold, clause-D, NOT a live registry query).
Deploy map[string]*Deploy `yaml:"deploy,omitempty" json:"deploy,omitempty"`
// the plugin-verb PRIMARY-field D-fact (word→primary input field) for plan resugar (carried here
// so a plugin holding this projection resugars WITHOUT dialing the host provider registry).
Primaries map[string]string `yaml:"primaries,omitempty" json:"primaries,omitempty"`
}
type RawProjectRequest ¶
type RawProjectRequest struct {
Dir string `yaml:"dir,omitempty" json:"dir,omitempty"`
IncludeDisabled bool `yaml:"include_disabled,omitempty" json:"include_disabled,omitempty"`
LocalSuperproject bool `yaml:"local_superproject,omitempty" json:"local_superproject,omitempty"`
}
#RawProjectRequest / #RawProject — the CHEAP raw-loader HostBuild seam ("raw-project"), the endgame keystone that lets the loader-coupled deploy files MOVE to their plugins (the ruling: loader-coupling is the work, not a defer reason). It mirrors resolved-project (#ResolvedProject) but SKIPS the expensive ResolveBox-per-box cost: a plugin that only needs the RAW loader reads (kind templates, the folded deploy tree with stamped Descent, the plugin-primaries D-fact) fetches this instead of paying the full box resolution the resolved-project envelope also pays. Kind-blind throughout — templates/deploy carry OPAQUE bytes the consuming plugin decodes itself. Additive: later fields (config defaults, etc.) join as the consumer unit that first needs them lands (the SAME additive pattern #ResolvedProject uses).
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 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 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 concrete git-fetch implementation (kit.DefaultDownloader, wrapping kit.DownloadRepo) stays in kit.
type RemoteImageResolveReply ¶
type RemoteImageResolveReply struct {
ImageRef string `yaml:"image_ref,omitempty" json:"image_ref,omitempty"`
CacheDir string `yaml:"cache_dir,omitempty" json:"cache_dir,omitempty"`
BoxName string `yaml:"box_name,omitempty" json:"box_name,omitempty"`
Error string `yaml:"error,omitempty" json:"error,omitempty"`
}
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: resolve an @github.com/org/repo/box ref to its registry pull ref + cached source dir (wraps ResolveRemoteImage).
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 RenderServiceReply ¶
type RenderServiceReply struct {
Rendered *RenderedService `yaml:"rendered,omitempty" json:"rendered,omitempty"`
}
type RenderServiceRequest ¶
type RenderServiceRequest struct {
Entry CandyService `yaml:"entry,omitempty" json:"entry"`
Init ResolvedInit `yaml:"init,omitempty" json:"init"`
Ctx ServiceRenderContext `yaml:"ctx,omitempty" json:"ctx"`
}
#RenderServiceRequest/#RenderServiceReply — the "render-service" HostBuild seam (K5-A item 1, compile-seam ctx-threading, increment B): the former charly/service_render.go:RenderService wraps TWO registry consults a plugin cannot do itself — candy/plugin-init's OpResolve (render the unit text/path) and the M16 egress gate (reject a template-render failure's "<no value>" marker before the unit is written) — so the WHOLE function stays host-side, reached as ONE seam call rather than splitting it into two separate InvokeProvider round trips. deploykit.CompileServiceSteps (the ctx/exec-threaded replacement for the retired deploykit.CompileServiceSteps func var) calls this ONLY for a systemd CUSTOM entry that needs unit-text rendering — the packaged-unit case and the supervisord case never reach it.
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 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"`
}
#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 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.
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 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 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"`
// 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.
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 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 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 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.
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 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 "vm-build" host-builder reply (P8b-rest): 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). The host resolves the kind:vm entity + the build vocabulary + the per-source-kind image refs into a #VmBuildReply envelope (the "vm-build" host-builder is PREP+RESOLVE only, P8b-rest); the plugin's `charly vm build` command 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("build-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
- candy_reader.go
- candy_ref_entry.go
- charly_methods.go
- charly_names.go
- config.go
- cue_types_gen.go
- deploy_consts.go
- deploy_executor.go
- deploy_methods.go
- deploy_tree_validate.go
- distro_methods.go
- enc_consts.go
- gpu_consts.go
- gpu_methods.go
- gpu_select.go
- hand_state_types.go
- install_plan.go
- install_step.go
- ir_enums.go
- label_consts.go
- labelset_methods.go
- load_directives.go
- loader_seam.go
- loadmodel.go
- mcp_provides.go
- namespace_ref.go
- node_helpers.go
- poll.go
- provider_carriers.go
- readiness.go
- ref_parse.go
- resolved_box.go
- scalar_aliases.go
- shellquote.go
- ssh_target.go
- status_result.go
- status_types.go
- substrate_template_methods.go
- tunnel_methods.go
- union_types.go
- verb_context.go
- version_gen.go
- vm_domain.go
- vocab_gen.go