pull
Recipe card from the charly-build plugin (Commands — runtime CLI verbs).
charly box pull – Fetch Image Into Local Storage
Section titled “charly box pull – Fetch Image Into Local Storage”Overview
Section titled “Overview”charly box pull fetches an image from its registry into the local container
engine’s storage so deploy-mode commands (charly shell, charly start, charly config,
charly alias add, etc.) can read its OCI labels via ExtractMetadata. It is a
pull-only operation — it does not start, configure, or restart any
service. That’s charly update’s job (see /charly-core:charly-update).
This command is the prerequisite for every deploy-mode operation on a fresh
host. Since the charly box refactor, deploy-mode commands no longer read
charly.yml — they read OCI labels + charly.yml only. If an image isn’t
in local storage, the label read fails and the CLI surfaces a friendly
recommendation pointing here.
Quick Reference
Section titled “Quick Reference”| Action | Command | Description |
|---|---|---|
| Pull short name | charly box pull jupyter |
Resolves registry + tag via charly.yml (requires project directory) |
| Pull fully-qualified ref | charly box pull ghcr.io/opencharly/jupyter:2026.108.56 |
Pulls as-is, works from anywhere |
| Pull remote project ref | charly box pull @github.com/org/repo/image:v1 |
Downloads repo, reads its charly.yml, pulls registry ref |
| Override tag (short name) | charly box pull jupyter --tag 2026.108.1 |
Pull a specific CalVer tag |
| Override platform | charly box pull jupyter --platform linux/arm64 |
Pull a specific platform |
Three Input Forms
Section titled “Three Input Forms”1. Short name (requires project directory)
Section titled “1. Short name (requires project directory)”cd ~/opencharly && charly box pull jupyterResolves <registry>/jupyter:<tag> via charly.yml. Equivalent to the
two-step:
charly box inspect jupyter --format registry # ghcr.io/opencharlycharly box inspect jupyter --format tag # ghcr.io/opencharly/jupyter:2026.108.56# …then podman pull <that-ref>…but packaged as a single command.
2. Fully-qualified ref (no project required)
Section titled “2. Fully-qualified ref (no project required)”charly box pull ghcr.io/opencharly/jupyter:2026.108.56Runs podman pull <ref> directly. Useful from any directory, including
/tmp. Supports any registry that the underlying engine (podman/docker)
can authenticate against.
3. Remote project ref (@github.com/...)
Section titled “3. Remote project ref (@github.com/...)”charly box pull @github.com/opencharly/charly/jupyter:2026.108.56charly box pull @github.com/opencharly/charly/jupyter # latest git tagDownloads and caches the repo, reads its charly.yml, then pulls the
registry ref declared there. This is the only place @github.com/...
refs are accepted in charly. Deploy-mode commands (charly shell, charly start,
charly config, etc.) reject them with a message pointing users here.
Semantics
Section titled “Semantics”- Idempotent. Running
charly box pull jupytertwice with the image already local is a no-op (the engine’spullis layer-aware). - No side effects on services. Unlike
charly update, this command does not restart, reconfigure, or touch any running container. - Prints the resolved ref on success. One line to stderr.
- Respects engine selection. Uses the run engine from
ResolveRuntime(podmanby default;dockerif configured).
Interaction with ErrImageNotLocal (the sentinel pattern)
Section titled “Interaction with ErrImageNotLocal (the sentinel pattern)”The CLI has a centralized design for “image not in local storage” errors. Every deploy-mode command inherits the same friendly recommendation without per-call-site code:
ExtractMetadata(engine, imageRef)incharly/labels.goreturnsErrImageNotLocal(wrapped with the image ref) when the image is absent from local storage.EnsureImage(imageRef, rt)incharly/transfer.goreturns the same sentinel when the image is absent from both run and build engines.FormatCLIErrorincharly/image.gounwraps the sentinel at the top-level error boundary inmain()and renders:
Error: image "X" is not available locally. Run 'charly box pull X' to fetch it firstAny deploy-mode command that calls ExtractMetadata or EnsureImage
(shell, start, stop, config, deploy, update, remove, alias, vm, service,
cdp, wl, vnc, tmux, record, dbus, logs — essentially everything outside
charly box) automatically participates. If you’re authoring a new command
that reads image metadata, just call ExtractMetadata and the
recommendation falls out for free.
Interaction with charly update
Section titled “Interaction with charly update”| Aspect | charly box pull |
charly update |
|---|---|---|
| Side effects | None. Pulls bytes, prints digest. | Pulls, seeds data into volumes, restarts active service. |
| Required before deploy | Yes (first time only). | No — it calls pull internally if needed. |
| Use when | You want labels available but aren’t ready to deploy. Or just installed charly on a fresh host. |
You have a running service and want to roll to a new image version. |
Rule of thumb: pull is the prerequisite; update is the refresh.
Use charly box pull <image> once to seed local storage; use charly update <image> every time you bump the version afterward.
--tag <tag>(default:latest) — Image tag when resolving a short name. Ignored for fully-qualified or remote-project refs (the tag is embedded in the ref).--platform <platform>— Target platform (default: host). Passed topodman pull --platform.
Typical Workflows
Section titled “Typical Workflows”Fresh host, new user
Section titled “Fresh host, new user”# Install charly (see README Install section)charly box pull jupyter # pull once; labels now readablecharly config jupyter # generate quadlet from labels + charly.ymlcharly start jupyter # systemctl --user startDeploying a remote image
Section titled “Deploying a remote image”charly box pull @github.com/opencharly/charly/hermes:latestcharly config hermescharly start hermesFixing a broken deploy-mode command
Section titled “Fixing a broken deploy-mode command”charly shell jupyter# Error: image "jupyter:latest" is not available locally.# Run 'charly box pull jupyter:latest' to fetch it firstcharly box pull jupyter:latest # follow the recommendationcharly shell jupyter # now worksWhy this command exists
Section titled “Why this command exists”Before the charly box refactor, deploy-mode commands had dual-mode logic:
try charly.yml first, fall back to OCI labels. That produced drift (the
two paths could diverge) and silently pulled-and-built remote refs as a
side effect of what looked like a simple charly shell @github.com/... call.
The refactor drew a hard line: deploy-mode commands read labels only;
build-mode commands (charly box …) read charly.yml only. charly box pull
is the bridge — it takes a build-mode identifier (short name or remote
repo ref) and produces a deploy-mode-consumable artifact (labels in local
storage).
Project directory override
Section titled “Project directory override”charly box pull resolves charly.yml via os.Getwd() when given a short name (to resolve registry + tag). Override with -C <dir> / --dir <dir> / CHARLY_PROJECT_DIR=<dir>. Fully-qualified refs and @github.com/... remote refs don’t need a project dir. See /charly-image:image “Project directory resolution”.
Cross-References
Section titled “Cross-References”/charly-image:image— family overview;charly box pullis one of 8 subcommands./charly-build:build— pulls and builds are orthogonal; build creates images, pull fetches existing ones./charly-core:charly-update— rolls deployed services to a new image version (pulls + data-seeds + restarts)./charly-build:inspect— print resolved ref fromcharly.ymlwithout pulling./charly-core:shell,/charly-core:start,/charly-core:charly-config,/charly-automation:alias,/charly-vm:vm— deploy-mode commands that require a pulled image./charly-core:deploy— charly.yml overlay semantics applied on top of the labelspullmaterializes./charly-internals:go—ErrImageNotLocal/EnsureImage/ExtractMetadatasource locations (charly/labels.go,charly/transfer.go,charly/image.go).