Documentation
¶
Overview ¶
Package sdk is the importable surface an out-of-tree charly plugin builds against. An external plugin implements the proto Provider + PluginMeta services (github.com/opencharly/sdk/proto) and calls sdk.Serve from its main; charly connects to it through the SAME handshake + dispense key. The handshake/glue live here (NOT in charly's package main) so both charly and an external plugin share ONE definition — no drift, no duplication (R3).
Index ¶
- Constants
- Variables
- func BuildCapabilities(calver string, provided []ProvidedCapability, schemaFS fs.FS, dir string) (*pb.Capabilities, error)
- func BuildDeployReply(reverseOps []spec.ReverseOp, candy, version string) (*pb.InvokeReply, error)
- func CheckRequiredModifiers(method string, op *spec.Op, required map[string][]string, ...) error
- func ContextWithExecutor(ctx context.Context, e *Executor) context.Context
- func DecodeDeployVenue(envJSON []byte) (spec.DeployVenue, error)
- func DecodeInstallPlans(paramsJSON []byte) ([]spec.InstallPlanView, error)
- func IsServeMode() bool
- func Main(providerSrv pb.ProviderServer, metaSrv pb.PluginMetaServer, ...)
- func MatchAll(value string, matchers []Matcher) error
- func MatchValueString(v any) string
- func MatchValueStrings(v any) []string
- func NewCheckContext(brokerID uint32, envJSON []byte) (kit.CheckContext, error)
- func NewMeta(calver string, caps []ProvidedCapability, schemaFS fs.FS) pb.PluginMetaServer
- func NormalizePhase(p string) string
- func OpModifierZero(op *spec.Op, name string) bool
- func PluginMap(providerSrv pb.ProviderServer, metaSrv pb.PluginMetaServer) plugin.PluginSet
- func PluginScriptReverseOp(scope spec.Scope, script string) spec.ReverseOp
- func Preview(s string) string
- func RequireModifiers(method string, op *spec.Op, required map[string][]string) error
- func ResultJSON(status, msg string) (*pb.InvokeReply, error)
- func RunArtifactValidators(op *spec.Op) error
- func Serve(providerSrv pb.ProviderServer, metaSrv pb.PluginMetaServer)
- func ServeCheckVerb(kv kit.CheckVerbProvider, meta pb.PluginMetaServer)
- func VerbVerdict(verb, method, out string, runErr error, op *spec.Op, artifact bool) (*pb.InvokeReply, error)
- type CLIArg
- type CLILeaf
- type CLIModel
- type Conn
- type Executor
- func (e *Executor) GetFile(ctx context.Context, path string, asRoot bool) ([]byte, error)
- func (e *Executor) HostArbiter(ctx context.Context, action string, params []byte) ([]byte, error)
- func (e *Executor) HostBuild(ctx context.Context, kind string, spec []byte) ([]byte, error)
- func (e *Executor) InvokeProvider(ctx context.Context, class, word, op string, params, env []byte) ([]byte, error)
- func (e *Executor) PutFile(ctx context.Context, remotePath string, content []byte, mode uint32, ...) error
- func (e *Executor) RunCapture(ctx context.Context, script string) (stdout, stderr string, exit int, err error)
- func (e *Executor) RunHostStep(ctx context.Context, step spec.InstallStepView, optsJSON []byte) ([]spec.ReverseOp, error)
- func (e *Executor) RunSystem(ctx context.Context, script string, optsJSON []byte) error
- func (e *Executor) RunUser(ctx context.Context, script string, optsJSON []byte) error
- func (e *Executor) Venue(ctx context.Context) (string, error)
- func (e *Executor) VenueCapture(ctx context.Context, script string) (string, error)
- func (e *Executor) VenueHasTool(ctx context.Context, tool string) bool
- func (e *Executor) VenueRunSilent(ctx context.Context, script string) error
- type Matcher
- type ProvidedCapability
- type StepContract
Constants ¶
const ( OpRun = "run" // verb: run a check / live-container probe → CheckResult OpLoad = "load" // kind: decode a node into its typed entity OpValidate = "validate" // kind: closed/concrete CUE validation → Diagnostics OpEmit = "emit" // deploy/step: emit an InstallPlan / Containerfile fragment OpExecute = "execute" // deploy/step: execute against a venue (streamed) OpResolve = "resolve" // builder: resolve a builder image + steps (build-time multi-stage) OpBuild = "build" // build: dispatch the image-build / generate engine host-side (F10 HostBuild seam) // OpCollectContext + OpReverse are the DEPLOY-TIME builder-IR legs of an externalized // detection-builder plugin (cargo/npm/pixi/aur). A builder's build-time multi-stage is // resolved by its OpResolve leg (C10); these two carry the per-builder deploy-time IR // shim — the stage-context the compiler records on a BuilderStep + that step's teardown // ops — out-of-process. BOTH are invoked HOST-SIDE in the build PRE-PASS (BEFORE the pure // BuildDeployPlan compile reads the result), never inside the pure compiler. OpCollectContext = "collect-context" // builder: per-candy stage-context keys → BuilderCollectReply OpReverse = "reverse" // builder: teardown ops for a resolved stage context → BuilderReverseReply // F6 — the SUBSTRATE LIFECYCLE selectors (host→plugin on Provider.Invoke): a deploy // substrate plugin brings its OWN host-side venue lifecycle. PrepareVenue/TeardownExecutor // return a VenueDescriptor the HOST re-materializes into a real DeployExecutor (the live // executor never crosses the wire); the rest carry name/node/opts in, error/StatusInfo out. OpPrepareVenue = "prepare-venue" // lifecycle: build the venue → VenueDescriptor (re-materialized host-side) OpArtifactKey = "artifact-key" // lifecycle: the per-deploy artifact ledger key OpPostApply = "post-apply" // lifecycle: post-walk finalize on the venue OpTeardownExecutor = "teardown-executor" // lifecycle: the executor for Del → VenueDescriptor OpPostTeardown = "post-teardown" // lifecycle: drop venue artifacts (image/domain) OpStart = "start" // lifecycle: start the venue OpStop = "stop" // lifecycle: stop the venue OpStatus = "status" // lifecycle: venue status → StatusInfo OpLogs = "logs" // lifecycle: stream venue logs OpShell = "shell" // lifecycle: open a venue shell OpRebuild = "rebuild" // lifecycle: rebuild the venue (charly update) // OpConfigWrite is the POD config-WRITE selector (P11, Q1=(a)): the HOST `charly config` // command resolves the full QuadletConfig + the host-side target paths and Invokes the // deploy:pod plugin's OpConfigWrite with a spec.PodConfigWriteRequest; the plugin renders the // .container/.pod/sidecar/tunnel file CONTENTS (deploykit.GenerateQuadlet + the pod/sidecar/ // tunnel generators) and os.WriteFiles them at the exact modes (same-host, compiled-in), // returning the written paths. RESOLVE + host side-effects (secret provisioning, saveDeployState, // enc-mount, data-seed, systemctl) stay in the host command — the plugin owns only the // config-WRITE (Ruling C). Distinct from the venue-lifecycle Ops: host-initiated, not a deploy. OpConfigWrite = "config-write" OpStatusCollect = "status-collect" // command:status: programmatic status collection → []spec.DeploymentStatus (distinct from lifecycle OpStatus) // OpPreresolve is the generalized host-side deploy preresolver (F6): a substrate plugin // declares a preresolve step the host runs BEFORE apply, returning the opaque JSON the host // ships in DeployVenue.Substrate (the wire-backed generalization of the in-core k8s/android // preresolvers). OpPreresolve = "preresolve" // OpBootstrap is the BOOTSTRAP-PHASE hook (F9): the kernel invokes a Phase=="bootstrap" // plugin BEFORE config validation, passing the RAW project config bytes // (params {"config": <bytes>}) and applying any transformed bytes the plugin returns // (reply {"config": <bytes>}) — a generic pre-validation transform hook (a no-op bootstrap // plugin returns the bytes unchanged). It is NOT the migration path: config-schema migration // is candy/plugin-migrate's command:migrate over OpRun (a whole-project file-walk that runs // on the config exactly when it cannot load), never a raw-byte bootstrap transform. Bootstrap // plugins are COMPILED-IN (in-proc), so this hook never re-enters the validated-config load. OpBootstrap = "bootstrap" )
Operation selectors (the op.Op / InvokeRequest.Op wire value). Each provider class uses the subset it needs. This is the SINGLE SOURCE for the selectors (R3): charly's package main aliases these (provider.go), and an out-of-tree / compiled-in plugin's Invoke dispatch compares req.GetOp() against them — so a kind candy checks sdk.OpLoad, a step/deploy candy sdk.OpEmit/sdk.OpExecute, a builder candy sdk.OpResolve.
const ( PhaseBootstrap = "bootstrap" // before config validation/migration; compiled-in only (no validated config exists yet to discover an out-of-process source). PhaseSchema = "schema" // schema / migration phase PhaseLoad = "load" // config-load phase (kind decode, etc.) PhaseBuild = "build" // image-build phase (OpEmit / OpResolve) PhaseRuntime = "runtime" // deploy / runtime phase (OpExecute / OpRun) — the DEFAULT )
Plugin lifecycle PHASES (F9) — the ordered points at which a plugin participates in charly's lifecycle. A plugin DECLARES its phase via ProvidedCapability.Phase (default PhaseRuntime); the kernel loads/invokes plugins in phase order. The BOOTSTRAP phase runs BEFORE config validation/migration, so an early-running capability can itself be a plugin loaded at the right time (today only the no-op candy/plugin-example-bootstrap registers here — neither migrate nor egress is a bootstrap plugin; both are verb plugins invoked the normal way). This is the SINGLE SOURCE for the phase vocabulary (R3): charly's package main aliases these, and a plugin's Describe declares its phase against them.
const DispenseKey = "charly"
DispenseKey is the single go-plugin plugin name; charly serves/dispenses ONE gRPC plugin exposing the uniform Provider + PluginMeta services.
const ProtocolVersion = 1
ProtocolVersion is the go-plugin/proto contract version — a thin secondary gate. CalVer (charly's version.go) is the authority; matching CalVer ⇒ matching proto.
Variables ¶
var Handshake = plugin.HandshakeConfig{ ProtocolVersion: ProtocolVersion, MagicCookieKey: "CHARLY_PLUGIN", MagicCookieValue: "charly-plugin-v1", }
Handshake is the magic-cookie handshake charly and every plugin MUST share. A plugin server refuses to serve unless launched with CHARLY_PLUGIN set, so a plugin binary run by hand prints the "not meant to be executed directly" notice instead of hanging.
var PhaseOrder = []string{PhaseBootstrap, PhaseSchema, PhaseLoad, PhaseBuild, PhaseRuntime}
PhaseOrder lists the phases in ascending load order; the kernel iterates plugins phase-ascending (bootstrap first). It is the authority for ordering + membership.
Functions ¶
func BuildCapabilities ¶
func BuildCapabilities(calver string, provided []ProvidedCapability, schemaFS fs.FS, dir string) (*pb.Capabilities, error)
BuildCapabilities is the serve-side half of the "every plugin ships its own CUE schema" contract. It concatenates the plugin's embedded schema/*.cue via the SAME schemaconcat contract charly uses for its base (R3 — one concat loop, no duplicate), compiles it STANDALONE to fail loudly on a broken or empty schema (a self-contained schema must compile alone — the same property that lets `cue exp gengotypes` generate the plugin's Go params), and assembles the Describe reply carrying the raw .cue source the host splices onto its base.
schemaFS is the plugin's `//go:embed schema/*.cue` FS; dir is the embedded subdirectory ("schema"). Both the SDK and charly's base reach the same internal schemaconcat because the SDK lives under charly/ — an external module imports only this SDK, never charly/internal directly.
func BuildDeployReply ¶
BuildDeployReply assembles the OpExecute reply: the teardown ops the host records + the ledger CandyRecord identity (candy name + version). The host decodes the same spec.DeployReply and persists it via install_ledger.go. A plugin's Invoke returns this directly as its *pb.InvokeReply.
func CheckRequiredModifiers ¶
func CheckRequiredModifiers(method string, op *spec.Op, required map[string][]string, isZero func(op *spec.Op, name string) bool) error
CheckRequiredModifiers verifies every modifier a method requires is present on op, returning a "missing required modifier(s): …" error naming the absent ones. required maps a method name to its required modifier field names, and isZero reports whether a named modifier is absent (zero) on op. RequireModifiers binds isZero to the generic OpModifierZero; this lower form stays for a verb whose zero-semantics genuinely differ from plain reflection (R3).
func ContextWithExecutor ¶
ContextWithExecutor returns ctx carrying an in-proc *Executor. The host's in-proc dispatch (inprocProvider.InvokeWithExecutor) calls this before invoking a compiled-in plugin so the plugin's ExecutorForInvoke can reach the reverse channel without a broker.
func DecodeDeployVenue ¶
func DecodeDeployVenue(envJSON []byte) (spec.DeployVenue, error)
DecodeDeployVenue decodes the venue descriptor the host put in an OpExecute Invoke's env_json (op.Env). The zero DeployVenue is returned for an empty payload (the common "no venue env" call, e.g. the e2e direct invoke).
func DecodeInstallPlans ¶
func DecodeInstallPlans(paramsJSON []byte) ([]spec.InstallPlanView, error)
DecodeInstallPlans decodes the host-marshalled InstallPlan VIEWS carried in an OpExecute Invoke's params_json (op.Params). Returns nil for an empty payload (a deploy with no candy plans — e.g. a marker-only example). The rich in-core Steps are NOT on the wire (see spec.InstallPlanView); the provenance fields prove the plan travelled.
func IsServeMode ¶
func IsServeMode() bool
IsServeMode reports whether this process was launched by charly as a go-plugin gRPC SERVER (the handshake magic-cookie env is present) rather than invoked directly as a CLI. charly sets the cookie ONLY when it execs a plugin to connect over gRPC (LocalTransport, for a verb/kind/deploy/step/builder capability); a COMMAND plugin fork/exec'd as a CLI passthrough (charly's syscall.Exec command dispatch strips the cookie) sees it absent and runs in CLI mode. The single switch a dual-mode plugin's main() pivots on.
func Main ¶
func Main(providerSrv pb.ProviderServer, metaSrv pb.PluginMetaServer, cli func(args []string) int)
Main is the dual-mode entry point a plugin's main() delegates to. In SERVE mode (charly launched it over go-plugin gRPC) it serves the plugin's Provider + PluginMeta (its verb/kind/deploy/step/builder capabilities). Otherwise the plugin was fork/exec'd by charly's COMMAND dispatch (or run by hand) and owns real terminal stdio/TTY: cli runs the command's work with os.Args[1:], its int return becoming the process exit code.
func main() { sdk.Main(&provider{}, &meta{}, cliMain) }
func MatchAll ¶
MatchAll returns nil if every matcher succeeds against the value. The first failure wins (reports the specific unmet expectation).
Takes []Matcher rather than MatcherList so callers can pass any named slice type whose underlying element is Matcher (e.g. ContainsList) without an explicit conversion at every call site.
Shared by the core check runner and out-of-tree verb plugins.
func MatchValueString ¶
MatchValueString coerces a matcher's stored Value (any) to a string. For numeric types it renders canonically; for everything else it falls back to fmt.Sprint.
Shared by the core check runner and out-of-tree verb plugins.
func MatchValueStrings ¶
MatchValueStrings handles list-valued matchers like {contains: [a, b]}. A scalar value becomes a singleton list.
Shared by the core check runner and out-of-tree verb plugins.
func NewCheckContext ¶ added in v0.2026187.2110
func NewCheckContext(brokerID uint32, envJSON []byte) (kit.CheckContext, error)
NewCheckContext builds the out-of-process kit.CheckContext for a RAW pb.Provider (Invoke) that needs the reverse-channel legs (ResolveEndpoint / HTTPDo / Exec) but is NOT a kit.CheckVerbProvider (so the kit-verb serve path never built one for it). envJSON is the InvokeRequest.env_json (the host's CheckEnv snapshot). Dials the broker ONCE — do NOT also call ExecutorFromInvoke on the same Invoke (a second Dial hangs; use cc.Exec() instead).
func NewMeta ¶ added in v0.2026185.2311
func NewMeta(calver string, caps []ProvidedCapability, schemaFS fs.FS) pb.PluginMetaServer
NewMeta returns the shared PluginMetaServer for a plugin: its Describe reply carries caps + the CUE schema embedded at schemaFS's "schema" dir. It replaces the ~58 hand-rolled meta types + Describe bodies (R3) — a plugin's NewMeta() is now just `return sdk.NewMeta(calver, caps, schemaFS)`.
func NormalizePhase ¶
NormalizePhase maps an empty or unrecognized declared phase to the default (PhaseRuntime), so a plugin that declares no phase participates at the normal (runtime) time.
func OpModifierZero ¶ added in v0.2026185.2336
OpModifierZero reports whether the modifier named `name` is absent (zero) on the step. Since the schema-compaction cutover a verb's per-method fields live in the desugared plugin INPUT map (op.PluginInput), so the lookup is map-first: a key present in the input with a non-zero value is present. The handful of genuinely SHARED #Op fields a method contract may still name (target, caps) fall back to reflection over spec.Op's yaml tags. An unknown name is treated as absent. Asserted by TestOpModifierZeroMatchesFields.
func PluginMap ¶
func PluginMap(providerSrv pb.ProviderServer, metaSrv pb.PluginMetaServer) plugin.PluginSet
PluginMap builds the go-plugin PluginSet for the dispense key. Server side passes the two service impls; the client side (charly connecting) passes nil,nil and receives a *Conn from the dispense.
func PluginScriptReverseOp ¶
PluginScriptReverseOp builds the generic recordable teardown op an external deploy/step/builder plugin returns: a verbatim shell script run at `charly bundle del` time at the given scope (spec.ScopeSystem → root, spec.ScopeUser → deploy user). The host records it in the ledger and replays it via reverse_ops.go's reversePluginScript — record-and-replay, never recomputed.
func Preview ¶ added in v0.2026186.2
Preview truncates a string to 400 chars (adding an ellipsis) for verdict error messages — the shared truncation each live-verb plugin formerly copied.
func RequireModifiers ¶ added in v0.2026185.2336
RequireModifiers verifies every modifier a method requires is present on op, using the generic OpModifierZero — so a plugin keeps ONLY its requiredModifiers map (genuinely per-verb data) and drops both its copy-pasted modifierZero func and the CheckRequiredModifiers wrapper call (R3). It is CheckRequiredModifiers bound to OpModifierZero.
func ResultJSON ¶
func ResultJSON(status, msg string) (*pb.InvokeReply, error)
ResultJSON builds the InvokeReply an out-of-process check verb's Invoke returns — the SAME {status,message} shape every verb plugin (and ServeCheckVerb) emits (R3).
func RunArtifactValidators ¶
RunArtifactValidators runs every artifact assertion the step's plugin input declares against the file at the input's `artifact` path: artifact_min_bytes, artifact_min_dimensions (WxH), artifact_not_uniform, and artifact_min_cast_events. The artifact fields live in the desugared plugin input map (per-verb fields left core #Op in the schema-compaction cutover). Returns nil when every declared validator passes, or the first validator's error. A plugin that produces an artifact calls this after writing the file as the post-run validation pipeline.
func Serve ¶
func Serve(providerSrv pb.ProviderServer, metaSrv pb.PluginMetaServer)
Serve exposes a plugin's Provider + PluginMeta services over go-plugin gRPC and blocks serving. The host reaps it on exit by killing the client connection (providerRegistry.Close → client.Kill, sending the gRPC Shutdown that stops this server); go-plugin's server has no parent-death detection of its own, so watchParentDeath is the backstop that self-terminates this process if the host dies without reaping (crash / SIGKILL / os.Exit) — preventing orphaned `__plugin serve` processes. The serve half of Main (a verb/kind/deploy/step/ builder plugin with no CLI mode may call it directly):
func main() { sdk.Serve(&myProvider{}, &myMeta{}) }
func ServeCheckVerb ¶
func ServeCheckVerb(kv kit.CheckVerbProvider, meta pb.PluginMetaServer)
ServeCheckVerb serves a HOST-COUPLED check verb (kit.CheckVerbProvider) OUT-OF-PROCESS (F2): it wraps the verb in a pb.ProviderServer whose Invoke reconstructs a kit.CheckContext from the host's reverse channel (ExecutorService for Exec + CheckContextService for HTTPDo/AddBackground, on the InvokeRequest broker) plus the env_json CheckEnv snapshot (Mode/Box/Instance/Distros/DialTimeout), runs RunVerb, and returns the verdict. The SAME kit verb compiles INTO charly in-process (registerCompiledCheckVerb passes the live *Runner as the CheckContext); this is the out-of-process placement, ZERO authoring change. A kit candy's cmd/serve is the SAME one-liner shape as every pb-provider plugin: sdk.ServeCheckVerb(pkg.NewCheckVerb(), pkg.NewMeta()) — meta is the ONE shared NewMeta the candy also exports, unifying kit candies with the pb-provider authoring shape (R3).
func VerbVerdict ¶ added in v0.2026186.2
func VerbVerdict(verb, method, out string, runErr error, op *spec.Op, artifact bool) (*pb.InvokeReply, error)
VerbVerdict is the shared exit/stdout/stderr/artifact verdict pipeline every live check verb runs after dispatching an op — the byte-identical block cdp/vnc/wl/dbus/record/… each carried, hoisted here (R3). It maps runErr → exit 1 + stderr, compares exit against the authored op.ExitStatus, runs the op.Stdout/op.Stderr MatchAll validators and (when artifact is true) the artifact validators, and returns the {status,message} reply — "fail" naming the first mismatch, "pass" with a non-empty body (out, else stderr, else a synthetic "<verb> <method>: exit=N"). `verb` prefixes every message ("cdp: screenshot: exit=…"); the caller passes whether THIS method produces an artifact (e.g. method == "screenshot").
Types ¶
type CLIArg ¶
type CLIArg struct {
Prop string `json:"prop"` // snake_case schema property name
Name string `json:"name"` // original kong flag name (for --Name=…)
Type string `json:"type"` // json-schema primitive: string/boolean/integer/number/array/object
Help string `json:"help,omitempty"` // arg help → schema description
Enum []string `json:"enum,omitempty"` // allowed values
Default string `json:"default,omitempty"` // raw default string (coerced to Type by the consumer)
HasDefault bool `json:"has_default,omitempty"` // a default was declared
Required bool `json:"required,omitempty"` // required arg
IsBool bool `json:"is_bool,omitempty"` // bool flag (emits --Name / --no-Name, no value)
IsSlice bool `json:"is_slice,omitempty"` // accumulating slice (repeated --Name=… / multiple positionals)
IsMap bool `json:"is_map,omitempty"` // map flag (object schema)
Negated bool `json:"negated,omitempty"` // bool flag supports the --no- prefix
ElemType string `json:"elem_type,omitempty"` // slice element json-schema type
}
CLIArg describes one positional or flag. `Prop` is the snake_case JSON-schema property name an MCP tool exposes; `Name` is the original kong flag name used to build `--Name=value` argv (unused for positionals, which are positional in argv).
type CLILeaf ¶
type CLILeaf struct {
Path string `json:"path"` // dotted, e.g. "box.build"
Help string `json:"help,omitempty"` // leaf help text
Hidden bool `json:"hidden,omitempty"` // hidden leaf (e.g. __plugin / __cli-model)
Positionals []CLIArg `json:"positionals,omitempty"` // declared order — appended to argv as values
Flags []CLIArg `json:"flags,omitempty"` // flattened across ancestor branches
}
CLILeaf is one runnable leaf command (e.g. "box.build"), with its arg surface.
type CLIModel ¶
type CLIModel struct {
Name string `json:"name"` // "charly"
Version string `json:"version"` // CharlyVersion()
Leaves []CLILeaf `json:"leaves"` // one per runnable leaf command
}
CLIModel is the full command-tree description of a `charly` binary.
type Conn ¶
type Conn struct {
Provider pb.ProviderClient
Meta pb.PluginMetaClient
// Broker is this connection's go-plugin GRPCBroker — the host's handle to stand up
// the E3b reverse-channel ExecutorService a deploy/step/builder plugin dials back
// to. Nil for an in-proc transport (no reverse channel needed).
Broker *plugin.GRPCBroker
}
Conn is the dispensed client handle — charly's side of a connected plugin.
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor is the plugin-side handle to the host's live DeployExecutor over the E3b reverse channel. An out-of-process deploy/step/builder plugin runs shell/SSH ops on the real venue by calling these; the host executes them with the executor it stood up on the broker for this Invoke. The plugin never holds the (unmarshallable) executor itself.
func ExecutorForInvoke ¶
ExecutorForInvoke resolves the host executor for a plugin's Invoke, transport-invisibly: an IN-PROC compiled-in plugin gets it from the context (ContextWithExecutor); an OUT-OF-PROCESS plugin falls back to the go-plugin broker id in its InvokeRequest. Plugin Invoke code calls this ONE accessor and works in either placement unchanged.
func ExecutorFromInvoke ¶
ExecutorFromInvoke dials the host's ExecutorService using the broker id the host passed in InvokeRequest.executor_broker_id. Errors if this plugin was not served over go-plugin (no broker) or the id is 0 (no executor attached — a verb/kind op, or a deploy op the host ran in-proc).
func NewInProcExecutor ¶
func NewInProcExecutor(client pb.ExecutorServiceClient) *Executor
NewInProcExecutor wraps an in-proc pb.ExecutorServiceClient (an adapter delegating DIRECTLY to the host's executorReverseServer, no socket) as an *Executor — the IN-PROCESS twin of the go-plugin broker path in ExecutorFromInvoke. It is what lets a COMPILED-IN plugin reach the SAME reverse-channel seam (HostBuild / RunHostStep / …) an out-of-process plugin dials over gRPC: the host threads the resulting *Executor onto the Invoke context (ContextWithExecutor), and the plugin picks it up via ExecutorForInvoke — so the plugin's Invoke code is byte-identical in both placements (placement-invisible above the registry, the whole point of the plugin abstraction).
func (*Executor) GetFile ¶
GetFile reads a venue file back to the host (asRoot reads via sudo) — the check-verb artifact-pull leg (a screenshot / recording produced on the venue).
func (*Executor) HostArbiter ¶
HostArbiter drives one C9 resource-arbiter host-seam over the reverse channel: the COMPILED-IN candy/plugin-preempt (verb:arbiter) calls back mid-logic for a host dependency it cannot hold across the module boundary — config gather/resources, VM/pod lifecycle running/stop/start, the GPU driver flip switchMode/ensureCDI. action is one of spec.ArbiterSeam*; params/result are the seam's spec request/reply JSON. A non-empty reply error is an infra failure of the RPC handler itself (a seam OP failure rides the reply's own spec error field, decoded by the caller).
func (*Executor) HostBuild ¶
HostBuild asks the host to run the registered host-builder for kind (F10 host-build) with the opaque spec, returning the builder's opaque result JSON. A non-empty reply error is a build failure (the RPC itself succeeded).
func (*Executor) InvokeProvider ¶
func (e *Executor) InvokeProvider(ctx context.Context, class, word, op string, params, env []byte) ([]byte, error)
InvokeProvider asks the host to invoke ANOTHER provider (class, word, op) on this plugin's behalf (F10 plugin↔plugin) — the host resolves it in the registry and Invokes it (threading the SAME venue executor into an out-of-process target), returning the raw result JSON. params/env are the op's plugin_input / env (nil for none).
func (*Executor) PutFile ¶
func (e *Executor) PutFile(ctx context.Context, remotePath string, content []byte, mode uint32, ownerRoot bool) error
PutFile places file content at a path on the venue — the deploy/step file-PLACEMENT leg. An out-of-process deploy/step plugin that EXECUTES an InstallPlan's steps ships the bytes (a service unit, an env.d file, the charly binary, a builder artifact); the host materializes them and delegates to the live DeployExecutor.PutFile. ownerRoot == true installs the file as root:root (root-owned system paths); mode is the octal permission bits. Binary-safe (proto bytes). A non-empty reply error is the placement failure on the venue.
func (*Executor) RunCapture ¶
func (e *Executor) RunCapture(ctx context.Context, script string) (stdout, stderr string, exit int, err error)
RunCapture runs a command on the venue and returns stdout/stderr/exit separately — the check-verb capture leg (an out-of-process exec-based check verb probing the live container). A non-empty reply error is an EXECUTION failure, NOT a non-zero exit (which rides the returned exit code). Mirrors kit.Executor.RunCapture over the wire.
func (*Executor) RunHostStep ¶
func (e *Executor) RunHostStep(ctx context.Context, step spec.InstallStepView, optsJSON []byte) ([]spec.ReverseOp, error)
RunHostStep is the HOST-ENGINE channel leg (the generalization of the former F3 build channel): a deploy/step plugin walking an InstallPlan that hits one of the five step kinds it cannot execute itself — BuilderStep (podman / makepkg / EnsureImagePresent), LocalPkgInstallStep, SystemPackagesStep (the DistroConfig package-template render), an act-verb OpStep (a builtin ProvisionActor that needs the in-proc registry), or an ExternalPluginStep (a verb served by ANOTHER out-of-process plugin, dispatched over a nested reverse channel) — drives this. The host reconstructs the step, runs the existing in-core machinery on the host, applies the effect onto the venue, and returns the step's recorded reverse ops. The plugin folds them into its DeployReply (sdk.BuildDeployReply) so `charly bundle del` replays them (record-and-replay teardown). The plugin owns the plan WALK; the host owns the host ENGINE. A non-nil error is a host-engine/apply FAILURE on the venue.
func (*Executor) RunSystem ¶
RunSystem runs a root (sudo) script on the venue; optsJSON is a marshalled EmitOpts (nil for none). A non-empty reply error is the script's failure on the venue.
func (*Executor) VenueCapture ¶
VenueCapture runs a command on the venue and returns stdout, surfacing stderr on a non-zero exit — an EXEC-based check verb's capture-or-fail helper over the reverse channel.
func (*Executor) VenueHasTool ¶
VenueHasTool reports whether tool is on PATH on the venue — an EXEC-based check verb's tool-presence probe over the reverse channel.
type Matcher ¶
Matcher is re-exported from charly/spec so an out-of-tree plugin reaches the matcher value type through the SDK alone (an external plugin imports no other charly package).
type ProvidedCapability ¶
type ProvidedCapability struct {
Class string // "verb" / "kind" / "deploy" / "step" / "builder"
Word string // the reserved word, e.g. "externalprobe"
InputDef string // the CUE def for this word's plugin_input, e.g. "#ExternalprobeInput"
// StepContract is set ONLY for Class=="step" (F3): the plugin-declared install-step
// contract (Scope/Venue/Gate) the host applies to the external step via the open default
// arm — no compiled-in case. nil for every other class.
StepContract *StepContract
// Structural is set ONLY for Class=="kind" (F5): the kind decodes a STRUCTURAL entity —
// its OpLoad returns a spec.Deploy member tree the host folds into uf.Bundle — rather than
// a FLAT body landed opaquely in uf.PluginKinds (F4). false for every other class/kind.
Structural bool
// Lifecycle is set ONLY for Class=="deploy" (F6): the substrate brings its OWN host-side
// venue lifecycle (PrepareVenue/Start/Stop/Status/Rebuild/...) served over the lifecycle Ops,
// so the host registers a wire-backed substrateLifecycle for it. false for every other
// class/deploy (local/android/k8s keep the generic host-venue behaviour).
Lifecycle bool
// Preresolve is set ONLY for Class=="deploy" (F6): the substrate declares a host-side
// PRERESOLVE step (OpPreresolve) the host runs before apply, shipping the opaque result in
// DeployVenue.Substrate — the wire-backed generalization of the in-core k8s/android
// preresolvers. false for every other class/deploy.
Preresolve bool
// Validates is set ONLY for Class=="kind" (F7/C8): the kind serves a deep OpValidate check
// (returns spec.Diagnostics) the host dispatches at load, BEYOND the static CUE input-def
// gate. false → only the static gate runs (every other class/kind).
Validates bool
// Phase is the plugin lifecycle PHASE (F9): one of the sdk.Phase* constants. "" → the kernel
// treats it as PhaseRuntime (the default). PhaseBootstrap runs BEFORE config validation —
// declare it for a capability that must load/run early (migrate, egress). The kernel loads +
// invokes plugins in PhaseOrder.
Phase string
// Primary is set ONLY for Class=="verb": the input field the scalar sugar
// shorthand targets (`file: /x` → plugin_input: {<Primary>: "/x"}). "" → the
// verb takes a map input only. The host registers it into the parse-time
// desugar's primary registry (compiled-in at init; an EXTERNAL plugin
// additionally declares it in its candy manifest's plugin.primary map so the
// byte-gated prescan knows it BEFORE the provider connects).
Primary string
// DeployTraits is set ONLY for Class=="kind" on a SUBSTRATE kind (P9): the kind's
// DECLARED deploy behaviour (venue + image_backed/image_context/machine_venue/
// exclusive_venue/leaf_only). kit.StampDescent stamps it onto every node's
// spec.DescentDescriptor so the kernel consults the substrate behaviour BY TRAIT
// (off node.Descent) — never by switching on the kind word. nil for every other
// capability (the zero-value → external-in-place semantics).
DeployTraits *spec.DeployTraits
}
ProvidedCapability is one capability a plugin serves plus the CUE def that validates its plugin_input — the SDK-facing form of the proto ProvidedCapability. An external plugin lists these in its Describe; the host validates authored plugin_input for each word against its def in the served schema.
type StepContract ¶
type StepContract struct {
Scope string // "system" | "user" | "user-profile"
Venue int // 0=host-native, 1=container-builder, 2=skip
Gate string // "" | "allow-repo-changes" | "allow-root-tasks" | "with-services"
// Emits declares that the step produces a build-context Containerfile FRAGMENT
// (the plugin serves Invoke(OpEmit) → EmitReply.Fragment). The pod-overlay OCITarget
// bakes it; false => a deploy-only step (no build fragment — OCITarget skips it, like
// apk on an image build). F-STEP-EMIT: the BUILD leg C1 needs to externalize a step
// kind whose EmitOCI produces a Containerfile fragment.
Emits bool
}
StepContract is the SDK-facing form of the proto StepContract — a class="step" plugin's declared install-step Scope/Venue/Gate. Reverse is NOT declared (an external step's teardown ops are recorded dynamically from its OpExecute reply).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package buildkit holds the pure Containerfile render/compute machinery that any build front-end (the charly host engine, an out-of-tree build plugin) can import — the SDK-library half of the "core is the kernel; every capability is a plugin" architecture.
|
Package buildkit holds the pure Containerfile render/compute machinery that any build front-end (the charly host engine, an out-of-tree build plugin) can import — the SDK-library half of the "core is the kernel; every capability is a plugin" architecture. |
|
quadlet.go — the POD config-WRITE mechanism, relocated out of charly core (P11a).
|
quadlet.go — the POD config-WRITE mechanism, relocated out of charly core (P11a). |
|
Package enginekit is the container-engine client mechanism: the single place in the status surface that shells out to podman/docker (ps + inspect + exec) and returns structured, batch-derived ContainerSnapshots.
|
Package enginekit is the container-engine client mechanism: the single place in the status surface that shells out to podman/docker (ps + inspect + exec) and returns structured, batch-derived ContainerSnapshots. |
|
internal
|
|
|
schemagen
command
Command schemagen is the dev-time companion code generator for the `spec` package.
|
Command schemagen is the dev-time companion code generator for the `spec` package. |
|
Package kit is the importable contract a HOST-COUPLED plugin candy implements to run against charly's live check engine — the seam that lets a check verb whose logic needs the running deployment (exec-in-container, host TCP dial, host-vantage HTTP) live in its own candy module instead of charly's module.
|
Package kit is the importable contract a HOST-COUPLED plugin candy implements to run against charly's live check engine — the seam that lets a check verb whose logic needs the running deployment (exec-in-container, host TCP dial, host-vantage HTTP) live in its own candy module instead of charly's module. |
|
Package loaderkit is the importable form of charly's unified-config PARSE — the parse half of LoadUnified relocated out of charly core (P6).
|
Package loaderkit is the importable form of charly's unified-config PARSE — the parse half of LoadUnified relocated out of charly core (P6). |
|
Package schema exports the CUE schema source as an embedded FS — the single runtime origin for charly core's sharedCueSchema, the loader's base-schema splice, and the dev-time spec generation.
|
Package schema exports the CUE schema source as an embedded FS — the single runtime origin for charly core's sharedCueSchema, the loader's base-schema splice, and the dev-time spec generation. |
|
THE single schema-concatenation contract (R3).
|
THE single schema-concatenation contract (R3). |
|
Pure structural methods on the generated/aliased spec types, relocated here from package main by the CUE-single-source cutover (WF-B THE REPOINT).
|
Pure structural methods on the generated/aliased spec types, relocated here from package main by the CUE-single-source cutover (WF-B THE REPOINT). |
|
Code-assisted alias surface for the CUE-single-source cutover (WF-B THE REPOINT).
|
Code-assisted alias surface for the CUE-single-source cutover (WF-B THE REPOINT). |