How it works¶
Four stages turn a small JSON profile into a proven, named image: Define, Resolve, Build, Prove.
Terms such as lock, seal and smoke test are explained in Key concepts.
%%{init: {'theme':'base','fontFamily':'ui-sans-serif, system-ui, -apple-system, Helvetica, Arial, sans-serif','themeVariables':{'primaryColor':'#ffffff','primaryTextColor':'#2b2d42','primaryBorderColor':'#c9184a','lineColor':'#8d99ae','edgeLabelBackground':'#ffffff','fontSize':'13px'},'flowchart':{'curve':'basis','nodeSpacing':45,'rankSpacing':55,'padding':8,'useMaxWidth':true}}}%%
flowchart TB
subgraph D[" โ Define ยท profiles, bases, packages (JSON) "]
direction LR
PR["profiles/<br/>NAME.json"] ~~~ BA["config/bases/<br/>*.json"] ~~~ PK["config/packages/<br/>*.json"]
end
subgraph R[" โก Resolve & review ยท the Mac, or any host with gpg "]
direction LR
RS["resolve.py<br/>resolve"] --> LK[("locks/<br/>NAME.lock.json")] --> RV{{"you review<br/>& commit"}}
end
subgraph B[" โข Build ยท Apple silicon Mac "]
direction LR
VF["verify<br/>cache โ lock"] --> IN["install from the<br/>vendor IPSW / ISO"] --> GU["guest re-verifies,<br/>hardens, seals"]
end
subgraph P[" โฃ Prove ยท a throwaway clone "]
direction LR
UV["โฆ-unverified"] --> ST{{"smoke test<br/>from outside"}}
ST -->|"pass"| OK["rbt-NAME-sha โ
"]
ST -->|"fail"| KEEP["kept for<br/>inspection"]
end
D ==> R ==> B ==> P
classDef cfg fill:#fff0f3,stroke:#c9184a,stroke-width:1.5px,color:#2b2d42
classDef step fill:#ffffff,stroke:#2b2d42,stroke-width:1.5px,color:#2b2d42
classDef gate fill:#ffd6de,stroke:#c9184a,stroke-width:1.5px,color:#2b2d42
classDef good fill:#c9184a,stroke:#800f2f,stroke-width:1.5px,color:#ffffff
classDef bad fill:#2b2d42,stroke:#2b2d42,color:#ffffff
class PR,BA,PK cfg
class RS,VF,IN,GU,UV step
class LK,RV,ST gate
class OK good
class KEEP bad
style D fill:#f7f7f9,stroke:#d9a5b3,color:#6b6b76
style R fill:#f7f7f9,stroke:#d9a5b3,color:#6b6b76
style B fill:#f7f7f9,stroke:#d9a5b3,color:#6b6b76
style P fill:#f7f7f9,stroke:#d9a5b3,color:#6b6b76
- Define. A profile names an OS base and a list of tools. Bases and packages declare where each input comes from and how it is verified.
- Resolve.
resolve.pyfinds the current versions, downloads them, verifies them, and writes a lock. macOS profiles resolve on the Mac; NixOS and Kali resolve anywhere withgpg. A person reviews the lock diff before it's committed. - Build.
build.shre-checks the cache against the lock and installs from the vendor image. Inside the guest, every staged file is checked again before install. The guest then hardens itself, removes build residue and identity, and powers off. - Prove.
smoke-test.shboots a disposable clone and tests it from outside. For a key-SSH image (RHUBARB_SSH_PUBKEYSset), password login must be refused and key login must work. It then logs in and checks the hardening: passwordless sudo, auto-login or a disabled firewall fails the test. A key-SSH Linux image is also rebooted once, and on both boots/must be read-write with its fstab mounts up. For an SSH-disabled image, port 22 must refuse connections; nothing inside can be checked, because there is no way in. Every image must keep Screen Sharing closed, and any port a package declares (Juice Shop's 3000) must answer. Only then is the image renamed torbt-<profile>-<inputs-sha>. The name is derived from the inputs, so identical inputs give an identical name, andout/<vm>.provenance.jsonrecords exactly what went in.