go
Recipe card from the charly-internals plugin (Development — contributor internals).
This card has additional detail pages:
Go - CLI Development
Section titled “Go - CLI Development”Overview
Section titled “Overview”The charly CLI is a Go program in the charly/ directory. It uses the Kong CLI framework, go-containerregistry for OCI operations, and YAML parsing for configuration. All computation, validation, and building logic lives in Go. Taskfiles are used only for bootstrapping (building charly itself).
Reference Index
Section titled “Reference Index”| Topic | File |
|---|---|
Architecture deep dives (unified YAML loader, Schema Driven Design pipeline + generation-coverage catalog, the sdk/spec package, namespace/remote-layer resolvers, Capabilities, the k8s/VM external substrates, YAML↔Go conventions, Kong parent+leaf commands, mode purity, the InstallPlan IR, the VM-path module topology, self-exec coordination) and the full file-by-file Source Code Map |
references/source-map.md |
The step-by-step recipe for changing the charly.yml schema (CUE is the single source of truth) |
references/schema-change-recipe.md |
Design notes for the Go-side architecture not obvious from reading the source cold (Kong flag-namespace collision, env-var proxy for parent-flag detection, the yaml.v3 Node API, scalar-to-sequence upgrades, the path-traversal guard, the two-step project-dir resolver) |
references/implementation-insights.md |
Quick Reference
Section titled “Quick Reference”| Action | Command | Description |
|---|---|---|
| Build | task build:binary |
Compile to bin/charly (CalVer-stamped), NO install. Also copies to candy/charly/bin/charly — does NOT touch the tracked pkg/arch/PKGBUILD (that file is read only by the containerized charly box pkg native-package build) |
| Package | task pkg:arch / pkg:fedora / pkg:debian / pkg:all |
Build a distro-native .pkg.tar.zst/.rpm/.deb release artifact into dist/, containerized via charly box pkg. Install it yourself with your OWN package manager (pacman -U/dnf install/apt install) |
| Install (portable) | task install-portable |
Copy bin/charly to $HOME/.local/bin/charly (solo bootstrap; NOT a multi-teammate dev-loop step — see below) |
| Run tests | cd charly && go test ./... |
Run all tests |
| Run specific test | cd charly && go test -run TestName ./... |
Run single test |
| Vet | cd charly && go vet ./... |
Static analysis |
| Format | cd charly && gofmt -w . |
Format code |
In a multi-teammate / multi-worktree setup, NO Taskfile target installs to the
host during in-flight work — use task build:binary per worktree instead; see
/charly-internals:agents “The charly binary in a multi-teammate /
multi-worktree setup” for the full discipline.
Project Directory Structure
Section titled “Project Directory Structure”project/├── bin/charly # Built by `task build:binary` (gitignored)├── charly/ # Go module (kong CLI, go-containerregistry)│ └── charly.yml # The binary's embedded default config (//go:embed,│ # embed_defaults.go): distro/builder/init/resource│ # build vocabulary + the sidecar: template library.│ # Parsed by the SAME unified loader as any project│ # charly.yml; a project ships none of it.├── sdk/ # Git submodule (github.com/opencharly/sdk) — the plugin│ # contract module: root package sdk, sdk/kit, sdk/spec,│ # sdk/proto, sdk/schema/*.cue, sdk/schemaconcat, sdk/vmshared;│ # its own Taskfile owns `task cue:gen` + `task proto:gen`├── .build/ # Generated Containerfiles (gitignored)├── charly.yml # Image definitions├── Taskfile.yml # Bootstrap tasks only├── taskfiles/ # Build.yml, Cue.yml, Setup.yml├── candy/<name>/ # Layer directories (160 layers)├── plugins/ # Git submodule (opencharly/plugins)└── templates/ # supervisord.header.conf (referenced by init.supervisord.header_file)Submodule convention: plugins/ is a submodule rooted at the
opencharly/plugins repo, and sdk/ is a submodule rooted at the
github.com/opencharly/sdk repo. Clone with --recurse-submodules or run
git submodule update --init after a plain clone. See
/charly-internals:skills for the skill-authoring and sync conventions.
Go Module Info
Section titled “Go Module Info”- Go version: 1.26.0
- Key dependencies:
kong(CLI),go-containerregistry(OCI), andgithub.com/opencharly/sdk(the plugin contract module — required withreplace github.com/opencharly/sdk => ../sdkfor in-tree resolution). The credential store’sgo-keyring(Secret Service API) is NOT a core dependency — it links only into the out-of-processcandy/plugin-secretsplugin (the C2 dep-shed) - Module path:
charly/go.mod
Common Workflows
Section titled “Common Workflows”Add a New CLI Command
Section titled “Add a New CLI Command”- Define command struct in appropriate file (or new file)
- Add to CLI struct in
main.go - Implement
Run()method - Add tests in
*_test.go - Build and test:
cd charly && go test ./... && go build -o ../bin/charly .
Add a New Validation Rule
Section titled “Add a New Validation Rule”A host-natural check that needs the raw loader goes in charly/validate.go; the
per-kind/op/candy/graph rule engine lives in the compiled-in command:box plugin
(candy/plugin-box) over the resolved-project envelope — add per-entity/op rules
there.
See references/schema-change-recipe.md for the full recipe when the change touches the charly.yml schema itself (CUE is the single source of truth).
Debug a Build Issue
Section titled “Debug a Build Issue”# Generate Containerfiles without buildingbin/charly box generate
# Inspect generated outputcat .build/<image>/Containerfile
# Validate configurationbin/charly box validate
# Inspect resolved image configbin/charly box inspect <image>Intermediate image cache invalidation
Section titled “Intermediate image cache invalidation”charly box build auto-generates intermediate images (e.g., ghcr.io/opencharly/charly-fedora-2-dbus-nodejs) that bundle the charly layer plus common layers for cache reuse across many downstream images. These intermediates are aggressively podman-cached. Updating candy/charly/bin/charly does invalidate the COPY step inside the intermediate, but if the intermediate tag already exists locally, charly box build may reuse it without re-running the build chain. To force a fresh binary propagation after a manual bin/charly update:
charly clean --invalidate 'charly-fedora-2*'charly box build <image>This also interacts with the dual-path gotcha documented in /charly-tools:charly: bin/charly (repo-root, used by host-side invocations) and candy/charly/bin/charly (what the charly candy actually copies into images) must stay in sync. The canonical task build:binary path does both; a manual go build -o bin/charly ./charly needs an explicit cp bin/charly candy/charly/bin/charly follow-up.
R9 — deployed binary matches source; runtime deps live in the PKGBUILD
Section titled “R9 — deployed binary matches source; runtime deps live in the PKGBUILD”See the project rulebook’s R9 mandate (CLAUDE.md/AGENTS.md). Applied to the charly toolchain:
- Syncing source does not rebuild the binary. Syncthing / git / rsync move
source between hosts. After pushing code, rebuild on the target —
task build:binaryin that checkout — and verify./bin/charly versionmatches what you built — if the version is old, the fix under test isn’t really under test. The freshness guard (/charly-internals:agents“The charly binary in a multi-teammate / multi-worktree setup”) catches a stale invoked binary against newercharly/*.goin the same tree, but the version check is still the explicit proof — the per-worktree-vs-host-package split lives there too. - Every runtime OS dependency goes into
pkg/arch/PKGBUILDdepends=— the single source of truth (nc,socat,xorriso,qemu-guest-agent, …); thepkg/fedora/pkg/debianpackaging mirrors it. A manual install on one host is a bug report disguised as a fix — it won’t survive a fresh install on a synced host.
The verification side (checking the deployed binary + deps on a live target)
is /charly-check:check Standards 7–8; the dual-path bin/charly ↔
candy/charly/bin/charly gotcha is above and in /charly-tools:charly.
Style Guide
Section titled “Style Guide”- All logic belongs in Go. Taskfiles are only for bootstrap (building charly).
- Taskfiles for bootstrap only, Go for all other logic.
- Test files alongside source files (
foo.go->foo_test.go).
Cross-References
Section titled “Cross-References”/charly-internals:generate-source— Understanding generated Containerfiles + deep dive on the task emission pipeline (charly/tasks.go)./charly-image:layer— Canonical author-facing reference for the task verb catalog thatcharly/tasks.goimplements./charly-build:validate— Validation rules and error handling (validateCandyTasksincandy/plugin-box/validate_rules.go)./charly-build:build— Using the built CLI./charly-check:check— Author-facing reference for the declarative-testing feature thatcheckspec.go/checkrun.go/checkrun_verbs.go/checkrun_charly_verbs.go/description_collect.go/check_cmd.go/check_endpoint_resolve.go(the host-endpoint reverse-legs) implement — plussdk/kit/checkvars.goandsdk/kit/local_image.go(moved out of core in P12a). (Op-level check validation moved out of core tocandy/plugin-box/validate_check.go.)/charly-build:charly-mcp-cmd— Author-facing reference for both (a) the declarativemcp:client check verb (method catalog, URL-rewrite behavior, port-publishing gotcha, transport dispatch — served out-of-process bycandy/plugin-mcp, which resolves its endpoint via thecheck_endpoint_resolve.goreverse-legs) and (b) thecharly mcp serveserver (externalized tocandy/plugin-mcpcommand:mcp: one tool per CLI leaf, auto-generated from thecharly __cli-modelreflection seam, destructive-hint +--read-onlyfilter, Streamable-HTTP + stdio transports, auto-fallback toopencharly/charly— pair withcli_model_cmd.go+main_repo.go+box_fetch_reentry.go+candy/plugin-authoring+sdk/kit/yaml.goinreferences/source-map.md)./charly-coder:charly-mcp— The candy that deployscharly mcp serveinside a container: bind-mount volume NAMEprojectat the container PATH/workspace,CHARLY_PROJECT_DIR=/workspaceso build-mode MCP tools (box.list.boxes,box.inspect, etc.) reachcharly.ymlfrom outside the project checkout — or auto-fall back toopencharly/charlywhen/workspaceis empty (the fallback fires on absence of charly.yml, not absence of CHARLY_PROJECT_DIR)./charly-check:wl,/charly-check:cdp,/charly-check:vnc, and/charly-check:dbusare out-of-process verbs served bycandy/plugin-wl/candy/plugin-cdp/candy/plugin-vnc/candy/plugin-dbus(cdp/vnc resolve their endpoints via thecheck_endpoint_resolve.goreverse-legs;wl/dbusare EXEC-based and reach the venue over the executor).- Source:
charly/directory (~304 source + ~294 test .go files).
When to Use This Skill
Section titled “When to Use This Skill”MUST be invoked before reading or modifying Go source files. Invoke this skill BEFORE launching Explore agents on charly/ code.
Live-deploy verification: see /charly-check:check (the 10 Testing Standards) and /charly-internals:disposable.