Quickstart
Everything below acts on one file. Read it first — building something you have not seen is how charly stops making sense three commands in.
The file
Section titled “The file”This is tutorial-shell — a real box in the repository,
and a check bed re-proves it on every acceptance
run. It composes three layers on the bare Fedora base: a tool (ripgrep), a service (sshd),
and the supervisord init.
Four things are going on, and they are the whole model:
candy:is the only entity keyword. This node carriesbase:, which makes it a box — a buildable container image. A node withoutbase:would be a layer instead.base: fedorapoints at another box defined next door, not an external registry image. A base can be either.- the
candy:list composes three layers: a tool (ripgrep), a service (sshd), and thesupervisordinit. Each installs one concern; composing them is what turns the bare Fedora base into a working shell. plan:is the acceptance spec, and it is mandatory. Note what it checks: not thatrgandsshdare present — each candy’s own plan already proves that, and those plans run against this same image — but that composing the service candy next to the init candy madesshda supervisord program. A check belongs on the behaviour’s provider; it belongs on the composing box only when the claim is about the composition itself.
New to the vocabulary? The words defines candy, box and candybox in five minutes.
Build it
Section titled “Build it”charly --repo opencharly/distro-fedora box validate # the schema gate — silence is the passcharly --repo opencharly/distro-fedora box build tutorial-shellbox build resolves the candy graph, generates a multi-stage Containerfile, and builds the image.
The acceptance plan of every composed candy is baked into the result as an OCI label, so the image
can be tested later without this repository.
The --repo opencharly/distro-fedora says which project to read, fetched and cached for you — so
this works with nothing but the charly binary installed, no clone required. If you do have the
repository checked out, -C box/fedora points at the same project on disk.
Enter it
Section titled “Enter it”charly --repo opencharly/distro-fedora shell tutorial-shellYou are now inside the candybox — the running, isolated form of that box. There is no command
filter: dnf works, rg works, anything you install works. It runs rootless at uid 1000 with no
--privileged. Why that is the safe arrangement →
Prove it
Section titled “Prove it”charly --repo opencharly/distro-fedora check box tutorial-shell # run the baked plan against the image24 steps: 19 passed, 0 failed, 5 skippedThe five skips are three different things, and the distinction is worth reading:
| Skipped | Why |
|---|---|
sshd-service-running, sshd-port-reachable, and supervisord’s control-socket check |
context: [runtime] — a service cannot be running inside an image |
a run: step that grants the uid-1000 account passwordless sudo |
it mutates, and check box is verify-only |
an agent-check: step |
it is graded by an agent, and no grader is bound here |
Only the first group needs a deployment. That is what the bed provides:
charly --repo opencharly/distro-fedora check run check-tutorial-shell[image-build] PASS after 36s[check-image] PASS after 17s[deploy-add] PASS after 16s[start] PASS after 9s[check-live] PASS after 16s...[update] PASS after 63s[check-live-rebuild] PASS after 16s[cleanup] PASS after 6sPASS (steps=13)Eight of the thirteen steps are shown; ... marks the elided ones (config,
bring-up-members, feature-run, feature-run-rebuild, cleanup-members).
One command: build, deploy, bring to steady state, run the plan, then destroy and rebuild from
scratch and run it again, then tear everything down. That second check-live-rebuild is the
interesting one — it is what separates “it worked once” from “it reproduces”.
Rebuilding from scratch on every run is affordable only because the deploy is declared
disposable: true — charly may destroy it without asking, so proving reproducibility costs one
flag rather than a separate environment.
Change the mold
Section titled “Change the mold”The same candies are not tied to containers. A local: deploy applies them to a machine — and
because that means real packages and real units on whatever it targets, this repository
demonstrates it against a disposable VM guest rather than a workstation:
# charly.yml — a local: deploy nested INSIDE a disposable VM guest, so the# "machine" it changes is the guest and never yourscheck-group: vm: from: eval-vm disposable: true lifecycle: dev check-group-member: local: from: check-group-appTree position is what makes it safe: because check-group-member is a member of the vm: node (check-group)
node, the local: deploy lands inside the guest rather than on the host. It carries no host:
field at all — the venue comes from its parent. check-group-app is itself a local: template
composing one candy that drops a marker file, and the bed asserts that marker exists in the
guest.
Point a local: deploy at your own machine only when you actually mean to change your own
machine.
Swap the substrate and the same list installs into a VM guest over SSH, generates Kubernetes manifests, or installs apps onto a phone. No second vocabulary.
Where to go next
Section titled “Where to go next”- The words — the vocabulary, defined once.
- The concepts tour — twelve short pages, in the order the project’s own vision states them.
- Authoring a candy — start your own project.
- Recipe cards — the dedicated page for every candy, box and verb.