sdk

package module
v0.2026203.1311 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2026 License: MIT Imports: 31 Imported by: 0

README

opencharly/sdk — the OpenCharly plugin SDK + contract repo

The single module every OpenCharly plugin imports — and the CONTRACT charly core itself consumes. It owns:

  • / (package sdk) — the go-plugin serve/handshake surface (Serve, ServeCheckVerb, Main, Handshake, ProtocolVersion), the executor reverse-channel client (Executor), capability building (BuildCapabilities, ProvidedCapability, StepContract), the streaming channel primitives (RelayChannel, SequenceGate, ReplayBuffer), and shared verb/deploy helpers.
  • protocol/schema/ — authoritative CUE model for every protobuf message, field, service, and streaming method (PluginMeta, Provider, ExecutorService, CheckContextService). proto/ is generated output only; regenerate with task wire:gen.
  • spec/ — the GENERATED Go param + wire types (cue exp gengotypes over schema/), plus the hand-written union/alias/method files. Never hand-edit the *_gen.go files; run task cue:gen.
  • schema/ — the CUE schema source (single source of truth for the charly.yml ingress schema) exported as an embedded FS (schema.FS).
  • kit/ — pure helpers for plugin authors (check-verb contract, deploy walk, shell/render/calver utilities, the shared process launch/lifecycle helpers). Imports only stdlib (+ x/sys/unix) + spec + vmshared + yaml.
  • agentkit/ — the agent control plane: transport-independent workflow invariants (Workflow) and the daemon-free durable record store (Store).
  • targetkit/ — transport-neutral gRPC connections to Charly targets (DialProvider/DialProcessProvider over exec/SSH stdio processes; ServeStdio on the target side).
  • schemaconcat/ — the ONE schema-concatenation contract shared by the runtime validator, the loader's base++plugin splice, and dev-time codegen.
  • testkit/ — disposable live protocol fixtures shared by SDK tests and consumers (e.g. the in-process SSH process server).
  • internal/wiregen/ — the CUE→protobuf generator behind task wire:gen (plus the bootstrap importer the model was imported with).
  • vmshared/ — VM rendering + orchestration types shared by charly core and the VM-facing plugins (libvirt YAML/XML, qemu argv, cloud-init, OVMF, SMBIOS, ssh client/tunnel helpers).

Versioning

Go module tags follow v0.<YYYYDDD>.<HHMM with leading zeros stripped>, derived from the same UTC CalVer the superproject uses for its v<YYYY.DDD.HHMM> release tags (which are NOT valid Go module versions). Mapping example: superproject v2026.185.0751 ⇄ sdk v0.2026185.751. Tags are immutable and add-only; minor (YYYYDDD) and patch (minutes-of-day) sort chronologically under semver comparison.

The plugin PROTOCOL gates are carried separately: sdk.ProtocolVersion (the go-plugin handshake) and the schema CalVer (kit.LatestSchemaVersion(), generated from schema/version.cue, advertised in Capabilities.calver).

Regeneration

  • task cue:gen — the full CUE-owned regeneration: chains wire:gen first, then regenerates spec/{cue_types_gen,vocab_gen,version_gen}.go from schema/*.cue (self-bootstraps the pinned cue CLI into ./bin/cue). Reproducibility-gated by TestGenReproducible.
  • task wire:gen — regenerates proto/plugin.proto and the Go stubs from protocol/schema/*.cue (self-bootstraps the pinned protoc + Go codegen plugins into ./bin).
  • task proto:gen — compatibility alias for wire:gen.

Consumers

  • charly core (github.com/opencharly/charly/charly) requires this module and mounts this repo as the sdk/ git submodule (in-tree builds resolve via replace github.com/opencharly/sdk => ../sdk).
  • Every plugin candy (candy/plugin-* in the charly monorepo, and any out-of-tree plugin) requires ONLY this module — never charly core. An out-of-tree plugin requires a tagged version; in-monorepo candies use replace github.com/opencharly/sdk => ../../sdk.

Authoring reference: the /charly-internals:plugin skill in the opencharly/plugins marketplace.

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

View Source
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.

View Source
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)
View Source
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.

View Source
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.

View Source
const DispenseKey = "charly"

DispenseKey is the single go-plugin plugin name; charly serves/dispenses ONE gRPC plugin exposing the uniform Provider + PluginMeta services.

View Source
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

View Source
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.

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

func BuildDeployReply(reverseOps []spec.ReverseOp, candy, version string) (*pb.InvokeReply, error)

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

func ContextWithExecutor(ctx context.Context, e *Executor) context.Context

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

func DecodeGeneratedJSON(definition string, payload []byte, dst any) error

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

func KongLeafToCLILeaf(leaf *kong.Node) spec.CLILeaf

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

func MatchAll(value string, matchers []Matcher) error

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

func MatchValueString(v any) string

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

func MatchValueStrings(v any) []string

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

func NormalizePhase(p string) string

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

func OpModifierZero(op *spec.Op, name string) bool

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

func PluginScriptReverseOp(scope spec.Scope, script string) spec.ReverseOp

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

func Preview(s string) string

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

func RequireModifiers(method string, op *spec.Op, required map[string][]string) error

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

func RunArtifactValidators(op *spec.Op) error

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

func RunInProcCLI(name string, cli any, args []string, opts ...kong.Option) error

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

func ValidateGenerated(definition string, value any) error

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

type CLISubcommand struct {
	Name string
	Help string
}

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

func ExecutorForInvoke(ctx context.Context, brokerID uint32) (*Executor, error)

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

func ExecutorFromInvoke(brokerID uint32) (*Executor, error)

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

func (e *Executor) GetFile(ctx context.Context, path string, asRoot bool) ([]byte, error)

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

func (e *Executor) HostArbiter(ctx context.Context, action string, params []byte) ([]byte, error)

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

func (e *Executor) HostBuild(ctx context.Context, kind string, spec []byte) ([]byte, error)

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

func (e *Executor) RunInteractive(ctx context.Context, script string) (int, error)

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

func (e *Executor) RunStream(ctx context.Context, script string) (int, error)

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

func (e *Executor) RunSystem(ctx context.Context, script string, optsJSON []byte) error

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) RunUser

func (e *Executor) RunUser(ctx context.Context, script string, optsJSON []byte) error

RunUser runs an unprivileged script on the venue (see RunSystem).

func (*Executor) Venue

func (e *Executor) Venue(ctx context.Context) (string, error)

Venue returns the host executor's stable venue identifier.

func (*Executor) VenueCapture

func (e *Executor) VenueCapture(ctx context.Context, script string) (string, error)

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

func (e *Executor) VenueHasTool(ctx context.Context, tool string) bool

VenueHasTool reports whether tool is on PATH on the venue — an EXEC-based check verb's tool-presence probe over the reverse channel.

func (*Executor) VenueRunSilent

func (e *Executor) VenueRunSilent(ctx context.Context, script string) error

VenueRunSilent runs a command on the venue discarding output, returning an error on a non-zero exit — an EXEC-based check verb's fire-and-forget helper over the reverse channel.

type ExitCodeError added in v0.2026194.1605

type ExitCodeError struct {
	Code int
	Err  error
}

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

type Matcher = spec.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).

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).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL