Using your VMs¶
Built images are templates: you work in disposable clones of them (see
Key concepts). The rhubarbtart CLI manages clones, and the
./rhubarbtart-tui dashboard drives the same actions over the same core. Scoped sets of clones
and their evidence are covered in Engagements and evidence; stacked macOS clones
from a registry in Publishing and stacked clones.
Everyday commands¶
./rhubarbtart images # built images; which one is current per profile
./rhubarbtart new web-1 --profile kali-research # clone the current image; give it its OWN password
./rhubarbtart new mac-1 --profile tahoe-research --from-registry # macOS: stack on the verified registry copy
./rhubarbtart run web-1 # GUI (Rosetta applied if the profile uses it)
./rhubarbtart ssh web-1 # key-only SSH, host key pinned per clone
./rhubarbtart enroll web-1 tailscale # VPN identity for this clone only
./rhubarbtart list # clones: state, outdated image?, password, enrollment
./rhubarbtart reset web-1 # destroy + fresh clone of the current image
./rhubarbtart rm web-1 # delete the clone (+ keychain entry if it had a unique password)
| Command | What it does |
|---|---|
new NAME --profile P / --image IMG |
Clones a verified image (never -unverified), records its lineage, then boots it headless and rotates it to a unique random password (keychain account = clone name), proving through sudo that the old one is rejected. --no-rotate keeps the image's password. --from-registry (macOS only): verify the image's published copy by digest (signature and provenance) and stack the clone on it, tens of MB instead of a full copy |
run NAME [--headless] [--detach] |
Starts the clone with the right flags for its profile (--rosetta=rosetta for Linux Rosetta profiles). It keeps the terminal until the VM shuts down; --detach returns it |
stop NAME |
Stops a running clone (the VM and its records stay) |
ssh NAME [-- CMD] |
Connects as the profile's user, host key pinned per clone name |
exec NAME -- CMD |
Runs one command over SSH and returns its output and exit code. In an engagement clone, the command and its output are recorded as evidence |
enroll NAME tailscale\|warp [--org TEAM] |
Runtime VPN/ZTNA enrollment (see below) |
images |
Lists built images and which one is current for each profile |
list |
Lists clones with their state, password mode and enrollments. A clone's image can show as outdated (the profile's lock changed), image-deleted, or, for a stacked clone, base-missing (the pulled base is gone) |
reset NAME [--same-image] [--no-rotate] |
Throws the clone away (identity, enrollment and all) and re-clones, from the current image by default. A stacked clone stays stacked |
rm NAME [--yes] |
Stops and deletes the clone, its keychain entry (only if it had a unique password) and its pinned host key. A stacked clone releases its base when no other clone uses it |
Note:
rhubarbtartonly touches clones it created. It never modifies built images (rbt-โฆ) or VMs made some other way, and clone names can't start withrbt-. Per-clone rotation needs key SSH (images built withRHUBARB_SSH_PUBKEYS, with the key inssh-agentorRHUBARB_SSH_IDENTITYpointing at it). Without it the clone keeps the image's password, andrhubarbtart listsaysinherited. For an image built with SSH disabled,newskips the rotation without booting the clone and prints a hint to rebuild withRHUBARB_SSH_PUBKEYS.
To read a clone's password: security find-generic-password -s RhubarbTart -a NAME -w (use the
image name instead of NAME when list says inherited).
The dashboard¶
./rhubarbtart-tui shows images, clones, provenance and logs, and runs run, ssh, enroll,
reset, rm, new and builds, with a confirmation before anything destructive.
The dashboard is keyboard-driven. 1โ4 open the tabs (Images, Clones, Provenance, Logs) and
put the cursor in them; Enter on an image or clone shows its provenance (a clone shows the image it
was cloned from); r refreshes, ? lists every key and q quits. The footer shows only the keys
that apply: clone actions (b run, s ssh, e enroll, x reset, d remove) are on the Clones
tab, while n (new clone) and B (build) work anywhere. Actions run one at a time; the header
shows the one in progress. Data refreshes in the background, so a slow tart list never freezes
the screen. The Logs tab lists each clone's run log, the build logs written by B
(logs/build-<profile>-<UTC time>.log in the state directory, mode 0600, with the VNC password
redacted) and events.log. It follows a growing log while you're at its end, and holds still
while you scroll back. B runs scripts/build.sh, so like any build it needs the Mac's GUI login
session; it refuses to start over SSH.
Inside herdr it leaves the mouse to herdr; use ./rhubarbtart-tui --mouse (or RHUBARB_TUI_MOUSE=1)
to capture it anyway, or --no-mouse to turn it off elsewhere. It also takes herdr's colour theme:
the [theme] in herdr's config.toml, including auto_switch (dark or light follows the Mac's
appearance) and any [theme.custom] colours. Use --theme NAME (or RHUBARB_TUI_THEME=NAME) to
pick a Textual theme instead, for example nord or catppuccin-latte.
Your SSH key¶
rhubarbtart reaches clones over SSH with a key, never a password. The public key goes into the
image when you build it (RHUBARB_SSH_PUBKEYS). The private key stays on your Mac.
- Use a key only for your VMs. Don't reuse the key you use for GitHub or servers. If a VM key leaks, nothing else is exposed, and you can replace it on its own.
- Create it with a passphrase. This command asks for one:
ed25519 keys are short and fast, and the build accepts them. -a 100 makes a passphrase much
slower to guess if someone copies the private key file.
- Keep the passphrase in your keychain. ssh-add --apple-use-keychain ~/.ssh/rhubarbtart_ed25519
loads the key into ssh-agent and stores the passphrase in your macOS keychain. After a
restart, ssh-add --apple-load-keychain loads it again without asking. The build's smoke test
and rhubarbtart use the key from the agent.
- Use one key file instead of the agent by setting RHUBARB_SSH_IDENTITY to the private key's
path. rhubarbtart then uses only that key. The build's smoke test still uses the agent.
- Authorize several keys by listing their public keys in one file, one per line, and passing
that file as RHUBARB_SSH_PUBKEYS. The build accepts ed25519 and ecdsa keys and refuses RSA.
- Hardware security keys (ed25519-sk, for example a YubiKey) are accepted by the build, but
macOS's built-in ssh can't use them without a separate FIDO2 provider. A passphrase-protected
key in the keychain is the simpler choice.
- Changing the key means rebuilding: each image holds the public keys it was built with. Build
again with the new RHUBARB_SSH_PUBKEYS, then rhubarbtart reset NAME moves a clone to the new
image.
VPN and ZTNA enrollment¶
Images never contain VPN identity. Each clone is enrolled at runtime. The secrets never touch the repo, a profile, the image or a command line: they go from your keychain to the clone over SSH stdin, into a 0600 temporary file that is shredded once read.
%%{init: {'theme':'base','fontFamily':'ui-sans-serif, system-ui, -apple-system, Helvetica, Arial, sans-serif','themeVariables':{'primaryColor':'#fff0f3','primaryBorderColor':'#c9184a','primaryTextColor':'#2b2d42','actorBkg':'#fff0f3','actorBorder':'#c9184a','actorTextColor':'#2b2d42','actorLineColor':'#c9a3ae','signalColor':'#8d99ae','signalTextColor':'#2b2d42','labelBoxBkgColor':'#fff0f3','labelBoxBorderColor':'#c9184a','labelTextColor':'#2b2d42','noteBkgColor':'#c9184a','noteTextColor':'#ffffff','noteBorderColor':'#800f2f','sequenceNumberColor':'#ffffff','activationBkgColor':'#c9184a','activationBorderColor':'#800f2f'},'sequence':{'mirrorActors':false,'messageAlign':'center','boxMargin':10,'noteMargin':10,'width':170}}}%%
sequenceDiagram
autonumber
actor You
participant KC as ๐ Host keychain
participant EN as rhubarbtart enroll
participant VM as Clone ยท web-1
You->>KC: store the token once<br/>(prompted, never in argv)
You->>EN: rhubarbtart enroll web-1 tailscale
EN->>KC: read the clone password + token
EN->>VM: send both over SSH stdin
Note over VM: secret โ 0600 temp file<br/>enroll โ shred
VM-->>You: connected
| Service | Command | Secret (keychain service RhubarbTart-enroll) |
Left in the clone | Notes |
|---|---|---|---|---|
| Tailscale | rhubarbtart enroll web-1 tailscale |
account tailscale-authkey |
Linux: nothing. macOS: the key stays in Tailscale's managed preferences until you remove it (below) | Use a one-off, pre-approved, tagged key (ephemeral for throwaway clones). macOS: approve the system extension once per clone |
| Cloudflare WARP | rhubarbtart enroll web-1 warp --org TEAM |
warp-client-id, warp-client-secret |
The service token, in a root-only (0600) mdm.xml the WARP client reads |
A service token allowed to enroll devices; dashboard version pushes are disabled |
Store a secret once with security add-generic-password -s RhubarbTart-enroll -a tailscale-authkey -w
(it prompts, so the secret never lands in your shell history). rhubarbtart enroll wraps
scripts/enroll.sh and records which services each clone is enrolled in.
On a macOS clone, once Tailscale has connected, remove the key from its preferences:
sudo defaults delete /Library/Preferences/io.tailscale.ipn.macsys AuthKey (the enroll output
repeats this). Because a clone's enrollment dies with it, prefer one-off and ephemeral keys, and
reset or rm a clone when the work is done.
Clone records¶
Records are kept in ~/Library/Application Support/RhubarbTart/ (override with
RHUBARB_STATE_DIR):
- Outside the repo, so they're never committed or synced with it, and outside Tart's VM folders, so they never ship inside a VM bundle or registry push.
- No secrets: they hold only names, lineage, username and timestamps. Passwords stay in the macOS keychain.
- Like OpenSSH's StrictModes, a record is trusted only if the folder is
0700and the file is0600, owned by you, and a regular file (not a symlink). Its schema is strict and its name must match the file, so a planted or corrupted record can't steer the CLI at the wrong VM or keychain entry. Writes are atomic. events.logkeeps an append-only trail (no secrets) ofnew,rotate,enroll,resetandrm.- They're not a boundary against malware running as you, which could drive
tartand your keychain directly anyway. They're about correctness and least surprise.