Skip to content

charly

Recipe card from the charly-tools plugin (Images — the deployable catalog).

Property Value
Install files run: step, bin/charly

The charly binary inside containers provides the full charly CLI for in-venue scripting, service management, and automation. The box need NOT bake the charly candy for charly to run inside a venue: when a flow needs charly present and the venue lacks it (nested from-image delegation), the generic copy-charly-into-a-running-venue mechanism (EnsureCharlyInDeployVenue over DeployExecutor.PutFilepodman cp for a container, scp for a VM/host) copies the host’s own binary in on demand and invokes the delivered copy. Baking the candy only pre-stages the binary so the first such call skips the copy.

Check-vs-production binary source — disposable beds bake the IN-DEV charly

Section titled “Check-vs-production binary source — disposable beds bake the IN-DEV charly”

The charly candy installs the binary as a proper, dependency-resolving OS package via localpkg: ({pac: pkg/arch, rpm: pkg/fedora, deb: pkg/debian}). The BINARY SOURCE depends on the box type — a hard distinction, NEVER mixed:

  • Disposable check beds (disposable: true deploys) bake the latest in-development charly: the check-bed runner builds every bed image with charly box build --dev-local-pkg, so the package is BUILT from the local working tree (pkg/<fmt> + charly/). A bed therefore tests the charly code under development — never a stale published release.
  • Production boxes bake the latest published charly: a normal charly box build DOWNLOADS the published release package (releases/latest/download/opencharly-<arch>.<fmt>).

ONE decision point (deploykit.RenderLocalPkgImageInstall), generic across all kinds and all localpkg candies; the check-bed runner sets --dev-local-pkg automatically, a production build never does. A dev build that cannot find its local source HARD errors (R4 — no silent fallback to the release). Full mechanics: /charly-internals:install-plan “Check-vs-production charly toolchain”. This is WHY a fresh check bed exercises your uncommitted charly changes while a real box ships the released toolchain.

The charly candy’s copy: bin/charly run step is resolved relative to the candy directory, so the box build reads candy/charly/bin/charly — NOT the repo-root bin/charly. Two independent paths need to stay in sync:

Path Who reads it
bin/charly (repo root) Host-side charly invocations; users running /tmp/charly style tests
candy/charly/bin/charly The charly candy’s COPY into boxes during charly box build

Canonical workflowtask build:binary compiles to repo-root AND syncs to the layer:

Terminal window
task build:binary # Builds + syncs both paths; rebuild images after.
./bin/charly box build <image> # Rebuild affected images.

Manual workflow — if you skip task build:binary and build with go build directly, you MUST sync the candy path, or boxes will bake the previous binary:

Terminal window
cd charly && go build -o ../bin/charly . # Only updates repo-root bin/charly.
cp bin/charly candy/charly/bin/charly # REQUIRED — sync to layer path.
./bin/charly box build <image> # Rebuild affected images.

Why this bites: charly box build uses auto-generated intermediate images (e.g., ghcr.io/opencharly/charly-fedora-2-dbus-nodejs) that cache the charly candy. If you update bin/charly in repo-root but forget the candy copy, the intermediate’s cache hit serves stale content. After cleaning up a stale dual-path situation, charly clean --invalidate 'charly-fedora-2*' forces a clean intermediate rebuild.

The charly probe checks:

  1. Whether the charly binary exists in the container
  2. The CalVer version (charly version)

Shows as charly:ok (2026.94.1417) in charly status detail view. Returns - for boxes without the charly candy.

Note: charly version writes to stdout via fmt.Println (the prior println(version) emitted to stderr; the move to fmt.Println landed with the MCP server work so the in-process tool-call path — which captures os.Stdout — returns the CalVer correctly). The candy test at candy/charly/charly.yml asserts stdout: matches [0-9]{4}\.[0-9]+. The charly status probe uses CombinedOutput() so it’s agnostic to the stream.

# charly.yml — the charly candy is composed into all supervisord images
my-image:
candy:
base: fedora
candy: [charly]
  • The charly candy is the full toolchain (charly binary + virtualization + gocryptfs + socat); composed into githubrunner, charly-fedora, charly-arch
  • Now directly added to all boxes with supervisord (openclaw, jupyter, ollama, sway-browser-vnc, selkies-desktop, immich, etc.)

Use when the user asks about:

  • Installing the charly binary inside containers
  • The full charly toolchain composition (charly binary + virtualization + gocryptfs + socat)
  • In-container charly CLI usage
  • Updating the charly candy binary after code changes
  • /charly-image:layer — candy authoring reference (charly.yml schema, plan-step verbs, service declarations)
  • /charly-check:check — declarative testing (check: block, charly check box, charly check live)