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 BuildCLIModel(root any, name, version, prefix string, options ...kong.Option) (*spec.CLIModel, error)
- 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 CopyChannel(dst interface{ ... }, src interface{ ... }) error
- func DecodeDeployVenue(envJSON []byte) (spec.DeployVenue, error)
- func DecodeGeneratedJSON(definition string, payload []byte, dst any) error
- func DecodeInstallPlans(paramsJSON []byte) ([]spec.InstallPlanView, error)
- func IsServeMode() bool
- func KongLeafToCLILeaf(leaf *kong.Node) spec.CLILeaf
- 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 OpenProviderChannel(ctx context.Context, client pb.ProviderClient, open *pb.ChannelFrame) (pb.Provider_ChannelClient, error)
- func ParseInProcCLI(name string, cli any, args []string, opts ...kong.Option) (done bool, err error)
- 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 ReceiveChannelOpen(stream ProviderChannel) (*pb.ChannelFrame, error)
- func RelayChannel(upstream ProviderChannel, downstream interface{ ... }) error
- 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 RunInProcCLI(name string, cli any, args []string, opts ...kong.Option) error
- func Serve(providerSrv pb.ProviderServer, metaSrv pb.PluginMetaServer)
- func ServeCheckVerb(kv kit.CheckVerbProvider, meta pb.PluginMetaServer)
- func ValidateGenerated(definition string, value any) error
- func VerbVerdict(verb, method, out string, runErr error, op *spec.Op, artifact bool) (*pb.InvokeReply, error)
- type CLISubcommand
- type ChannelProvider
- 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) RunInteractive(ctx context.Context, script string) (int, error)
- func (e *Executor) RunStream(ctx context.Context, script string) (int, 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 ExitCodeError
- type InvokeProviderOpts
- type Matcher
- type ProvidedCapability
- type ProviderChannel
- type ReplayBuffer
- type SchemaValidator
- type SequenceGate
- type StepContract
Constants ¶
const ( ChannelOpen = "open" ChannelStdin = "stdin" ChannelStdout = "stdout" ChannelStderr = "stderr" ChannelTerminal = "terminal" ChannelStatus = "status" ChannelResize = "resize" ChannelSignal = "signal" ChannelAck = "ack" ChannelCancel = "cancel" ChannelExit = "exit" ChannelError = "error" ChannelResync = "resync" )
Channel frame kinds are transport vocabulary, not runtime semantics. Runtime- specific events travel as CUE-generated JSON in ChannelFrame.PayloadJson.
const ( CheckFailExitCode = 2 CheckSkippedExitCode = 3 )
The check-command exit-code convention (goss/pytest 0/1/2/3), single-sourced here so both the HOST (main()'s exit mapping + `charly box feature run`) and candy/plugin-check reference ONE contract:
0 all checks passed 1 infra / usage error — no pass/fail verdict was produced (the host default) 2 the check RAN and one or more checks FAILED 3 the bed was SKIPPED (a required host prerequisite — e.g. a GPU — is absent)
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) // OpCompile is the K4-B deploy-COMPILE selector (command:bundle): the host's // deployAddCmd.compileNodePlans computes the per-node selection and Invokes the // command:bundle plugin's OpCompile with a spec.DeployCompileRequest; the plugin // re-hydrates the resolved-project envelope (HostBuild("resolved-project")) + // loops deploykit.BuildDeployPlan, returning []spec.InstallPlanView the host // re-materializes. A generic action selector (never a provider word — F11). OpCompile = "compile" // 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/VenueExecutor // 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: NON-interactive in-container exec CAPTURE (charly service — output-in-reply); interactive shell is OpAttach OpAttach = "attach" // F12 lifecycle: LIVE-STDIO attach — charly shell (-it TTY) + charly cmd (-i, stdin piped). The plugin exec.RunInteractive's a host-resolved #PodLiveStdioPlan.script; the host reverse-server holds the operator's terminal (stdio never crosses the wire) 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" // OpConfigSetup / OpConfigRemove are the P13-KERNEL config-BODY selectors: the deploy:pod // plugin's Invoke handles these carrying #PodConfigSetupRequest / #PodConfigRemoveRequest // VERBATIM as Params — the direction-flip counterpart of OpConfigWrite (which stayed // host-initiated/plugin-rendered): host_build_pod_config.go's hostBuildPodConfigSetup/ // hostBuildPodConfigRemove now FORWARD onward to the plugin (resolve the deploy:pod provider + // InvokeWithExecutor, the SAME primitive InvokeProvider/grpcSubstrateLifecycle use) instead of // running the ported BoxConfigSetupCmd/BoxConfigRemoveCmd orchestration in-core. The plugin // calls back the narrow "pod-config-*" HostBuild seams (sdk/schema/seam.cue) for the // genuinely host/loader/registry-coupled sub-steps. OpConfigSetup = "config-setup" OpConfigRemove = "config-remove" OpStatusCollect = "status-collect" // command:status: programmatic status collection → []spec.DeploymentStatus (distinct from lifecycle OpStatus) // OpStatusCollectAll is the K6 whole-subsystem status FAN-OUT + deploy-cone ENRICHMENT // selector: verb:status-fanout (candy/plugin-substrate) serves it, taking a // spec.StatusSubstrateRequest and returning a spec.StatusSubstrateReply — the SAME wire // shape the "status-substrate" HostBuild seam already carries. The host's thin forward // (charly/status_substrate_host.go) is pure dispatch (no status business logic); the // plugin owns the fan-out (calling its own per-word OpStatusCollect handlers directly, an // in-package call — no registry needed for that leg), the pod/vm deploy-cone enrichment // (kit.ExtractMetadata/kit.ResolveBoxName/deploykit.QuadletDir/deploykit. // ResolveBoxEngineForDeploy — all sdk-portable), and the sort. Distinct from // OpStatusCollect (the single-word per-substrate collector op the SAME provider ALSO // serves on kind:pod/vm/k8s/local/android). OpStatusCollectAll = "status-collect-all" // 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" // OpEphemeralRegister / OpEphemeralTeardown are the command:bundle EPHEMERAL-LIFECYCLE // selectors (FINAL/K5 unit 6a): the host Invokes these as the first action of an ephemeral // deploy's Add and the last action of its Del, mirroring OpCompile's host→plugin dispatch // shape (a generic action selector, never a provider word — F11). OpEphemeralRegister = "ephemeral-register" OpEphemeralTeardown = "ephemeral-teardown" // OpDeployDispatch is the command:bundle S3b selector: the ONE generic host→plugin envelope // every UnifiedDeployTarget/LifecycleTarget method (Add/Update/Del/Test/Start/Stop/Status/ // Logs/Shell/Attach/Rebuild) dispatches through, discriminated by // spec.DeployTargetDispatchRequest.Op — a generic action selector (never a provider word, // F11), mirroring OpCompile's shape but carrying ELEVEN former methods through ONE wire pair // instead of eleven (R3 — the project rulebook's "generic over ad-hoc"). Core's thin // ResolveTarget proxy (charly/unified_targets.go) Invokes this; candy/plugin-bundle's handler // switches on Op and reaches the ACTUAL deploy-substrate provider (pod/vm/local/k8s/android) // via its OWN sdk.Executor.InvokeProvider (S1) — core never talks to the substrate directly // once this lands. OpDeployDispatch = "deploy-dispatch" // EphemeralPanicMarker prefixes an error the command:bundle plugin converts from a // RECOVERED PANIC inside OpEphemeralRegister/OpEphemeralTeardown (RCA #5, FINAL/K5 unit 6a — // a nil-map write panic in persistEphemeralRuntime was previously UNRECOVERED and vanished // silently: the deploy still reported PASS). A STRING marker (not a typed Go error) because // this classification must survive the Provider.Invoke wire boundary dual-placement demands // stay portable — command:bundle is compiled-in TODAY, but the SAME code must behave // identically if ever served out-of-process, where only a string error crosses gRPC. The // host-side caller (charly's registerEphemeralIfMarked) checks for this marker to distinguish // a PANIC (a genuine bug — must FAIL the whole deploy Add, never silently continue) from an // ORDINARY registration error (e.g. systemd-run missing — an expected condition that stays a // soft, logged warning, unchanged). EphemeralPanicMarker = "ephemeral op panic:" )
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 = 2
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 BuildCLIModel ¶ added in v0.2026200.1312
func BuildCLIModel(root any, name, version, prefix string, options ...kong.Option) (*spec.CLIModel, error)
BuildCLIModel reflects a Kong command tree into the generated #CLIModel. Prefix is a dotted command path prepended to every leaf (for example "agent" when a command plugin reflects only its owned subtree).
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 CopyChannel ¶ added in v0.2026200.1312
func CopyChannel(dst interface{ Send(*pb.ChannelFrame) error }, src interface {
Recv() (*pb.ChannelFrame, error)
}) error
CopyChannel relays frames until EOF or cancellation. It is intentionally a byte-preserving transport primitive; it does not inspect agent or terminal payloads.
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 DecodeGeneratedJSON ¶ added in v0.2026200.1312
DecodeGeneratedJSON strictly decodes one persisted or received JSON value into its generated Go type, then validates that typed value against the authoritative CUE definition. Typed decoding is required for fields such as []byte, whose standard JSON representation is base64 text but whose CUE value is bytes. Unknown fields and trailing JSON values are rejected before CUE validation so decoding cannot silently discard persisted input.
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 KongLeafToCLILeaf ¶ added in v0.2026200.1312
KongLeafToCLILeaf converts one Kong leaf into the generated wire shape.
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 OpenProviderChannel ¶ added in v0.2026200.1312
func OpenProviderChannel(ctx context.Context, client pb.ProviderClient, open *pb.ChannelFrame) (pb.Provider_ChannelClient, error)
OpenProviderChannel starts a generated Provider.Channel stream and sends its mandatory open frame. The returned stream is ready for concurrent Send/Recv, as supported by gRPC.
func ParseInProcCLI ¶ added in v0.2026195.204
func ParseInProcCLI(name string, cli any, args []string, opts ...kong.Option) (done bool, err error)
ParseInProcCLI parses args into cli WITHOUT running any leaf — the entry for a command plugin that dispatches MANUALLY after parsing (it reads the populated struct itself rather than relying on kong's Run()). It returns done=true when kong printed --help/--version: the caller MUST return nil immediately and NOT proceed to its post-parse logic (otherwise `charly <cmd> --help` would run the command's action on default flags). A parse error is returned as err.
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 ReceiveChannelOpen ¶ added in v0.2026200.1312
func ReceiveChannelOpen(stream ProviderChannel) (*pb.ChannelFrame, error)
ReceiveChannelOpen reads and validates the mandatory first frame. The request id, provider class/word, and operation are required so every later frame can be correlated without inspecting runtime-specific payloads.
func RelayChannel ¶ added in v0.2026200.1312
func RelayChannel(upstream ProviderChannel, downstream interface { ProviderChannel CloseSend() error }) error
RelayChannel connects a controller-side ProviderChannel to a downstream gRPC channel with ordered half-close semantics. Provider output is the evidence writer: once that direction ends, no later frame can mutate controller state and the relay may return. If controller input ends first, CloseSend delivers the protocol EOF and the relay drains provider output before returning. This prevents a completed command from racing its successor's durable cursor.
Cancellation ownership: when the provider-output direction finishes first, RelayChannel returns while the input-copy goroutine may still be blocked in upstream.Recv(). The CALLER owns upstream's lifecycle — after return, cancel the context upstream carries (or close upstream) to release that goroutine.
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 RunInProcCLI ¶ added in v0.2026195.204
RunInProcCLI parses args into the kong grammar cli and runs the selected leaf's Run() — the entry for a command plugin whose grammar carries Run() leaves (the common case). On `--help`/`--version` it prints and returns nil WITHOUT running any leaf; a non-zero kong exit becomes *ExitCodeError; a parse error propagates. Pass any extra kong.Option (kong.Description, kong.Bind, …) via opts — kong.Name + the exit-sentinel are supplied for you.
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 ValidateGenerated ¶ added in v0.2026200.1312
ValidateGenerated validates a generated SDK value against its authoritative CUE definition. Command plugins use the same embedded schema as core, so moving command ownership never creates a hand-maintained validation copy.
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 CLISubcommand ¶ added in v0.2026201.542
CLISubcommand is one DECLARED child of a class="command" capability's own CLI word — see ProvidedCapability.Subcommands.
func KongSubcommands ¶ added in v0.2026201.542
func KongSubcommands(v any) []CLISubcommand
KongSubcommands walks a Kong-tagged struct (or pointer to one) ONE level deep and returns its named `cmd:""` children as a CLISubcommand catalog: Name from the field's `name:` tag when present, otherwise the SAME kebab-cased field name Kong itself computes as the default command name (RDD-spiked: Kong's `cmd:"<value>"` tag VALUE is never read as a name — a `cmd` tag is a pure presence marker — so a value-carrying `cmd:"foo"` with no separate `name:` tag does NOT mean the command is named "foo"; replicating the fallback here keeps a declared subcommand's NAME byte-identical to what Kong actually dispatches). Help comes from the field's `help:` tag. A field tagged `hidden:""` is skipped — machinery subcommands stay invisible to `--help` and MCP exactly as in the plugin's own internal grammar. A field with no `cmd` tag key at all (kong requires the key to be PRESENT, regardless of value, to mark a subcommand) is skipped too.
type ChannelProvider ¶ added in v0.2026200.1312
type ChannelProvider interface {
OpenChannel(open *pb.ChannelFrame, stream ProviderChannel) error
}
ChannelProvider is the optional streaming extension to Provider. The first frame has already been validated as an open frame and remains available as open; subsequent controller frames arrive through stream. Domain payloads are generated from CUE and carried in open.PayloadJson.
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, opts InvokeProviderOpts) ([]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 (lazily connecting it from the project's candy closure on a miss, S2) and Invokes it, returning the raw result JSON. params/env are the op's plugin_input / env (nil for none). opts carries the optional S1 venue descriptor + the optional S3b canonical-ref fallback; the zero value InvokeProviderOpts{} reproduces the pre-S1 behavior exactly.
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) RunInteractive ¶ added in v0.2026195.501
RunInteractive runs a command on the venue wired to the operator's LIVE TTY (F12): the HOST reverse-server runs it inheriting os.Stdin/os.Stdout/os.Stderr (= the operator's terminal), so stdio never crosses the wire — only script→exit. Blocks until the session ends. The live-stdio sibling of RunCapture (consumers: charly shell/-cmd via the plugin's OpAttach).
func (*Executor) RunStream ¶ added in v0.2026195.501
RunStream runs a command on the venue streaming stdout/stderr LIVE to the operator (F12; no stdin). Blocks until exit. Consumer: charly logs --follow via the plugin's OpLogs.
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 ExitCodeError ¶ added in v0.2026194.1605
ExitCodeError carries a specific PROCESS exit code from a command plugin's Invoke(OpRun) back to the host, which maps it to os.Exit(Code). A compiled-in command candy returns its error verbatim through the in-proc dispatch, but the host cannot classify the plugin's OWN error TYPES across the module boundary — so a command that must set a NON-1 exit code (the check 0/1/2/3 convention) wraps its failure in *ExitCodeError, which the host detects with errors.As and honors as the exit status. Code 0 falls back to the host's default error handling (no special code).
func (*ExitCodeError) Error ¶ added in v0.2026194.1605
func (e *ExitCodeError) Error() string
func (*ExitCodeError) Unwrap ¶ added in v0.2026194.1605
func (e *ExitCodeError) Unwrap() error
type InvokeProviderOpts ¶ added in v0.2026203.438
type InvokeProviderOpts struct {
// VenueDescriptor optionally supplies a SELF-DESCRIBED venue (S1 — the
// venue-scoped-executor-session seam): the host re-materializes it into a FRESH DeployExecutor
// and threads THAT onto the target's InvokeWithExecutor instead of the caller's own executor.
// Use this when the calling plugin holds no enclosing executor of its own (e.g. a verb/kind
// Invoke with no deploy-context broker) but still wants the target Invoked WITH a live venue.
// Nil (the default) — no descriptor; the caller's own executor, if any, is forwarded unchanged.
VenueDescriptor *spec.VenueDescriptor
// ExtraRef optionally supplies a canonical candy ref (S3b — the Pass-2 lazy-connect gap) for
// the host's S2 lazy-connect fallback: connectPluginByWordRef(class, word, ExtraRef). Empty
// (the default) only ever reaches Pass-1 (the calling project's own candy closure) — a target
// declared nowhere in that closure but resolvable via an explicit @github canonical ref (the
// same Pass-2 fetch the credential/vm/kube host adapters already use) needs this set.
ExtraRef string
}
InvokeProviderOpts carries the OPTIONAL extras to an InvokeProvider peer-dispatch call. The zero value is byte-identical to the pre-S1 behavior: no venue descriptor, so the host threads the CALLING plugin's own enclosing executor (if any) onto the target — exactly as before this field existed.
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
// Subcommands is set ONLY for Class=="command" (F-CLI-NEST): the plugin's DECLARED
// one-level-deep CLI subcommand catalog (name+help). The host uses it to build a REAL
// nested Kong grammar — a named `cmd:""` child per entry, restoring `--help` fidelity
// and CLI-model (MCP) leaf discoverability — in place of the opaque `[<args>...]`
// pass-through holder every command-class capability otherwise gets. Empty (the
// default) preserves today's flat pass-through behavior byte-for-byte; use
// KongSubcommands to derive the catalog from an existing Kong-tagged struct instead of
// hand-duplicating it.
Subcommands []CLISubcommand
// CommandModel is set ONLY for Class=="command". Its generated #CLIModel
// describes the plugin-owned leaf grammar for host and MCP reflection.
CommandModel *spec.CLIModel
}
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 ProviderChannel ¶ added in v0.2026200.1312
type ProviderChannel interface {
Context() context.Context
Send(*pb.ChannelFrame) error
Recv() (*pb.ChannelFrame, error)
}
ProviderChannel is the common subset of the generated client and server streams. It lets in-process and gRPC providers share one channel handler.
type ReplayBuffer ¶ added in v0.2026200.1312
type ReplayBuffer struct {
// contains filtered or unexported fields
}
ReplayBuffer is a bounded, acknowledgement-aware frame history for detach / reconnect. Bounds are enforced by both frame count and protobuf byte size. When an unacknowledged frame would be evicted, Add fails loudly; callers must preserve evidence and enter the incident/RCA workflow rather than hide loss.
func NewReplayBuffer ¶ added in v0.2026200.1312
func NewReplayBuffer(maxFrames, maxBytes int) *ReplayBuffer
func (*ReplayBuffer) Acknowledge ¶ added in v0.2026200.1312
func (b *ReplayBuffer) Acknowledge(sequence uint64)
func (*ReplayBuffer) Add ¶ added in v0.2026200.1312
func (b *ReplayBuffer) Add(frame *pb.ChannelFrame) error
func (*ReplayBuffer) ReplayFrom ¶ added in v0.2026200.1312
func (b *ReplayBuffer) ReplayFrom(sequence uint64) ([]*pb.ChannelFrame, error)
type SchemaValidator ¶ added in v0.2026200.1312
type SchemaValidator struct {
// contains filtered or unexported fields
}
SchemaValidator validates plugin-owned generated values against the same embedded CUE source that the plugin publishes through Describe.
func NewSchemaValidator ¶ added in v0.2026200.1312
func NewSchemaValidator(schemaFS fs.FS, dir string) (*SchemaValidator, error)
NewSchemaValidator compiles one self-contained embedded plugin schema.
func (*SchemaValidator) Validate ¶ added in v0.2026200.1312
func (v *SchemaValidator) Validate(definition string, value any) error
Validate checks a value against a named definition in the compiled schema.
func (*SchemaValidator) ValidateJSON ¶ added in v0.2026200.1312
func (v *SchemaValidator) ValidateJSON(definition string, payload []byte) error
ValidateJSON validates the original JSON bytes without first decoding JSON numbers through float64-backed map[string]any values.
type SequenceGate ¶ added in v0.2026200.1312
type SequenceGate struct {
// contains filtered or unexported fields
}
SequenceGate rejects duplicates, regressions, and gaps. A provider can turn a gap into ChannelResync using ReplayBuffer.ReplayFrom; it must never silently reorder process or terminal output.
func NewSequenceGate ¶ added in v0.2026200.1312
func NewSequenceGate(first uint64) *SequenceGate
func (*SequenceGate) Accept ¶ added in v0.2026200.1312
func (g *SequenceGate) Accept(frame *pb.ChannelFrame) error
func (*SequenceGate) Expected ¶ added in v0.2026200.1312
func (g *SequenceGate) Expected() uint64
Expected returns the next sequence without advancing the gate.
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 agentkit implements transport-independent control-plane invariants.
|
Package agentkit implements transport-independent control-plane invariants. |
|
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. |
|
wiregen
command
Command wiregen makes CUE the author of record for Charly's protobuf transport contract.
|
Command wiregen makes CUE the author of record for Charly's protobuf transport contract. |
|
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. |
|
directives.go — the loaderkit-local WALK-only directives: MaxIncludeDepth, namespaceAliasRe, and validateNamespaceAlias.
|
directives.go — the loaderkit-local WALK-only directives: MaxIncludeDepth, namespaceAliasRe, and validateNamespaceAlias. |
|
Package proclifecycle is a stdlib-only leaf package (no deps beyond os/ os/signal/path/filepath/strings/sync/syscall/time) holding generic host-process lifecycle plumbing: catchable-signal-triggered cleanup hooks and a stale-temp-file sweep.
|
Package proclifecycle is a stdlib-only leaf package (no deps beyond os/ os/signal/path/filepath/strings/sync/syscall/time) holding generic host-process lifecycle plumbing: catchable-signal-triggered cleanup hooks and a stale-temp-file sweep. |
|
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). |
|
Package targetkit provides transport-neutral gRPC connections to Charly targets.
|
Package targetkit provides transport-neutral gRPC connections to Charly targets. |
|
Package testkit provides disposable live protocol fixtures shared by SDK consumers.
|
Package testkit provides disposable live protocol fixtures shared by SDK consumers. |
|
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). |