Rodeo Builder¶
The Rodeo Builder composes a new rodeo in the browser: pick the lab engine, put
story chapters in order, tag their text for translation and story variants, and
download the result as a profile you install with rodeo.
It comes in two versions of the same page:
- Published (the button above): a static page built by GitHub Actions with the docs. Nothing you do there leaves your browser; you download a zip.
- Live, installed with rodeo-cli:
rodeo builderserves the page on your machine, reads its data live and adds Save to profiles, which writes the rodeo straight into~/.rodeo/profiles/<name>/.
What you compose¶
- Lab engine.
suse-virt,rancherandsuse-edgeare rodeo's own engines; the (i) on each shows its size and what it provides (for example Harvester, KubeVirt, Longhorn, Rancher, Fleet).lab-in-a-boxbuilds any lab lab-in-a-box can:- Open in Lab Builder opens lab-in-a-box's lab-builder with this rodeo's add-ons filled in. Download the lab.json there and upload it here to bring its add-ons back.
- Embed here opens the lab-builder inside the page. Handing the lab back directly needs a lab-in-a-box release with the embed hand-off; until then, use Download there and Upload here.
- Import existing lab starts from a bundled lab-in-a-box rodeo
(
smlm-workshop) or from a lab.json.
- Chapters. The library lists every workshop's chapters: the bundled
examples'
story/directories, the workshops whose chapters live in their own repository (listed inrodeo/builder/chapter_sources.yaml: the SMLM workshop's Instruqt track and the Virtualization workshop's exercises), thestory/of every published rodeo in the catalog (rodeo/data/catalog.d/, unlesschapter_sources.yamlalready names its repository), and the chapters you create with + New. Workshops start collapsed: click a workshop's title to show its chapters. The (i) next to it (hover, or click to keep it open) shows where the chapters come from, the lab engine and profile they run on, their count and time, and what they need. use sets the rodeo up as that workshop: its lab engine and profile (for examplesuse-virtwith thevirt-workshop-awsprofile, whose extra setup the exercises need) and all its chapters, in place of the rodeo's (Undo restores them); + all adds its chapters to the rodeo's. A chapter card shows its title; hover over it for its time and needs, or turn on the list button next to the search to show them on every card. The funnel button next to it shows only the workshops for this lab engine, the chapters this lab can run, or the chapters not in the rodeo yet. Drag chapters into the rodeo (anywhere on the list) or click them; click a chapter again to take it out. Click a row in the rodeo to insert new chapters after it instead of at the end. Reorder by dragging or with Alt+↑/↓. Removing a chapter or a span offers Undo for a few seconds. When every chapter comes from workshops that run on one other engine, a banner offers to switch to it. A chapter that needs something the lab doesn't provide is marked, and for lab-in-a-box one click adds the missing add-on. Chapters that come with a check script bring it along. A new chapter's Requires from the lab field suggests, as you type, what an engine really provides: the native engines' capabilities and lab-in-a-box's catalogue (add-ons, infrastructure services, Kubernetes cluster types). If the lab-in-a-box catalogue could not be read, the page says why. When nothing matches, Enter adds what you typed as a new need: a placeholder for something no engine provides yet, likemissing_addon. Such a need only marks the chapter as "work needed" (also listed, by name, in the download's README); it never blocks the rodeo and never reaches the lab. - Import. Import above the library adds a workshop from a URL: a git
repository or a directory in one (
https://<host>/<owner>/<repo>/tree/<ref>/<dir>). The builder detects the format (a rodeostory/, an Instruqt track<dir>/*/assignment.md, or markdown<dir>/*.md) and lists its chapters as a new group; × removes the group again. The published page reads public github.com repositories through the GitHub API (60 unauthenticated requests an hour); the rodeo builder server clones any public https repository (shallow and sparse, no credentials). Imported groups are kept with the draft. - Story. Edit on a chapter opens the story editor (rows with span
warnings show how many). Select text and click Tag selection, use the toolbar, or press
Alt+T / Alt+I / Alt+N to mark it Translatable, Invariant or No-lang;
double-click a span to change its type,
language, id or story variant. Ids follow rmstory's rules (
<chapter>.N,<parent>.Nwhen nested) and the editor shows the same warnings asrmstory validate. The source and the preview sit side by side and scroll together; the preview renders the chapter as Markdown or HTML (only safe tags, images as links), with its spans marked. Ctrl+Z / Ctrl+Shift+Z undo and redo; Cancel closes the editor and discards its changes, Done keeps them. - Translations. The story editor's translations tab lists every
translatable span per language. Click a cell to write or review a
translation; a nested span stays in it as
<span id="..."/>. The download carries them in rmstory's translation store,story/strings/, androdeo story render --language <lang>uses them (with--enginefor the spans still missing).
Review and download (step 03) checks the rodeo before you download it: the
lab covers every chapter, the span ids are valid (each warning has a Fix
button), the exact files in the zip (listed when you click N files in
The page keeps a draft of your rodeo in this browser. After a reload it offers to restore it.
With rmstory on the live builder¶
When rmstory is installed where rodeo builder runs (sudo rodeo install-deps
--story), the live page also uses it:
- Translate missing (translations tab): fills the chosen language's missing
translations with an rmstory engine (
rmstory extract --engine <e> --to <lang>). They are stored asdraft; what you wrote or reviewed is kept. rmstory also gives unnamed spans an id (<chapter>.N). - Validate (step 03): rmstory's own
validateon the chapters. - Draft with an engine (New chapter): an engine that can generate text
(
rmstory enginessays "translate + generate", for exampleclaude-code,ollamaorgemini) drafts the chapter from your description, told what the lab provides and which chapters come before it. Its spans are renumberednew-chapter.N; review the draft before you create the chapter. - New story (rodeo tab): the same exercises told as a different story. You say what is what with the span tags (story editor):
- story: spans with a story variant (
hist): retold; - exercise: translatable spans without a story variant: translated, never retold;
- fixed: invariant (
no) or no-lang spans, and text outside spans: kept word for word.
Say what the new story is about and pick an engine that can generate text;
rmstory generate rewrite --theme rewrites the story spans and the exercise
spans never reach the engine. The retold chapters (ids <chapter>-<theme>,
story span ids <theme>.<id>; exercise and fixed spans keep their ids and
translations) become a library group and replace the rodeo's chapters;
Undo puts the originals back.
Engines read their keys from the environment rodeo builder runs in, never from
the page: for example DEEPL_AUTH_KEY, GEMINI_API_KEY, RMSTORY_OLLAMA_URL, or a
logged-in claude command for claude-code. The published page writes
translations by hand only.
The right-hand panel sets the name, story title, language, deployment target and
story variant, and previews every file of the download. plan.yaml is an
editor: change anything and the download keeps it; name, deployment_target
and story stay in sync with the rodeo tab, and Reset to generated drops your
edits. Changing the lab engine regenerates the plan, after asking.
Infrastructure and instructions¶
The page keeps the lab (Infrastructure: engine, base profile, plan,
definition, lab-in-a-box lab) apart from what students read (Instructions:
chapters, story variants, translations, checks). Step 03 downloads either the
whole rodeo or one part: Infrastructure only (<name>-infrastructure.zip,
installable with rodeo new --from-zip) or Instructions only
(<name>-instructions.zip: story/ and checks/, laid out to be pushed as a
content repository).
Instructions repository (rodeo tab) points the plan at instructions kept in
their own repository: the URL (https://<host>/<owner>/<repo>[/tree/<ref>/<path>])
and format become the plan's content: block. rodeo up renders them after
the lab is up, and rodeo content apply updates them on a running lab without
redeploying (see the plan reference).
Install what you downloaded¶
The zip holds one profile directory, <name>/, with rodeo-plan.yaml, the
chapters (story/NN-<id>.md), story/chapters.yaml (minutes, needs and check
script per chapter), one story/stories/<variant>.yaml per story variant,
checks/check-<id>.sh stubs, a README with the exact commands and, when the
rodeo starts from a profile (suse-virt, rancher, suse-edge or an imported
lab-in-a-box rodeo), builder.yaml naming that base profile.
--from-zip copies the base profile, lays the zip's files over it and writes
~/.rodeo/profiles/<name>/. A new lab-in-a-box rodeo carries its own
definition.yaml and needs no base. --from <profile> overrides the base and
--force replaces an existing profile. The zip may only hold the files the
builder writes; anything else (other paths, links, oversize files) is refused
before anything is written. Save to profiles in rodeo builder does the same
without the zip.
A published rodeo (see Publish to GitHub) installs the same way from its repository, and a catalog rodeo by its name alone:
rodeo new <name> --from https://github.com/<owner>/<repo>[@<ref>]
rodeo up --profile <catalog name> # fetched and installed on first use
rodeo profiles # lists the catalog too
The repository is fetched shallow, without credentials, and only the files a
builder rodeo may hold are installed; ~/.rodeo/profiles/<name>/rodeo.lock
records the repository, ref and commit. --force re-fetches.
The live builder¶
rodeo builder serves rodeo/builder/htdocs/ and answers the page's requests
with rodeo/builder/api.py (rodeo/builder/server.py, standard library only).
It links to and embeds lab-in-a-box's published static lab-builder, or the one
--lab-builder-url names. With --start-lab-builder it starts the checkout's own
dynamic lab-builder (webui/run-local.py) next to it on --lab-builder-port,
opened on the host name the browser used for the builder (the published one when
the checkout has none or it can't start). That lab-builder has no login or token,
so any site you browse can save labs through it: it is off by default and only
allowed on a loopback --host.
At start it fetches the chapter sources (a source that can't be fetched is left
out, with a warning) and the lab-in-a-box catalogue: --labinabox <checkout>,
else RODEO_LABINABOX_PATH, else lab-in-a-box's latest release, fetched into
~/.rodeo/vendor/lab-in-a-box/ the way a deploy does.
| Option | Default | |
|---|---|---|
--host, --port |
127.0.0.1, 8678 |
Where to listen (--port 0 picks a free port). |
--expose |
off | Required for a non-loopback --host: anyone who reaches it can save profiles as you. Publish then uses the github.com pages, never this host's GitHub login. |
--allow-host NAME |
Extra Host name to accept (the name or IP clients use with --expose). |
|
--labinabox |
latest release | lab-in-a-box checkout for the catalogue and the lab-builder. |
--lab-builder-url URL (RODEO_LAB_BUILDER_URL) |
the published lab-builder | A lab-builder you already run (for example on an automation node) for the page to link to and embed; nothing is started. |
--start-lab-builder |
off | Start the checkout's own dynamic lab-builder. Loopback --host only. |
--lab-builder-port |
8677 |
Port of the lab-builder --start-lab-builder starts, on the same address as --host. |
--source ID=CHECKOUT, --no-fetch-sources |
fetch all | Local chapter sources, or none. |
--open/--no-open |
open | Open the page in a browser. |
Writes are guarded: the server only answers requests whose Host is an allowed
name (loopback names by default), so other sites can't reach it through DNS
rebinding, and Save sends a per-run token that only the served page has. The
page runs under a Content-Security-Policy that allows only its own files (the fonts
ship in htdocs/fonts/) and the lab-builder frame.
Deploy without saving¶
Deploy (header) runs the rodeo on the builder's host exactly as it is on the
page, without saving it to your profiles first. It needs rodeo builder on
loopback (not --expose) and root for rodeo up: the builder runs as root or
the user has passwordless sudo (sudo -n true). Otherwise, and on the
published page, the button is greyed out and says why on hover.
- The dialog says plainly that the rodeo is not saved, and offers Save to profiles and deploy next to Deploy without saving.
- The deploy runs
rodeo up --yes --no-tmux --dir <lab>from a copy in~/rodeo-labs/<name>-unsaved-<UTC time>/(base profile plus the page's files, as Save lays them out), its output in<lab>/.builder-deploy.log. Secrets rodeo can generate go to the lab's.rodeo-secrets.yaml; ones only you have (operator_secrets) are asked for in the dialog and written to~/.rodeo/secrets.yaml, never shown again. - A banner stays while it runs and after it ends, with the lab directory, the
log, Stop (interrupts
rodeo uplike Ctrl-C) and Save to profiles, and keeps saying the rodeo is not saved until you save it. One deploy runs at a time; rerun or resume from the lab directory withrodeo up.
Settings and stored credentials¶
Settings, in the user menu (the person icon at the right of the
header, which also shows the user@host the builder runs as), keeps the
credentials you deploy with — SCC registration codes, registry and Application Collection logins, AWS keys, SSH keys, GitHub
tokens, or any key: value fields — encrypted on the builder's host, one file
per credential in ~/.rodeo/vault/. It works only on rodeo builder on
loopback: with --expose and on the published page, Settings is greyed out in
the menu with the reason.
- Each credential has its own password, at least 8 characters, asked every time the credential is used. Neither the password nor the values are kept by the page: the credentials list shows a credential's name, type, when it was added and last used, never its values.
- In the deploy dialog, each secret the lab asks for offers Use stored… with
the credentials that can fill it. After the password, the page fills that
secret and every other one the same credential provides (an AWS credential
fills both
aws_access_key_idandaws_secret_access_key). A generic credential fills secrets whose names match its field names. - The filled values go where typed ones go: into
~/.rodeo/secrets.yamlforrodeo up. Deleting a stored credential does not remove them from there. - The dialog also lists the keys already in
~/.rodeo/secrets.yaml, names only and read-only:rodeo upreads that file directly. In the deploy dialog, Use stored… offers them under secrets.yaml (names sharing a word with the secret first). Picking one asks no password and locks the input ("from secrets.yaml:", Clear to type instead); the builder copies the value on its host, so it never reaches the browser.
Publish to GitHub¶
Publish puts the rodeo in a public GitHub repository of yours and opens a pull
request on rodeo-cli that adds it to the rodeo catalog
(rodeo/data/catalog.d/<name>.yaml: name, title, repository, ref, engine,
languages). The repository holds the files of the download.
rodeo builderon loopback uses the GitHub login already on its host, without storing it:GH_TOKENorGITHUB_TOKEN, else the GitHub CLI's login (gh auth token), else git's stored github.com credential. With none, Log in to GitHub runsgh auth login --weband shows its one-time code to enter at github.com/login/device; withoutgh, install it, rungh auth loginor setGH_TOKENbeforerodeo builder. Publishing createsgithub.com/<you>/<name>, pushes one commit onmain, forks rodeo-cli when you don't own it and opens the catalog pull request. Update my existing repository pushes a new commit that replaces the files and opens no pull request.RODEO_CATALOG_REPOpoints the pull request at another repository.- The published page (and
rodeo builder --expose) links to github.com pages that use your browser's GitHub login, and GitHub asks you to log in when you aren't: create the repository, upload the unzipped download (orgit push), and add the catalog file, which forks rodeo-cli and opens the pull request under your account.
How the published page is built¶
The published page is the live page's static subset. scripts/build-builder-static.py
embeds what rodeo/builder/api.py answers (engines and bundled chapters from
rodeo/data/, the chapter sources' chapters, and the catalogue of lab-in-a-box's
latest release) into the page and marks it static, which hides Save to
profiles; the docs workflow publishes it at builder/. The build fetches each
chapter source with a shallow, sparse git clone (only the chapter files and check
scripts) and fails if one can't be fetched, or if the lab-in-a-box catalogue
can't be read. Build it locally:
python3 scripts/build-builder-static.py --output /tmp/builder --labinabox /path/to/lab-in-a-box
python3 -m http.server -d /tmp/builder 8000
--source <id>=<checkout> uses a local checkout for one chapter source,
--no-fetch-sources builds without them (offline), and --no-labinabox builds
without the lab-in-a-box catalogue (the page then shows it as unavailable).