Key concepts¶
RhubarbTart uses a handful of words in a specific sense. This page explains each one in plain language. They are ordered to follow the lifecycle of a guest: from what you ask for, to what gets built, to what you work in. The collapsed sections go deeper.
%%{init: {'theme':'base','fontFamily':'ui-sans-serif, system-ui, -apple-system, Helvetica, Arial, sans-serif','themeVariables':{'primaryColor':'#ffffff','primaryTextColor':'#2b2d42','primaryBorderColor':'#c9184a','lineColor':'#8d99ae','fontSize':'13px'},'flowchart':{'curve':'basis','nodeSpacing':30,'rankSpacing':40}}}%%
flowchart LR
P["<b>Profile</b><br/>what you want"] -->|resolve| L[("<b>Lock</b><br/>exact inputs")]
L -->|build + seal| I["<b>Image</b><br/>rbt-profile-sha"]
I -->|smoke test passes| I
I -->|rhubarbtart new| C["<b>Clone</b><br/>where you work"]
I -.->|publish| R[("<b>Registry</b><br/>signed copy")]
R -.->|"rhubarbtart new --from-registry<br/>(macOS)"| S["<b>Stacked clone</b>"]
classDef a fill:#fff0f3,stroke:#c9184a,stroke-width:1.5px,color:#2b2d42
classDef b fill:#c9184a,stroke:#800f2f,color:#ffffff
class P,L,R a
class I,C,S b
What you ask for¶
Profile. A small JSON file (profiles/<name>.json) describing one kind of guest. It names an
OS base, a list of packages (tools), and a few options such as the desktop, VM size,
username or Rosetta. It's the only file you normally write. See Define your own guest.
Base. The operating system a profile starts from: macOS 26, macOS 27, NixOS or Kali
(config/bases/*.json). A base says where the vendor's installer comes from and how it's verified.
Package. One tool that can go into a guest, such as Chrome, ZAP, WARP or Tailscale
(config/packages/*.json). A package has a variant per OS family, because Chrome for macOS and
Chrome for Kali are different downloads with different ways to verify them.
What gets pinned¶
Input. Anything downloaded from outside that ends up in a guest: the OS installer (an Apple
IPSW, or a NixOS/Kali ISO), each app installer or .deb, and for NixOS the package
collection (nixpkgs). Inputs are the things RhubarbTart refuses to trust blindly.
Lock file. locks/<profile>.lock.json records the exact inputs for one profile: every URL,
version, SHA-256 hash and, where the vendor signs, the signer. The lock is produced by
resolving, reviewed by a person, and committed. A build uses what the lock says, so the same
lock means the same inputs, even months later. The exception is Kali's own packages (the
kali-linux-* sets and ZAP on Kali): they come from Kali's signed archive as it is on the day of
the build. Their signatures are checked, and the image records the exact versions installed.
Resolve vs verify, and the download cache
- **Resolve** (`resolve.py resolveWhat gets built¶
Image. The finished, hardened VM that a profile's lock produces, named
rbt-<profile>-<12 hex chars> (for example rbt-kali-research-0ff4b8e78398). The suffix is
derived from the inputs, so the same lock always yields the same name, and a changed input
yields a new one. An image is a template: you never boot it, so it stays exactly as built.
How a build gets there: vanilla VM, seal, smoke test, provenance
- **Vanilla VM** (macOS only). A plain macOS install that has been through Setup Assistant (`rbt-macos-27-What you work in¶
Clone. A disposable copy of an image, made with rhubarbtart new. This is where you actually
work. On macOS's file system (APFS) a clone is copy-on-write: it shares the image's data and
only stores what changes, so it's fast and cheap. Delete it when you're done; the image is
untouched and the next clone starts identical.
Per-clone password. By default each clone is rotated to its own random password as it's
created, stored in your macOS keychain under the clone's name. rhubarbtart proves the old (image)
password no longer works, so no two clones share a credential. Rotation needs key SSH: a clone of
an image built without SSH keys, or one made with --no-rotate, keeps the image's password. VPN/ZTNA enrollment also happens per
clone and is never baked into an image.
Engagement. A named scope (engagements/<id>.json) that stands up a whole set of tagged
clones at once, and tears exactly that set down again. Its clones can't reach each other unless
the manifest declares a link between them. See Engagements and evidence.
Evidence. The record of what happened in an engagement, kept on your Mac and never in the
clones: the commands run through rhubarbtart exec, files pulled from each clone's ~/evidence
folder, approvals and lifecycle events. Each entry is chained to the one before it by its hash, so
an edit or deletion shows up when you verify it.
Vault. A signed, read-only copy of an engagement's evidence (rhubarbtart vault seal) that you can
hand to someone else and check offline with rhubarbtart vault verify.
Sharing images (optional)¶
Publishing. Pushing an image to an OCI registry (the same kind of server that stores container images), then signing it and attaching its provenance record as a signed attestation. By default the registry runs locally on your Mac. Signing is offline: the key stays in your keychain and nothing is sent to public signing services.
Digest. A registry identifies an image by the SHA-256 of its contents
(…@sha256:07cb38…). A tag such as :edbb1008a6c0 is a movable label, so RhubarbTart
always uses digests.
Stacked clone (macOS only). A clone made with rhubarbtart new --from-registry. Instead of an
APFS copy of your local image, it's a thin overlay (tens of MB) on top of a base pulled
from the registry. The base is read-only and shared, and the overlay holds only the clone's
changes. The published copy's signature and provenance are verified before the clone is made.