Solitary

Installation

Requirements, the install script, Homebrew, keeping solitary current, and removing it.

Solitary is on its 0.x line: the configuration shape can still change between releases. Read the limitations before trusting a cell with credentials.

Requirements

  • macOS or Linux
  • Lima 2.0 or newer — solitary drives limactl on the host and podman through it in the machine, so there is nothing else to install
  • QEMU 11 or newer for gpu: true on Linux — built with OpenGL and Venus support in virglrenderer
  • git, only for solitary clone, which shells out to it so that your existing credentials and SSH agent apply
  • Go, only if you build from source

A host also has to be able to run a guest at all, which is not something an install can check. solitary doctor does, once solitary is on your PATH: Lima and its version, the hypervisor, memory, disk and the config tree. See doctor checks the host.

Install

The install script downloads the release archive for your platform, verifies its signed checksum manifest and puts the binary on your PATH. It works on macOS and Linux:

curl --proto '=https' --proto-redir '=https' -fsSL https://solitary.balakin.io/install.sh | sh

It installs into /usr/local/bin when that is writable without sudo and ~/.local/bin otherwise. SOLITARY_INSTALL_DIR overrides that, and SOLITARY_VERSION installs a tag from the releases page other than the latest:

curl --proto '=https' --proto-redir '=https' -fsSL https://solitary.balakin.io/install.sh | SOLITARY_VERSION=v1.2.3 sh

Homebrew

A tap of our own carries the formula, on macOS and on Linux, and installs Lima with it:

brew install balakin/solitary/solitary

brew upgrade solitary keeps it current after that. Both the install script and solitary update recognise an install Homebrew owns and say so rather than fighting over the binary.

Download a release by hand

Signed releases carry versioned archives, checksums.txt and checksums.txt.sigstore.json. With a trusted installation of cosign, verify the manifest before extracting an archive:

VERSION=1.2.3
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
BASE="https://github.com/balakin/solitary/releases/download/v${VERSION}"
ARCHIVE="solitary_${VERSION}_${OS}_${ARCH}.tar.gz"
curl --proto '=https' --proto-redir '=https' -fsSLO "$BASE/checksums.txt"
curl --proto '=https' --proto-redir '=https' -fsSLO "$BASE/checksums.txt.sigstore.json"
cosign verify-blob \
  --certificate-identity 'https://github.com/balakin/solitary/.github/workflows/release.yml@refs/heads/main' \
  --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
  --bundle checksums.txt.sigstore.json checksums.txt
curl --proto '=https' --proto-redir '=https' -fsSLO "$BASE/$ARCHIVE"
# Verify the entry for $ARCHIVE with sha256sum or shasum before extraction.
sha256sum --check --ignore-missing checksums.txt
tar -xzf "$ARCHIVE" solitary
install -m 755 solitary /usr/local/bin/solitary

Stop if either verification fails. solitary update performs signature verification internally and needs no cosign installation. The install script uses an existing cosign or downloads a fixed verifier version checked against a digest embedded in the script. As with any installer, the script itself must come from a trusted source. Older unsigned releases are refused; use Homebrew or build from source to migrate. Signatures bind releases to this repository's release workflow; they do not protect against compromise of that authorized workflow.

solitary --version prints the version that was baked in at build time. The release page lists every archive and the changelog for the version.

On macOS the binary is unsigned, so Gatekeeper quarantines a download from the browser; fetching it with curl as above avoids that, and neither Homebrew nor solitary update quarantines anything.

Staying current

solitary update replaces the binary with the newest release, after verifying the release workflow's Sigstore signature and the archive checksum:

solitary update          # install the latest release
solitary update --check  # only say whether one exists

Every other command mentions a newer release once a day, at most three times per release, and only when it is talking to a terminal. Set SOLITARY_NO_UPDATE_CHECK=1 to keep solitary from asking GitHub anything on its own.

A binary built from source is left alone by both: it is ahead of the release it names rather than behind it.

Uninstall

The uninstall script removes the binary the install script put on your PATH. It leaves the cells and their definitions alone unless you say otherwise:

curl -fsSL https://solitary.balakin.io/uninstall.sh | sh

A cell is a virtual machine with a disk, and a definition holds the secrets that cell was given, so neither is thrown away on a bare run. Remove the cells first — solitary ls lists them, solitary rm <name> destroys one machine — or let the script do it:

# destroy every machine solitary created, then remove the binary
curl -fsSL https://solitary.balakin.io/uninstall.sh | SOLITARY_REMOVE_CELLS=1 sh

# or leave the machines where they are and remove solitary only
curl -fsSL https://solitary.balakin.io/uninstall.sh | SOLITARY_KEEP_CELLS=1 sh

# and delete the cell definitions, the secrets and the state with them
curl -fsSL https://solitary.balakin.io/uninstall.sh | SOLITARY_PURGE=1 sh

While a machine is still on the host the script stops and asks for one of the two rather than choosing for you: once the binary is gone, the tool that knows how to remove a machine is gone too. Kept machines are named on the way out — limactl delete solitary-<name> removes one later, and installing solitary again picks them up where they are. SOLITARY_INSTALL_DIR points the script at an install somewhere unusual, the same as during an install.

A Homebrew install is left for Homebrew, which the script says rather than deleting a file the formula still claims:

brew uninstall solitary

Lima is installed separately and is never removed: it may well be running machines of its own.

Build from source

git clone https://github.com/balakin/solitary
cd solitary
go mod tidy
make build

Dependencies are not vendored, which is what go mod tidy is for. make lint runs the linters if you intend to change anything.

What the first run costs

Creating a cell takes a couple of minutes: it downloads a cloud image and installs podman inside the machine. Everything after that is container-speed — replacing a container, starting a stopped cell, or opening a shell.

On this page