<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>TinyComputers.io (Posts about ssh)</title><link>https://tinycomputers.io/</link><description></description><atom:link href="https://tinycomputers.io/categories/ssh.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Original site content © 2022–2026 Tiny Machines Workshop, LLC, except where otherwise noted. Some rights reserved.</copyright><lastBuildDate>Mon, 07 Sep 2026 23:39:23 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>A Mac, Two Disk Images, and an SSH Prompt: OpenBSD 7.9 on the Raspberry Pi 4B</title><link>https://tinycomputers.io/posts/openbsd-7-9-on-the-raspberry-pi-4b.html?utm_source=feed&amp;utm_medium=rss&amp;utm_campaign=rss</link><dc:creator>A.C. Jokela</dc:creator><description>&lt;h2&gt;A Mac, Two Disk Images, and an SSH Prompt: OpenBSD 7.9 on the Raspberry Pi 4B&lt;/h2&gt;
&lt;p&gt;I wanted an OpenBSD machine I could plug into the network and immediately start using. Put the card in, connect Ethernet, apply power, find its address, SSH in. An ordinary account with my key, &lt;code&gt;doas&lt;/code&gt; when I needed root, and enough room on the card that installing software would not immediately become another storage project. A modest list, which is how these things usually begin.&lt;/p&gt;
&lt;p&gt;The machine now exists. A Raspberry Pi 4 Model B Rev 1.5, four Cortex-A72 cores and 4 GB of RAM, running OpenBSD 7.9 from microSD. It answers to &lt;code&gt;rpi4b&lt;/code&gt;, negotiated gigabit Ethernet, and has separate &lt;code&gt;/usr&lt;/code&gt; and &lt;code&gt;/home&lt;/code&gt; filesystems with about 28 GiB each. The installation happened inside a virtual machine on my Apple Silicon Mac. By the time the card reached the Pi, the operating system was installed, patched, configured, and waiting for hardware to inhabit.&lt;/p&gt;
&lt;figure style="margin: 2rem 0; text-align: center;"&gt;
&lt;img src="https://tinycomputers.io/images/openbsd-rpi4b/openbsd-7.9-rpi4b-terminal.png" alt="SSH terminal connected to rpi4b showing neofetch: OpenBSD 7.9 arm64, four ARM Cortex-A72 cores, and 3954 MiB of memory." width="1384" height="1050" style="display: block; max-width: 100%; height: auto; margin: 0 auto;"&gt;
&lt;figcaption&gt;&lt;em&gt;The Pi over SSH, running OpenBSD 7.9. Four Cortex-A72 cores, 12 installed packages, and Puffy taking up most of the terminal.&lt;/em&gt;&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p&gt;&lt;strong&gt;Make your own card:&lt;/strong&gt; &lt;a href="https://tinycomputers.io/downloads/openbsd-7.9-rpi4b-generic-20260907/openbsd-7.9-rpi4b-generic.img.gz"&gt;Download the generic OpenBSD 7.9 image (525 MiB)&lt;/a&gt;, with &lt;a href="https://tinycomputers.io/downloads/openbsd-7.9-rpi4b-generic-20260907/README.md"&gt;setup instructions&lt;/a&gt;, &lt;a href="https://tinycomputers.io/downloads/openbsd-7.9-rpi4b-generic-20260907/SHA256"&gt;SHA-256 checksums&lt;/a&gt;, and &lt;a href="https://tinycomputers.io/downloads/openbsd-7.9-rpi4b-generic-20260907/openbsd-7.9-rpi4b-generic-redistribution.tar.gz"&gt;firmware licenses and corresponding source&lt;/a&gt;. This unofficial build accepts your SSH public key before boot. Its VM tests passed; a physical Pi boot test of the generic version is still pending.&lt;/p&gt;
&lt;p&gt;The handoff from VM to hardware is the interesting part. A VM can tell you quite a lot about an operating system. It has considerably less to say about whether a Raspberry Pi will find the bootloader you put on a card.&lt;/p&gt;
&lt;p&gt;OpenBSD already supports the Pi 4 through its arm64 port. The official installation media includes the Raspberry Pi firmware, so the starting point was conveniently unexotic: &lt;code&gt;install79.img&lt;/code&gt; from the OpenBSD 7.9 arm64 release, its signed checksum manifest, and the release public key. The &lt;a href="https://www.openbsd.org/arm64.html"&gt;arm64 platform page&lt;/a&gt; documents the support and the firmware requirements.&lt;/p&gt;
&lt;p&gt;I wanted the installation completed before deployment. An installer image gets you to the place where you can install an operating system; the image I wanted would get me to an SSH prompt. That meant running the installer somewhere, making the configuration decisions there, and preserving its finished disk as the thing I would eventually write to microSD.&lt;/p&gt;
&lt;p&gt;The Mac made a good workshop. QEMU ran a generic ARM64 &lt;code&gt;virt&lt;/code&gt; machine using Apple's Hypervisor Framework acceleration, with four virtual CPUs, 2 GB of RAM, EDK2 UEFI firmware, and virtio disk and network devices. SSH was forwarded to a port on the Mac's loopback interface. I could inspect the console and connect to the guest without giving every intermediate version of the build a presence on the LAN.&lt;/p&gt;
&lt;p&gt;Before installation, we verified the release material. The helper checked the Ed25519 signature on the manifest and the SHA-256 digest of the installer. It also compared the release public keys obtained from the official download location and OpenBSD's source repository. The original downloads and verification results stayed in the project directory with the scripts.&lt;/p&gt;
&lt;p&gt;There would eventually be several images with very similar names. Keeping a record of which bytes had actually been checked seemed like a better plan than trusting my future ability to remember what “final” meant.&lt;/p&gt;
&lt;p&gt;The installer set the hostname to &lt;code&gt;rpi4b&lt;/code&gt;, created my &lt;code&gt;alex&lt;/code&gt; account, enabled SSH, and selected &lt;code&gt;America/Chicago&lt;/code&gt;. We installed the release sets and retained the answer file and customization scripts. OpenBSD then booted as a normal installed guest. At that point, the account, services, and filesystems were all available for testing, and the Pi-specific boot work was still ahead.&lt;/p&gt;
&lt;p&gt;QEMU was providing the guest with UEFI firmware. The Pi would need to reach OpenBSD through its own boot chain, using files on the card. Installing under the generic virtual machine did not automatically arrange all of those files for the physical board.&lt;/p&gt;
&lt;p&gt;The customization script mounted the target's FAT boot partition and populated it with the Pi firmware, device trees, overlays, and U-Boot supplied by the official installer. It checked for the Pi 4's &lt;code&gt;bcm2711-rpi-4-b.dtb&lt;/code&gt; and the EFI loader at &lt;code&gt;efi/boot/bootaa64.efi&lt;/code&gt;. The boot configuration fit in four lines:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="na"&gt;arm_64bit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;enable_uart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;dtoverlay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;disable-bt&lt;/span&gt;
&lt;span class="na"&gt;kernel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;u-boot.bin&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;We compared the resulting configuration, &lt;code&gt;start4.elf&lt;/code&gt;, &lt;code&gt;fixup4.dat&lt;/code&gt;, Pi 4 device tree, U-Boot, Bluetooth-disabling overlay, and EFI loader against the signed installer. All matched. There was a known source for the firmware on this card, and a check that the files had survived the assembly process intact.&lt;/p&gt;
&lt;p&gt;The guest could demonstrate that OpenBSD started and accepted a login. The firmware comparison established that the Pi's boot files matched their source. The eventual hardware boot would connect those observations.&lt;/p&gt;
&lt;p&gt;The network configuration had a similar wrinkle. QEMU's network card appeared as &lt;code&gt;vio0&lt;/code&gt;. The Pi's onboard Ethernet appeared as &lt;code&gt;bse0&lt;/code&gt;, through OpenBSD's &lt;a href="https://man.openbsd.org/bse"&gt;Broadcom GENET driver&lt;/a&gt;. A perfectly functional DHCP configuration for a virtual network card would be of limited assistance to a physical interface with a different name.&lt;/p&gt;
&lt;p&gt;So the image kept its &lt;code&gt;vio0&lt;/code&gt; configuration for future VM testing and gained an &lt;code&gt;/etc/hostname.bse0&lt;/code&gt; containing:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;inet autoconf
inet6 autoconf
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;IPv4 DHCP and IPv6 autoconfiguration. That was the entire wired-network setup. No static address embedded in an image that might eventually land on another network, and no Wi-Fi credentials to get right before the first login. I would find &lt;code&gt;rpi4b&lt;/code&gt; in the router's lease table. Automatic &lt;code&gt;.local&lt;/code&gt; discovery was not configured, so the lease table was part of the intended procedure.&lt;/p&gt;
&lt;p&gt;For SSH, I used the same arrangement I wanted to live with afterward. My public key went into &lt;code&gt;/home/alex/.ssh/authorized_keys&lt;/code&gt;; the private key stayed on the Mac. The server started automatically, with public-key authentication enabled and password authentication, keyboard-interactive authentication, and direct root login disabled.&lt;/p&gt;
&lt;p&gt;There is consequently no default password for this image. Someone repeating the build needs to supply their own public key. These are the access choices for my installation, rather than a claim about what every OpenBSD installation does by default.&lt;/p&gt;
&lt;p&gt;Root access was one line in &lt;code&gt;/etc/doas.conf&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;permit nopass alex as root
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I like how little explanation that line needs. &lt;code&gt;alex&lt;/code&gt; may become root, and another password prompt is not involved. The &lt;a href="https://man.openbsd.org/doas.conf"&gt;doas manual&lt;/a&gt; describes how to restrict rules to particular commands or arguments, but this account is the machine's administrator. Its rule deliberately permits all commands as root. Anyone holding the corresponding login key has that authority.&lt;/p&gt;
&lt;p&gt;The test was equally compact:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;doas&lt;span class="w"&gt; &lt;/span&gt;-n&lt;span class="w"&gt; &lt;/span&gt;id&lt;span class="w"&gt; &lt;/span&gt;-u
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;It printed &lt;code&gt;0&lt;/code&gt;. The &lt;code&gt;-n&lt;/code&gt; makes it noninteractive, so a successful result establishes that the permission actually works without a prompt. We also checked the running SSH daemon and its effective configuration. A line in a file is an intention; being able to log in and run the command is the behavior I was trying to buy with it.&lt;/p&gt;
&lt;p&gt;Then there was the card, and the apparently small request to use the rest of it.&lt;/p&gt;
&lt;p&gt;The portable image was 14 GiB: 15,032,385,536 bytes. Its layout included root, swap, separate &lt;code&gt;/usr&lt;/code&gt; and &lt;code&gt;/home&lt;/code&gt; filesystems, and a firmware partition. It also retained about 7.1 GiB of unallocated space inside the image. Writing it onto a larger card would reproduce that layout faithfully, including its unused space.&lt;/p&gt;
&lt;p&gt;There was no automatic first-boot expansion in this build. The image had exactly the filesystem sizes we gave it. A larger card would not persuade OpenBSD to reconsider them on its own.&lt;/p&gt;
&lt;p&gt;I wanted the extra capacity divided between &lt;code&gt;/usr&lt;/code&gt; and &lt;code&gt;/home&lt;/code&gt;, where installed software and my own work would have room to accumulate. Root, swap, and the boot area could stay as they were. So we made a second image sized to the actual card: &lt;strong&gt;63,864,569,856 bytes&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That number matters more than the “64 GB” on the packaging. Cards sold under the same capacity label can differ in their exact size. An image built to the last usable part of one card needs another card at least that large. The portable version is the smaller starting point; the expanded version is the one prepared for this deployment.&lt;/p&gt;
&lt;p&gt;Here is what &lt;code&gt;df -h&lt;/code&gt; reported for the filesystems, with swap and the boot partition included for context:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Filesystem or area&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Portable image&lt;/th&gt;
&lt;th style="text-align: right;"&gt;Expanded image&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.3 GiB&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.3 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/usr&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;2.9 GiB&lt;/td&gt;
&lt;td style="text-align: right;"&gt;28.3 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;/home&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right;"&gt;1.9 GiB&lt;/td&gt;
&lt;td style="text-align: right;"&gt;27.4 GiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Swap&lt;/td&gt;
&lt;td style="text-align: right;"&gt;256 MiB&lt;/td&gt;
&lt;td style="text-align: right;"&gt;256 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Firmware partition&lt;/td&gt;
&lt;td style="text-align: right;"&gt;260 MiB&lt;/td&gt;
&lt;td style="text-align: right;"&gt;260 MiB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The added space was divided equally. The final totals differ because &lt;code&gt;/usr&lt;/code&gt; started larger. Filesystem sizes are also slightly smaller than their containing partitions, which explains why multiplying a displayed number back into sectors will not reproduce the disklabel.&lt;/p&gt;
&lt;p&gt;The complication was physical ordering. &lt;code&gt;/home&lt;/code&gt; sat after &lt;code&gt;/usr&lt;/code&gt;. Giving &lt;code&gt;/usr&lt;/code&gt; more contiguous space required relocating &lt;code&gt;/home&lt;/code&gt; first. The friendly phrase “expand both partitions” had turned into a sequence where the order of operations mattered quite a bit.&lt;/p&gt;
&lt;p&gt;We did this offline, in an OpenBSD installer VM, with the original image attached read-only alongside the larger target image and installer. The scripts checked the disk capacity, partition offsets, filesystem types, and disk identifier against the expected layout before proceeding. They were intentionally specific to these images. I would rather have a resize script decline a disk it does not recognize than improvise with its contents.&lt;/p&gt;
&lt;p&gt;First came extending the OpenBSD area in the outer partition table and enlarging the disklabel boundary. Then &lt;code&gt;/home&lt;/code&gt; was recreated at its final location, populated from the read-only source, and checked for matching contents and ownership. Only after that copy was verified could &lt;code&gt;/usr&lt;/code&gt; grow into the space that had become available.&lt;/p&gt;
&lt;p&gt;For &lt;code&gt;/usr&lt;/code&gt;, the script checked the existing filesystem, enlarged its partition, ran a &lt;code&gt;growfs&lt;/code&gt; dry run, performed the growth, and checked the filesystem again. OpenBSD's &lt;a href="https://man.openbsd.org/growfs"&gt;growfs documentation&lt;/a&gt; calls for an unmounted filesystem and a filesystem check afterward. Booting the installer gave us an environment where the target's &lt;code&gt;/usr&lt;/code&gt; was simply another unmounted disk partition.&lt;/p&gt;
&lt;p&gt;The expanded filesystems passed their checks. The disk identifier, root, swap, and firmware area were preserved. A normal boot of the expanded image then confirmed that &lt;code&gt;/usr&lt;/code&gt; and &lt;code&gt;/home&lt;/code&gt; mounted where they belonged, at their new sizes.&lt;/p&gt;
&lt;p&gt;Root still has about 1.3 GiB, and &lt;code&gt;/var&lt;/code&gt; lives there in this layout. That is a choice with consequences if the machine later becomes a collector of logs or service data. For the current installation it leaves ample room, but I have not allocated 28 GiB to every directory just by putting a larger card underneath them.&lt;/p&gt;
&lt;p&gt;Before packaging, we applied the available updates. The September 7, 2026 build had patches 001 through 014 installed, with no further patches reported by the update check. That statement has a date attached for a reason; anyone deploying later should check the &lt;a href="https://www.openbsd.org/errata79.html"&gt;OpenBSD 7.9 errata&lt;/a&gt; and run the update tools again.&lt;/p&gt;
&lt;p&gt;We also waited for kernel relinking to finish. Then the image needed to forget the parts of its identity that belonged to the VM: its generated SSH host keys and temporary DHCP lease. The resolver configuration was reset, and the Pi would generate fresh host keys at its own boot. A first-boot &lt;code&gt;fw_update&lt;/code&gt; would detect firmware needs in the physical environment.&lt;/p&gt;
&lt;p&gt;My authorized public key stayed. That key says who may administer the machine. The host keys say which machine has answered the connection. Cloning the first is the point of this personalized image; generating the second during deployment gives the running system its own SSH identity.&lt;/p&gt;
&lt;p&gt;Both variants booted independently in QEMU. SSH login worked, &lt;code&gt;sshd&lt;/code&gt; was running, &lt;code&gt;doas&lt;/code&gt; returned root, and the effective SSH settings and filesystem mounts matched the intended configuration. Each guest shut down cleanly before packaging. We compressed the raw images with gzip and verified that decompression reproduced the originals, along with checks of the partition table and boot partition.&lt;/p&gt;
&lt;p&gt;The compressed files were about 644 MiB and 738 MiB. Their expanded disk sizes were considerably larger, but empty space is cooperative material for a compressor. A SHA-256 manifest recorded the packaged artifacts.&lt;/p&gt;
&lt;p&gt;Writing the expanded image from macOS used the usual whole-device procedure:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;diskutil&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;external&lt;span class="w"&gt; &lt;/span&gt;physical
diskutil&lt;span class="w"&gt; &lt;/span&gt;info&lt;span class="w"&gt; &lt;/span&gt;/dev/diskN
diskutil&lt;span class="w"&gt; &lt;/span&gt;unmountDisk&lt;span class="w"&gt; &lt;/span&gt;/dev/diskN
sudo&lt;span class="w"&gt; &lt;/span&gt;dd&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;images/openbsd-7.9-rpi4b-64gb.img&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/rdiskN&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4m
sync
diskutil&lt;span class="w"&gt; &lt;/span&gt;eject&lt;span class="w"&gt; &lt;/span&gt;/dev/diskN
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;N&lt;/code&gt; must be the verified card identifier, and the target must be large enough. That command replaces the whole device's contents. It takes the raw &lt;code&gt;.img&lt;/code&gt;; a gzip file needs decompression first. On macOS, Control-T asks &lt;code&gt;dd&lt;/code&gt; for a progress report, which is useful when the terminal has spent several minutes giving every appearance of having lost interest.&lt;/p&gt;
&lt;p&gt;The entire 63,864,569,856-byte write completed. I stopped the subsequent full-card readback before it finished, and the card was safely ejected. The records therefore say exactly that: complete image-package verification, complete card write, incomplete card readback verification. The physical boot that followed supplied different evidence; it did not retroactively checksum the unread part of the card.&lt;/p&gt;
&lt;p&gt;With the card in the Pi and Ethernet connected, the machine booted. The router assigned &lt;code&gt;10.1.1.32&lt;/code&gt; on my &lt;code&gt;10.1.1.0/24&lt;/code&gt; network, under the hostname &lt;code&gt;rpi4b&lt;/code&gt;. I connected with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;~/.ssh/id_ed25519&lt;span class="w"&gt; &lt;/span&gt;alex@10.1.1.32
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;OpenBSD 7.9, arm64. The boot log identified the Pi 4 Model B Rev 1.5 and all four Cortex-A72 cores. &lt;code&gt;rcctl check sshd&lt;/code&gt; returned a running service. &lt;code&gt;doas -n id -u&lt;/code&gt; printed &lt;code&gt;0&lt;/code&gt;. &lt;code&gt;/usr&lt;/code&gt; mounted at 28.3 GiB and &lt;code&gt;/home&lt;/code&gt; at 27.4 GiB.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bse0&lt;/code&gt; reported &lt;code&gt;1000baseT full-duplex&lt;/code&gt;, with the link active. That is a negotiated link speed, not a throughput benchmark. The session established that wired networking and SSH worked on the real board. The expanded image received this physical test; the portable variant had its independent VM validation but was not separately booted on the Pi.&lt;/p&gt;
&lt;p&gt;That was the handoff completed: the boot files found the operating system, the operating system found its hardware, and the configuration I had tested on the Mac became the configuration of a machine on the network.&lt;/p&gt;
&lt;h3&gt;A download for the next card&lt;/h3&gt;
&lt;p&gt;An image containing my login key had a rather limited audience. For the downloadable version, we created fresh filesystems, copied the system into them, and removed my account, keys, history, and inherited machine identities. Fresh filesystems also keep deleted personal files out of the free space. Each deployment generates its own SSH host keys.&lt;/p&gt;
&lt;p&gt;The generic archive is about &lt;strong&gt;525 MiB&lt;/strong&gt;, expanding to &lt;strong&gt;63,864,569,856 bytes&lt;/strong&gt;. Check your card's actual capacity; a “64 GB” label is not enough. This version keeps separate &lt;code&gt;/usr&lt;/code&gt; and &lt;code&gt;/home&lt;/code&gt; filesystems, reporting about 28.4 GiB and 27.4 GiB after fresh formatting. There is &lt;strong&gt;no automatic expansion&lt;/strong&gt; on larger cards.&lt;/p&gt;
&lt;p&gt;Download the image and &lt;code&gt;SHA256&lt;/code&gt; from the links above into one directory. On macOS, verify the download before decompressing it:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;shasum&lt;span class="w"&gt; &lt;/span&gt;-a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;256&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--ignore-missing&lt;span class="w"&gt; &lt;/span&gt;-c&lt;span class="w"&gt; &lt;/span&gt;SHA256
gzip&lt;span class="w"&gt; &lt;/span&gt;-dc&lt;span class="w"&gt; &lt;/span&gt;openbsd-7.9-rpi4b-generic.img.gz&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;openbsd-7.9-rpi4b-generic.img
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Continue only if verification reports the image as &lt;code&gt;OK&lt;/code&gt;. Linux uses &lt;code&gt;sha256sum --ignore-missing -c SHA256&lt;/code&gt;. The manifest also covers the companion files; missing companions are skipped. Allow roughly 64 GB of free space for decompression.&lt;/p&gt;
&lt;p&gt;Flash the resulting &lt;code&gt;.img&lt;/code&gt; using the whole-card procedure above, substituting the generic filename. Reinsert the card afterward and find its small FAT boot volume. Copy your &lt;strong&gt;public&lt;/strong&gt; key into that volume's root directory, replacing &lt;code&gt;BOOT_VOLUME&lt;/code&gt; with its actual mounted name:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;cp&lt;span class="w"&gt; &lt;/span&gt;~/.ssh/id_ed25519.pub&lt;span class="w"&gt; &lt;/span&gt;/Volumes/BOOT_VOLUME/authorized_keys
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The private key stays on your computer. Optionally add a plain-text &lt;code&gt;setup.conf&lt;/code&gt; beside &lt;code&gt;authorized_keys&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;openbsd&lt;/span&gt;
&lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;openbsd-rpi4b&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Those are the defaults; the timezone is UTC. Safely eject the card, boot the Pi with Ethernet connected, and find its address in the DHCP leases. Then:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;~/.ssh/id_ed25519&lt;span class="w"&gt; &lt;/span&gt;openbsd@PI_ADDRESS
doas&lt;span class="w"&gt; &lt;/span&gt;-s
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;There is &lt;strong&gt;no default password&lt;/strong&gt;. SSH stays disabled until a valid public key is supplied, then permits key login for the owner. Direct root SSH login is disabled; &lt;code&gt;doas&lt;/code&gt; grants root without a password. Missing or malformed setup input can be corrected on the FAT partition and retried at the next boot.&lt;/p&gt;
&lt;p&gt;Disposable VM copies passed owner setup, SSH, &lt;code&gt;doas&lt;/code&gt;, reboot persistence, and installation of a signed package through &lt;code&gt;pkg_add&lt;/code&gt;. Unrelated keys and root logins were rejected. The &lt;a href="https://tinycomputers.io/downloads/openbsd-7.9-rpi4b-generic-20260907/VALIDATION.json"&gt;validation record&lt;/a&gt; and &lt;a href="https://tinycomputers.io/downloads/openbsd-7.9-rpi4b-generic-20260907/openbsd-7.9-rpi4b-generic-firstboot.tar.gz"&gt;first-boot helper source&lt;/a&gt; are included. The generic image retains the boot firmware from my working card, but it still needs its own physical Pi test. The successful hardware session above belongs to the personalized build.&lt;/p&gt;
&lt;p&gt;For now, &lt;code&gt;rpi4b&lt;/code&gt; has an address and an SSH prompt. I can install software, copy a project over, or become root without reaching for a monitor. The card arrived with the installation work already behind it, which was the whole ambition when this was still a modest list.&lt;/p&gt;</description><category>arm64</category><category>bsd</category><category>disk images</category><category>doas</category><category>firmware</category><category>macos</category><category>microsd</category><category>openbsd</category><category>openbsd 7.9</category><category>qemu</category><category>raspberry pi</category><category>raspberry pi 4</category><category>single board computers</category><category>ssh</category><category>u-boot</category><guid>https://tinycomputers.io/posts/openbsd-7-9-on-the-raspberry-pi-4b.html</guid><pubDate>Mon, 07 Sep 2026 22:06:31 GMT</pubDate></item><item><title>Building Cross-Platform Rust Binaries: A Multi-Architecture Build Orchestration System</title><link>https://tinycomputers.io/posts/multi-operating-system-and-multi-architecture-build-orchestration-system.html?utm_source=feed&amp;utm_medium=rss&amp;utm_campaign=rss</link><dc:creator>A.C. Jokela</dc:creator><description>&lt;p&gt;When developing &lt;a href="https://baud.rs/QckusG"&gt;ballistics-engine&lt;/a&gt;, a high-performance ballistics calculation library written in Rust, I faced a challenge: how do I efficiently build and distribute binaries for multiple operating systems and architectures? The answer led to the creation of an automated build orchestration system that leverages diverse hardware (from single-board computers to powerful x86_64 servers) to build native binaries for macOS, Linux, FreeBSD, NetBSD, and OpenBSD across both ARM64 and x86_64 architectures.  Now, you are probably wondering why I am bothering to show love for the BSD Trilogy; the answer is simple: because I want to.  Sure they are a bit esoteric, but I ran FreeBSD for years as my mail server.  I still like the BSDs.&lt;/p&gt;
&lt;p&gt;This article explores the architecture, implementation, and lessons learned from building a production-grade multi-platform build system that powers &lt;a href="https://baud.rs/zuncyr"&gt;https://ballistics.zip&lt;/a&gt;, where users can download pre-built binaries for their platform with a simple curl command.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;curl --proto '=https' --tlsv1.2 -sSf https://ballistics.zip/install.sh | sh
&lt;/pre&gt;&lt;/div&gt;

&lt;div class="audio-widget"&gt;
&lt;div class="audio-widget-header"&gt;
&lt;span class="audio-widget-icon"&gt;🎧&lt;/span&gt;
&lt;span class="audio-widget-label"&gt;Listen to this article&lt;/span&gt;
&lt;/div&gt;
&lt;audio controls preload="metadata"&gt;
&lt;source src="https://tinycomputers.io/multi-operating-system-and-multi-architecture-build-orchestration-system_tts.mp3" type="audio/mpeg"&gt;
&lt;/source&gt;&lt;/audio&gt;
&lt;div class="audio-widget-footer"&gt;12 min · AI-generated narration&lt;/div&gt;
&lt;/div&gt;

&lt;h3&gt;The Problem: Cross-Platform Distribution&lt;/h3&gt;
&lt;p&gt;Rust's cross-compilation capabilities are impressive, but they have limitations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cross-compilation complexity: While Rust supports cross-compilation, getting it working reliably for BSD systems (especially with system dependencies) is challenging&lt;/li&gt;
&lt;li&gt;Native testing: You need to test on actual hardware to ensure binaries work correctly&lt;/li&gt;
&lt;li&gt;Binary compatibility: Different BSD versions and configurations require native builds&lt;/li&gt;
&lt;li&gt;Performance verification: Emulated builds may behave differently than native ones&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The solution? Build natively on each target platform using actual hardware or high-performance emulation.&lt;/p&gt;
&lt;h3&gt;Architecture Overview&lt;/h3&gt;
&lt;p&gt;The build orchestration system consists of three main components:&lt;/p&gt;
&lt;h4&gt;1. Build Nodes (Physical and Virtual Machines)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;macOS systems (x86_64 and aarch64) - Local builds&lt;/li&gt;
&lt;li&gt;Linux x86_64 server - Remote build via SSH&lt;/li&gt;
&lt;li&gt;FreeBSD ARM64 - Single-board computer (Raspberry Pi 4)&lt;/li&gt;
&lt;li&gt;OpenBSD ARM64 - QEMU VM emulated on x86_64 (rig.localnet)&lt;/li&gt;
&lt;li&gt;NetBSD x86_64 and ARM64 - QEMU VMs&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;2. Orchestrator (Python-based coordinator)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Reads build node configuration from &lt;code&gt;build-nodes.yaml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Executes builds in parallel across all nodes&lt;/li&gt;
&lt;li&gt;Collects artifacts via SSH/SCP&lt;/li&gt;
&lt;li&gt;Generates SHA256 checksums&lt;/li&gt;
&lt;li&gt;Uploads to Google Cloud Storage&lt;/li&gt;
&lt;li&gt;Updates version metadata&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;3. Distribution (ballistics.zip website)&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Serves install script at https://ballistics.zip&lt;/li&gt;
&lt;li&gt;Hosts binaries in GCS bucket (&lt;code&gt;gs://ballistics-releases/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Provides version detection and automatic downloads&lt;/li&gt;
&lt;li&gt;Supports version fallback for platforms with delayed releases&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Hardware Infrastructure&lt;/h3&gt;
&lt;h4&gt;Single-Board Computers&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://baud.rs/1RRLno"&gt;Orange Pi 5 Max (ARM64)&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Role: Host for NetBSD ARM64 VM&lt;/li&gt;
&lt;li&gt;CPU: Rockchip RK3588 (8-core ARM Cortex-A76/A55)&lt;/li&gt;
&lt;li&gt;RAM: 16GB&lt;/li&gt;
&lt;li&gt;Why: Native ARM64 hardware for running QEMU VMs&lt;/li&gt;
&lt;li&gt;Host IP: 10.1.1.10&lt;/li&gt;
&lt;li&gt;VM IPs:&lt;/li&gt;
&lt;li&gt;NetBSD ARM64: 10.1.1.15&lt;/li&gt;
&lt;li&gt;OpenBSD ARM64 (native, disabled): 10.1.1.11&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://baud.rs/zn9Y3Y"&gt;Raspberry Pi 4 (ARM64)&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Role: FreeBSD ARM64 native builds&lt;/li&gt;
&lt;li&gt;CPU: Broadcom BCM2711 (quad-core Cortex-A72)&lt;/li&gt;
&lt;li&gt;RAM: 8GB&lt;/li&gt;
&lt;li&gt;Why: Stable FreeBSD support, reliable ARM64 platform&lt;/li&gt;
&lt;li&gt;IP: 10.1.1.7&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;x86_64 ("rig.localnet")&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Role: Linux builds, BSD VM host, emulated ARM64 builds&lt;/li&gt;
&lt;li&gt;CPU: Intel i9&lt;/li&gt;
&lt;li&gt;RAM: 96GB&lt;/li&gt;
&lt;li&gt;IP: 10.1.1.27 (Linux host), 10.1.1.17 (KVM host)&lt;/li&gt;
&lt;li&gt;VMs Hosted:&lt;/li&gt;
&lt;li&gt;FreeBSD x86_64: 10.1.1.21&lt;/li&gt;
&lt;li&gt;OpenBSD x86_64: 10.1.1.20&lt;/li&gt;
&lt;li&gt;OpenBSD ARM64 (emulated): 10.1.1.23&lt;/li&gt;
&lt;li&gt;NetBSD x86_64: 10.1.1.19&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Local macOS Development Machine&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Role: macOS binary builds (both architectures)&lt;/li&gt;
&lt;li&gt;Build Method: Local cargo builds with target flags&lt;/li&gt;
&lt;li&gt;Architectures:&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aarch64-apple-darwin&lt;/code&gt; (Apple Silicon)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;x86_64-apple-darwin&lt;/code&gt; (Intel Macs)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;A Surprising Discovery: Emulated ARM64 Performance&lt;/h3&gt;
&lt;p&gt;One of the most interesting findings during development was discovering that emulated ARM64 builds on powerful x86_64 hardware are significantly faster than emulated ARM64 on native ARM64 builds on single-board computers.&lt;/p&gt;
&lt;h4&gt;Performance Comparison&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Emulated ARM64 on ARM64: ~99+ minutes per build&lt;/li&gt;
&lt;li&gt;Emulated ARM64 on x86_64: 15m 37s ⚡&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The emulated build on rig.localnet (running QEMU with KVM acceleration) completed in about 6x less time than the native ARM64 hardware. This is because:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The x86_64 server has significantly more powerful CPU cores&lt;/li&gt;
&lt;li&gt;QEMU with KVM provides near-native performance for many workloads&lt;/li&gt;
&lt;li&gt;Rust compilation is primarily CPU-bound and benefits from faster single-core performance&lt;/li&gt;
&lt;li&gt;The x86_64 server has faster storage (NVMe vs eMMC/SD card)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As a result, the native OpenBSD ARM64 node on the Orange Pi is now disabled in favor of the emulated version.&lt;/p&gt;
&lt;h3&gt;Prerequisites&lt;/h3&gt;
&lt;h4&gt;SSH Key-Based Authentication&lt;/h4&gt;
&lt;p&gt;Critical: The orchestration system requires passwordless SSH access to all remote build nodes. Here's how to set it up:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Generate SSH key (if you don't have one):&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;ssh-keygen&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;ed25519&lt;span class="w"&gt; &lt;/span&gt;-C&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"build-orchestrator"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Copy public key to each build node:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# For each build node&lt;/span&gt;
ssh-copy-id&lt;span class="w"&gt; &lt;/span&gt;user@build-node-ip

&lt;span class="c1"&gt;# Examples:&lt;/span&gt;
ssh-copy-id&lt;span class="w"&gt; &lt;/span&gt;alex@10.1.1.27&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="c1"&gt;# Linux x86_64&lt;/span&gt;
ssh-copy-id&lt;span class="w"&gt; &lt;/span&gt;freebsd@10.1.1.7&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="c1"&gt;# FreeBSD ARM64&lt;/span&gt;
ssh-copy-id&lt;span class="w"&gt; &lt;/span&gt;root@10.1.1.20&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;# OpenBSD x86_64&lt;/span&gt;
ssh-copy-id&lt;span class="w"&gt; &lt;/span&gt;root@10.1.1.23&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;# OpenBSD ARM64 emulated&lt;/span&gt;
ssh-copy-id&lt;span class="w"&gt; &lt;/span&gt;root@10.1.1.19&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;# NetBSD x86_64&lt;/span&gt;
ssh-copy-id&lt;span class="w"&gt; &lt;/span&gt;root@10.1.1.15&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="c1"&gt;# NetBSD ARM64&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Test SSH access:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;user@build-node-ip&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"uname -a"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Software Requirements&lt;/h4&gt;
&lt;p&gt;On Build Orchestrator Machine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python 3.8+&lt;/li&gt;
&lt;li&gt;pyyaml (&lt;code&gt;pip install pyyaml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Google Cloud SDK (&lt;code&gt;gcloud&lt;/code&gt; command) for GCS uploads&lt;/li&gt;
&lt;li&gt;SSH client&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On Each Build Node:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rust toolchain (&lt;code&gt;cargo&lt;/code&gt;, &lt;code&gt;rustc&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Build essentials (compiler, linker)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;wget&lt;/code&gt;, or &lt;code&gt;ftp&lt;/code&gt; (for downloading source)&lt;/li&gt;
&lt;li&gt;Sufficient disk space (~2GB for build artifacts)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;BSD-Specific Requirements&lt;/h4&gt;
&lt;p&gt;NetBSD: Install curl via pkgsrc (native &lt;code&gt;ftp&lt;/code&gt; doesn't support HTTPS)&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# Bootstrap pkgsrc&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/usr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ftp&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;pkgsrc.tar.gz&lt;span class="w"&gt; &lt;/span&gt;http://cdn.netbsd.org/pub/pkgsrc/current/pkgsrc.tar.gz
tar&lt;span class="w"&gt; &lt;/span&gt;-xzf&lt;span class="w"&gt; &lt;/span&gt;pkgsrc.tar.gz
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/usr/pkgsrc/bootstrap&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;./bootstrap&lt;span class="w"&gt; &lt;/span&gt;--prefix&lt;span class="o"&gt;=&lt;/span&gt;/usr/pkg

&lt;span class="c1"&gt;# Install curl&lt;/span&gt;
/usr/pkg/bin/pkgin&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;update
/usr/pkg/bin/pkgin&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;curl
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;OpenBSD: Native &lt;code&gt;ftp&lt;/code&gt; supports HTTPS&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;pkg_add&lt;span class="w"&gt; &lt;/span&gt;rust&lt;span class="w"&gt; &lt;/span&gt;git
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;FreeBSD: Use pkg for everything&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;pkg&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;-y&lt;span class="w"&gt; &lt;/span&gt;rust&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;curl
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;The ballistics.zip Website and Install Script&lt;/h3&gt;
&lt;h4&gt;How It Works&lt;/h4&gt;
&lt;p&gt;&lt;a href="https://baud.rs/zuncyr"&gt;https://ballistics.zip&lt;/a&gt; serves as the primary distribution point for pre-built ballistics-engine binaries. The system uses:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;GCS Bucket:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;gs://ballistics-releases/&lt;/code&gt; - Binary artifacts&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CDN: Google Cloud CDN provides global distribution&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Script: Universal installer that:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Detects OS and architecture&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Downloads appropriate binary&lt;/li&gt;
&lt;li&gt;Verifies SHA256 checksum&lt;/li&gt;
&lt;li&gt;Installs to &lt;code&gt;/usr/local/bin&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;Usage&lt;/h4&gt;
&lt;p&gt;Basic installation:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sSL&lt;span class="w"&gt; &lt;/span&gt;https://ballistics.zip/install.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Specific version:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sSL&lt;span class="w"&gt; &lt;/span&gt;https://ballistics.zip/install.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;--version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.13.3
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Different install location:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sSL&lt;span class="w"&gt; &lt;/span&gt;https://ballistics.zip/install.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bash&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;--prefix&lt;span class="w"&gt; &lt;/span&gt;~/.local
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Install Script Architecture&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;install.sh&lt;/code&gt; script intelligently handles:&lt;/p&gt;
&lt;p&gt;Platform Detection:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;OS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;uname&lt;span class="w"&gt; &lt;/span&gt;-s&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;tr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'[:upper:]'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'[:lower:]'&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;uname&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="k"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ARCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;x86_64&lt;span class="p"&gt;|&lt;/span&gt;amd64&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"x86_64"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;aarch64&lt;span class="p"&gt;|&lt;/span&gt;arm64&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"aarch64"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;*&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Unsupported architecture: &lt;/span&gt;&lt;span class="nv"&gt;$ARCH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;

&lt;span class="nv"&gt;PLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OS&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# e.g., "openbsd-aarch64"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Version Fallback:
If a requested version isn't available for a platform, the script automatically finds the latest available version:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="c1"&gt;# If openbsd-aarch64 0.13.3 doesn't exist, fall back to 0.13.2&lt;/span&gt;
&lt;span class="nv"&gt;AVAILABLE_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;curl&lt;span class="w"&gt; &lt;/span&gt;-sL&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$BASE_URL&lt;/span&gt;/versions.txt&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;grep&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^&lt;/span&gt;&lt;span class="nv"&gt;$PLATFORM&lt;/span&gt;&lt;span class="s2"&gt;:"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;cut&lt;span class="w"&gt; &lt;/span&gt;-d:&lt;span class="w"&gt; &lt;/span&gt;-f2&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Checksum Verification:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nv"&gt;EXPECTED_SHA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BINARY&lt;/span&gt;&lt;span class="s2"&gt;.sha256"&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;ACTUAL_SHA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;sha256sum&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BINARY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;awk&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'{print $1}'&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$EXPECTED_SHA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;!&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ACTUAL_SHA&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Checksum verification failed!"&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Build Orchestration System Deep Dive&lt;/h3&gt;
&lt;h4&gt;Configuration: build-nodes.yaml&lt;/h4&gt;
&lt;p&gt;The heart of the system is &lt;code&gt;build-nodes.yaml&lt;/code&gt;, which defines all build targets:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nt"&gt;nodes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# macOS builds (local machine)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;macos-aarch64&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;local&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;aarch64-apple-darwin&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;build_command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cd /tmp &amp;amp;&amp;amp; rm -rf ballistics-engine-{version}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;curl -L -o v{version}.tar.gz https://github.com/ajokela/ballistics-engine/archive/refs/tags/v{version}.tar.gz&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;tar xzf v{version}.tar.gz&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cd ballistics-engine-{version}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cargo build --release --target {target}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;binary_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/tmp/ballistics-engine-{version}/target/{target}/release/ballistics&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# Linux x86_64 (remote via SSH)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;linux-x86_64&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;alex@10.1.1.27&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;x86_64-unknown-linux-gnu&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;build_command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cd /tmp &amp;amp;&amp;amp; rm -rf ballistics-engine-{version}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;wget -q https://github.com/ajokela/ballistics-engine/archive/refs/tags/v{version}.tar.gz&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;tar xzf v{version}.tar.gz&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cd ballistics-engine-{version}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;~/.cargo/bin/cargo build --release --target {target}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;binary_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/tmp/ballistics-engine-{version}/target/{target}/release/ballistics&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# OpenBSD ARM64 emulated (FASTEST ARM64 BUILD!)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;openbsd-aarch64-emulated&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;root@10.1.1.23&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;aarch64-unknown-openbsd&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;build_command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cd /tmp &amp;amp;&amp;amp; rm -rf ballistics-engine-{version}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;ftp -o v{version}.tar.gz https://github.com/ajokela/ballistics-engine/archive/refs/tags/v{version}.tar.gz&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;tar xzf v{version}.tar.gz&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cd ballistics-engine-{version}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cargo build --release&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;binary_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/tmp/ballistics-engine-{version}/target/release/ballistics&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;

&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# NetBSD x86_64 (HTTPS support via pkgsrc curl)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;netbsd-x86_64&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;root@10.1.1.19&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;x86_64-unknown-netbsd&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;build_command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cd /tmp &amp;amp;&amp;amp; rm -rf ballistics-engine-{version}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;/usr/pkg/bin/curl -L -o v{version}.tar.gz https://github.com/ajokela/ballistics-engine/archive/refs/tags/v{version}.tar.gz&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;tar xzf v{version}.tar.gz&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cd ballistics-engine-{version}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;/usr/pkg/bin/cargo build --release&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;binary_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/tmp/ballistics-engine-{version}/target/release/ballistics&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Orchestrator Workflow&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;orchestrator.py&lt;/code&gt; script coordinates the entire build process:&lt;/p&gt;
&lt;p&gt;Step 1: Parallel Build Execution&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;build_on_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'host'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'local'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Local build&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;build_command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Remote build via SSH&lt;/span&gt;
        &lt;span class="n"&gt;ssh_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"ssh &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'host'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; '&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;build_command&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ssh_command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Step 2: Artifact Collection&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;collect_artifacts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;binary_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"ballistics-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'host'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'local'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;shutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'binary_path'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"./&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;binary_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Download via SCP&lt;/span&gt;
        &lt;span class="n"&gt;scp_command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"scp &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'host'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'binary_path'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ./&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;binary_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scp_command&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Step 3: Checksum Generation&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;generate_checksum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binary_path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;binary_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'rb'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sha256&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;binary_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.sha256"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Step 4: Upload to GCS&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;upload_to_gcs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;bucket_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"gs://ballistics-releases/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;

    &lt;span class="c1"&gt;# Upload binaries and checksums&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"gsutil -m cp ballistics-* &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Set public read permissions&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;"gsutil -m acl ch -u AllUsers:R &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;bucket_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Update latest-version.txt&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'latest-version.txt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"gsutil cp latest-version.txt gs://ballistics-releases/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;Running a Build&lt;/h4&gt;
&lt;p&gt;Dry-run (test without uploading):&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;build-orchestrator
./build.sh&lt;span class="w"&gt; &lt;/span&gt;--version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.13.4&lt;span class="w"&gt; &lt;/span&gt;--dry-run
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Production build:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;./build.sh&lt;span class="w"&gt; &lt;/span&gt;--version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.13.4
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Output:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="n"&gt;Building&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ballistics&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v0&lt;/span&gt;&lt;span class="mf"&gt;.13.4&lt;/span&gt;
&lt;span class="o"&gt;===========================================&lt;/span&gt;

&lt;span class="n"&gt;Enabled&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;nodes&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;macos&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aarch64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;macos&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;local&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alex&lt;/span&gt;&lt;span class="mf"&gt;@10.1.1.27&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;freebsd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aarch64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;freebsd&lt;/span&gt;&lt;span class="mf"&gt;@10.1.1.7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;openbsd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aarch64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;emulated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="mf"&gt;@10.1.1.23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;netbsd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="mf"&gt;@10.1.1.19&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;netbsd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aarch64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="mf"&gt;@10.1.1.15&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;Starting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;parallel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;builds&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;macos&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aarch64&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Building&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12345&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Building&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PID&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12346&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;macos&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aarch64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;linux&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;x86_64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;freebsd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aarch64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;openbsd&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;aarch64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;emulated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;⚡&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;FASTEST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ARM64&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="n"&gt;Uploading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;gs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//ballistics-releases/0.13.4/&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Uploaded&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;binaries&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Uploaded&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;checksums&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Updated&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;latest&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;txt&lt;/span&gt;

&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;🎉&lt;/span&gt;
&lt;span class="n"&gt;Total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;Adding New Build Nodes&lt;/h3&gt;
&lt;h4&gt;Interactive Script&lt;/h4&gt;
&lt;p&gt;The easiest way to add a new node is using the interactive script:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;build-orchestrator
./add-node.sh
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This will prompt you for:
- Node name (e.g., &lt;code&gt;openbsd-aarch64-emulated&lt;/code&gt;)
- SSH host (e.g., &lt;code&gt;root@10.1.1.23&lt;/code&gt; or &lt;code&gt;local&lt;/code&gt;)
- Rust target triple (e.g., &lt;code&gt;aarch64-unknown-openbsd&lt;/code&gt;)
- Build commands (how to download and build)
- Binary location (where the compiled binary is located)&lt;/p&gt;
&lt;h4&gt;Manual Configuration&lt;/h4&gt;
&lt;p&gt;Alternatively, edit &lt;code&gt;build-nodes.yaml&lt;/code&gt; directly:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;your-new-platform&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;user@ip-address&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;# or 'local' for local builds&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;rust-target-triple&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;build_command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p p-Indicator"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;# Commands to download source and build&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cd /tmp &amp;amp;&amp;amp; rm -rf ballistics-engine-{version}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;curl -L -o v{version}.tar.gz https://github.com/...&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;tar xzf v{version}.tar.gz&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cd ballistics-engine-{version}&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="no"&gt;cargo build --release&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;binary_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;/path/to/compiled/binary&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;true&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Variables:
- &lt;code&gt;{version}&lt;/code&gt;: Replaced with target version (e.g., &lt;code&gt;0.13.4&lt;/code&gt;)
- &lt;code&gt;{target}&lt;/code&gt;: Replaced with Rust target triple&lt;/p&gt;
&lt;h4&gt;Setting Up a New VM&lt;/h4&gt;
&lt;p&gt;Example: OpenBSD ARM64 Emulated&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create VM on host:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;alex@rig.localnet
&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/opt/bsd-vms/openbsd-arm64-emulated
&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Create boot script:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;boot.sh&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; 'EOF'&lt;/span&gt;
&lt;span class="s"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="s"&gt;exec qemu-system-aarch64 \&lt;/span&gt;
&lt;span class="s"&gt;  -M virt,highmem=off \&lt;/span&gt;
&lt;span class="s"&gt;  -cpu cortex-a57 \&lt;/span&gt;
&lt;span class="s"&gt;  -smp 4 \&lt;/span&gt;
&lt;span class="s"&gt;  -m 2G \&lt;/span&gt;
&lt;span class="s"&gt;  -bios /usr/share/qemu-efi-aarch64/QEMU_EFI.fd \&lt;/span&gt;
&lt;span class="s"&gt;  -drive file=openbsd.qcow2,if=virtio,format=qcow2 \&lt;/span&gt;
&lt;span class="s"&gt;  -netdev bridge,id=net0,br=br0 \&lt;/span&gt;
&lt;span class="s"&gt;  -device virtio-net-pci,netdev=net0,romfile=,mac=52:54:00:12:34:99 \&lt;/span&gt;
&lt;span class="s"&gt;  -nographic&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;
chmod&lt;span class="w"&gt; &lt;/span&gt;+x&lt;span class="w"&gt; &lt;/span&gt;boot.sh
&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Create systemd service:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/systemd/system/openbsd-arm64-emulated-vm.service&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;lt;&amp;lt; 'EOF'&lt;/span&gt;
&lt;span class="s"&gt;[Unit]&lt;/span&gt;
&lt;span class="s"&gt;Description=OpenBSD ARM64 VM (Emulated on x86_64)&lt;/span&gt;
&lt;span class="s"&gt;After=network.target&lt;/span&gt;

&lt;span class="s"&gt;[Service]&lt;/span&gt;
&lt;span class="s"&gt;Type=simple&lt;/span&gt;
&lt;span class="s"&gt;User=alex&lt;/span&gt;
&lt;span class="s"&gt;WorkingDirectory=/opt/bsd-vms/openbsd-arm64-emulated&lt;/span&gt;
&lt;span class="s"&gt;ExecStart=/opt/bsd-vms/openbsd-arm64-emulated/boot.sh&lt;/span&gt;
&lt;span class="s"&gt;Restart=always&lt;/span&gt;
&lt;span class="s"&gt;RestartSec=10&lt;/span&gt;

&lt;span class="s"&gt;[Install]&lt;/span&gt;
&lt;span class="s"&gt;WantedBy=multi-user.target&lt;/span&gt;
&lt;span class="s"&gt;EOF&lt;/span&gt;

sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;openbsd-arm64-emulated-vm.service
sudo&lt;span class="w"&gt; &lt;/span&gt;systemctl&lt;span class="w"&gt; &lt;/span&gt;start&lt;span class="w"&gt; &lt;/span&gt;openbsd-arm64-emulated-vm.service
&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Configure networking (assign static IP 10.1.1.23)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install build tools inside VM:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;root@10.1.1.23
pkg_add&lt;span class="w"&gt; &lt;/span&gt;rust&lt;span class="w"&gt; &lt;/span&gt;git
&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Test SSH access:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;ssh&lt;span class="w"&gt; &lt;/span&gt;root@10.1.1.23&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cargo --version"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Add to build-nodes.yaml and test:&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;./build.sh&lt;span class="w"&gt; &lt;/span&gt;--version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.13.3&lt;span class="w"&gt; &lt;/span&gt;--dry-run
&lt;/pre&gt;&lt;/div&gt;

&lt;h3&gt;GitHub Webhook Integration (Optional)&lt;/h3&gt;
&lt;p&gt;For fully automated builds triggered by GitHub releases:&lt;/p&gt;
&lt;h4&gt;1. Deploy Webhook Receiver to Cloud Run&lt;/h4&gt;
&lt;div class="code"&gt;&lt;pre class="code literal-block"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;build-orchestrator
gcloud&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;deploy&lt;span class="w"&gt; &lt;/span&gt;ballistics-build-webhook&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--source&lt;span class="w"&gt; &lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--region&lt;span class="w"&gt; &lt;/span&gt;us-central1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--allow-unauthenticated&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;--set-env-vars&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_WEBHOOK_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-secret-here
&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;2. Configure GitHub Webhook&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Go to: https://github.com/yourusername/your-repo/settings/hooks&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add webhook:&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Payload URL: &lt;code&gt;https://ballistics-build-webhook-xxx.run.app/webhook&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Content type: &lt;code&gt;application/json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Secret: Your webhook secret&lt;/li&gt;
&lt;li&gt;Events: Select "Releases" only&lt;/li&gt;
&lt;/ol&gt;
&lt;h4&gt;3. Test&lt;/h4&gt;
&lt;p&gt;Create a new release on GitHub, and the webhook will automatically trigger builds for all platforms!&lt;/p&gt;
&lt;h3&gt;Performance Metrics and Insights&lt;/h3&gt;
&lt;p&gt;From real-world builds of ballistics-engine v0.13.3:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Hardware&lt;/th&gt;
&lt;th&gt;Build Time&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;macOS aarch64&lt;/td&gt;
&lt;td&gt;Apple M1/M2&lt;/td&gt;
&lt;td&gt;45s&lt;/td&gt;
&lt;td&gt;Native Apple Silicon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;macOS x86_64&lt;/td&gt;
&lt;td&gt;Intel i7/i9&lt;/td&gt;
&lt;td&gt;30s&lt;/td&gt;
&lt;td&gt;Cross-compile on Apple Silicon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux x86_64&lt;/td&gt;
&lt;td&gt;Xeon/EPYC&lt;/td&gt;
&lt;td&gt;25s&lt;/td&gt;
&lt;td&gt;Fastest overall ⚡&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FreeBSD aarch64&lt;/td&gt;
&lt;td&gt;Raspberry Pi 4&lt;/td&gt;
&lt;td&gt;6m 32s&lt;/td&gt;
&lt;td&gt;Native ARM64 hardware&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenBSD aarch64 (emulated)&lt;/td&gt;
&lt;td&gt;x86_64 QEMU&lt;/td&gt;
&lt;td&gt;15m 37s&lt;/td&gt;
&lt;td&gt;⚡ FASTEST ARM64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenBSD aarch64 (native)&lt;/td&gt;
&lt;td&gt;Orange Pi 5 Max&lt;/td&gt;
&lt;td&gt;99+ min&lt;/td&gt;
&lt;td&gt;Disabled due to slower speed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NetBSD x86_64&lt;/td&gt;
&lt;td&gt;x86_64 VM&lt;/td&gt;
&lt;td&gt;3m 45s&lt;/td&gt;
&lt;td&gt;KVM acceleration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NetBSD aarch64&lt;/td&gt;
&lt;td&gt;Orange Pi VM&lt;/td&gt;
&lt;td&gt;8m 12s&lt;/td&gt;
&lt;td&gt;QEMU on ARM64 host&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Key Insights:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;x86_64 is fastest: Modern x86_64 CPUs dominate for single-threaded compilation&lt;/li&gt;
&lt;li&gt;Emulation wins for ARM64: x86_64 emulating ARM64 beats native ARM64 SBCs&lt;/li&gt;
&lt;li&gt;SBCs are viable: Raspberry Pi and Orange Pi work well for native builds, but slower&lt;/li&gt;
&lt;li&gt;Parallel execution: Running all 7 builds in parallel takes only ~16 minutes (longest pole is FreeBSD ARM64)&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Building a custom multi-platform build orchestration system may seem daunting, but the benefits are substantial:&lt;/p&gt;
&lt;p&gt;→ Full control: Own your build infrastructure&lt;/p&gt;
&lt;p&gt;→ Native builds: Real hardware ensures compatibility&lt;/p&gt;
&lt;p&gt;→ Cost-effective: Low operational costs after initial hardware investment&lt;/p&gt;
&lt;p&gt;→ Fast iteration: Parallel builds complete in ~16 minutes&lt;/p&gt;
&lt;p&gt;→ Flexibility: Easy to add new platforms&lt;/p&gt;
&lt;p&gt;→ Learning: Deep understanding of cross-platform development&lt;/p&gt;
&lt;p&gt;The surprising discovery that emulated ARM64 on powerful x86_64 hardware outperforms native ARM64 single-board computers has practical implications: you don't always need native hardware for every architecture. Strategic use of emulation can provide better performance while maintaining compatibility.&lt;/p&gt;
&lt;p&gt;For projects requiring broad platform support (especially BSD systems not well-served by traditional CI/CD), this approach offers a reliable, maintainable, and cost-effective solution.&lt;/p&gt;
&lt;h3&gt;Architecture Diagram&lt;/h3&gt;
&lt;div class="mermaid"&gt;
graph TB
    subgraph "Trigger Sources"
        GH[GitHub Release&lt;br&gt;v0.13.x]
        MANUAL[Manual Execution&lt;br&gt;./build.sh]
    end

    subgraph "Build Orchestrator"
        ORCH[Python Orchestrator&lt;br&gt;orchestrator.py]
        CONFIG[Build Configuration&lt;br&gt;build-nodes.yaml]
    end

    subgraph "Build Nodes - Local"
        MAC_ARM[macOS ARM64&lt;br&gt;Apple Silicon&lt;br&gt;~45s]
        MAC_X86[macOS x86_64&lt;br&gt;Rosetta 2&lt;br&gt;~30s]
    end

    subgraph "Build Nodes - Remote x86_64"
        LINUX_X86[Linux x86_64&lt;br&gt;alex@10.1.1.27&lt;br&gt;~25s]
        FREEBSD_X86[FreeBSD x86_64&lt;br&gt;root@10.1.1.21&lt;br&gt;~4m]
        OPENBSD_X86[OpenBSD x86_64&lt;br&gt;root@10.1.1.20&lt;br&gt;~12m]
        NETBSD_X86[NetBSD x86_64&lt;br&gt;root@10.1.1.19&lt;br&gt;~3m 45s]
    end

    subgraph "Build Nodes - Remote ARM64"
        FREEBSD_ARM[FreeBSD ARM64&lt;br&gt;freebsd@10.1.1.7&lt;br&gt;~6m 32s]
        OPENBSD_ARM_EMU[OpenBSD ARM64&lt;br&gt;root@10.1.1.23&lt;br&gt;Emulated on x86_64&lt;br&gt;~15m 37s ⚡]
        NETBSD_ARM[NetBSD ARM64&lt;br&gt;root@10.1.1.15&lt;br&gt;~8m 12s]
    end

    subgraph "Artifact Collection"
        COLLECT[SCP Collection&lt;br&gt;Pull binaries from nodes]
        CHECKSUM[Generate SHA256&lt;br&gt;checksums]
    end

    subgraph "Distribution"
        GCS[Google Cloud Storage&lt;br&gt;gs://ballistics-releases/]
        WEBSITE[ballistics.zip&lt;br&gt;Install Script]
    end

    GH --&amp;gt;|webhook| ORCH
    MANUAL --&amp;gt;|CLI| ORCH
    CONFIG --&amp;gt;|reads| ORCH

    ORCH --&amp;gt;|SSH parallel builds| MAC_ARM
    ORCH --&amp;gt;|SSH parallel builds| MAC_X86
    ORCH --&amp;gt;|SSH parallel builds| LINUX_X86
    ORCH --&amp;gt;|SSH parallel builds| FREEBSD_X86
    ORCH --&amp;gt;|SSH parallel builds| OPENBSD_X86
    ORCH --&amp;gt;|SSH parallel builds| NETBSD_X86
    ORCH --&amp;gt;|SSH parallel builds| FREEBSD_ARM
    ORCH --&amp;gt;|SSH parallel builds| OPENBSD_ARM_EMU
    ORCH --&amp;gt;|SSH parallel builds| NETBSD_ARM

    MAC_ARM --&amp;gt;|binary| COLLECT
    MAC_X86 --&amp;gt;|binary| COLLECT
    LINUX_X86 --&amp;gt;|binary| COLLECT
    FREEBSD_X86 --&amp;gt;|binary| COLLECT
    OPENBSD_X86 --&amp;gt;|binary| COLLECT
    NETBSD_X86 --&amp;gt;|binary| COLLECT
    FREEBSD_ARM --&amp;gt;|binary| COLLECT
    OPENBSD_ARM_EMU --&amp;gt;|binary| COLLECT
    NETBSD_ARM --&amp;gt;|binary| COLLECT

    COLLECT --&amp;gt; CHECKSUM
    CHECKSUM --&amp;gt; GCS
    GCS --&amp;gt; WEBSITE

    style OPENBSD_ARM_EMU fill:#90EE90
    style LINUX_X86 fill:#87CEEB
    style GCS fill:#FFD700
    style WEBSITE fill:#FFD700
&lt;/div&gt;

&lt;h4&gt;Diagram Legend&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Green: Fastest ARM64 build (emulated on powerful x86_64)&lt;/li&gt;
&lt;li&gt;Blue: Fastest overall build (native Linux x86_64)&lt;/li&gt;
&lt;li&gt;Yellow: Distribution endpoints&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Build Flow&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Trigger: GitHub release webhook or manual execution&lt;/li&gt;
&lt;li&gt;Parallel Execution: All enabled build nodes start simultaneously&lt;/li&gt;
&lt;li&gt;Collection: Orchestrator collects binaries via SCP&lt;/li&gt;
&lt;li&gt;Verification: SHA256 checksums generated for integrity&lt;/li&gt;
&lt;li&gt;Upload: Binaries and checksums uploaded to GCS&lt;/li&gt;
&lt;li&gt;Availability: Install script immediately serves new version&lt;/li&gt;
&lt;/ol&gt;</description><category>aarch64</category><category>arm64</category><category>automation</category><category>binary distribution</category><category>bsd</category><category>build systems</category><category>cargo</category><category>ci cd</category><category>cross platform</category><category>devops</category><category>emulation</category><category>freebsd</category><category>google cloud storage</category><category>kvm</category><category>linux</category><category>macos</category><category>netbsd</category><category>openbsd</category><category>orange pi</category><category>orchestration</category><category>parallel builds</category><category>qemu</category><category>raspberry pi</category><category>rust</category><category>single board computers</category><category>ssh</category><category>x86_64</category><guid>https://tinycomputers.io/posts/multi-operating-system-and-multi-architecture-build-orchestration-system.html</guid><pubDate>Fri, 21 Nov 2025 21:19:35 GMT</pubDate></item></channel></rss>