Pre-release Harn is pre-1.0 — the language, standard library, and CLI may change between releases. See the release notes

Bootstrap an exact Harn release

scripts/bootstrap_harn.mjs is Harn's supported host-native bootstrap interface for local development and CI systems outside GitHub Actions. It uses only Node.js built-ins, runs on Linux, macOS, and Windows, and never requires a POSIX shell. Node.js 20 or newer is required.

Pin the bootstrap script itself by checking out an immutable Harn release or commit, then provide either an exact version or a file containing one:

node path/to/harn/scripts/bootstrap_harn.mjs --version 0.10.31 --cache-dir path/to/cache --install-dir path/to/tools/harn
node path/to/harn/scripts/bootstrap_harn.mjs --version-file .harn-version --cache-dir path/to/cache --install-dir path/to/tools/harn

The effective request must resolve to one MAJOR.MINOR.PATCH value, with an optional leading v. latest, version ranges, and prerelease selectors are rejected. When neither option is written on the command line, --version-file defaults to .harn-version in the working directory; a missing or invalid file is a closed failure.

The command prints one JSON receipt to standard output:

{
  "schema_version": "harn-bootstrap-v1",
  "version": "0.10.31",
  "target": "aarch64-apple-darwin",
  "binary_path": "/ci/tools/harn/harn",
  "source": "https://github.com/burin-labs/harn/releases/download/v0.10.31/harn-aarch64-apple-darwin.tar.gz",
  "checksum": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
  "cache_hit": false,
  "binary_sha256": "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
}

The first seven fields form the stable harn-bootstrap-v1 contract. binary_sha256 provides additional local integrity evidence. The installed directory also contains the integrity-only install-manifest.json used to validate future reuse; the JSON printed for each invocation is its audit receipt.

Verification and atomicity

Every online run downloads the exact release's SHA256SUMS, requires one unambiguous entry for the selected platform archive, and verifies the archive before extraction. A cached archive is only a candidate: the bootstrapper hashes it again against the release metadata before reporting cache_hit: true. If checksum metadata previously cached for an exact release differs from newly published metadata, the run fails closed.

Archive files and install directories are prepared beside their destinations and published atomically only after verification. Interrupted temporary paths never become cache or install state. Concurrent processes installing the same version may both extract work, but a <install-dir>.lock ownership record serializes validation, corrupt-state quarantine, and final publication. A process that loses publication validates the winner before returning.

The publication lock records its host, process, and a unique ownership token. Waiters give each unchanged owner 30 seconds before evicting it; an ownership change starts a fresh deadline rather than transferring stale wait time to the new owner. After at least two minutes of total contention, the next acquisition attempt revalidates the install: an exact valid peer publication wins, while a missing or invalid install fails closed. After acquiring the boundary, a waiter also revalidates any installation the prior owner may have published. A dead same-host process or a lock older than five minutes is reclaimed immediately. Filesystem mutations retry transient Windows sharing and antivirus errors while the boundary remains owned. Token checks fence publication and cleanup so an evicted process cannot later mutate state or delete its replacement's lock.

The default cache is under the runner tool cache, $XDG_CACHE_HOME, Windows %LOCALAPPDATA%, or the user's .cache directory. The default installation is versioned below that bootstrap cache. Use --cache-dir and --install-dir in shared CI so Harn does not write to a package manager, Cargo home, or another toolchain's directory.

Offline runs

After one successful online install, --offline requires both of these exact version entries:

  • metadata/<version>/SHA256SUMS
  • downloads/<version>/<platform archive>

Offline mode performs no network request and re-hashes the archive against the cached checksum metadata. A missing or corrupt entry is a closed failure; the bootstrapper never selects another version or falls back to an unverified binary.

Proxies

The bootstrapper uses Node's built-in fetch. Node.js 22.21+, 24.5+, and newer release lines can read proxy settings when NODE_USE_ENV_PROXY=1 is set before Node starts. Configure HTTPS_PROXY, HTTP_PROXY, and NO_PROXY in the process environment. Harn's setup action enables this support automatically when the runner's Node version provides it. On older Node versions, use a network environment that provides direct GitHub access or pre-populate the exact cache on a connected machine and run with --offline.

Failure recovery and source builds

Rerun the same command after a network interruption. Unique .harn-bootstrap-* leftovers are ignored and can be deleted after no bootstrap processes are active. An <install-dir>.lock directory can likewise be removed after confirming no bootstrap process is active; normal runs reclaim dead, stale, or timed-out owners automatically. If an operator must rebuild one entry, remove only that version's metadata/archive directory and its versioned install directory, then rerun online.

The bootstrapper does not silently build from source. A source build has different provenance, prerequisites, and cache semantics, so it must be an explicit policy decision. The supported fallback is to install the same exact version with Rust:

cargo install --locked harn-cli --version 0.10.31

Alternatively, check out the matching v0.10.31 source tag and run cargo install --locked --path crates/harn-cli. Neither source-build path emits a harn-bootstrap-v1 receipt or counts as a release-archive cache hit.

GitHub Actions

The first-party setup action is a thin adapter over this same file. It resolves the version and platform through the bootstrapper, restores the candidate cache, invokes the bootstrapper for verification and installation, adds the binary directory to PATH, and exposes the JSON as its receipt output:

- uses: burin-labs/harn/.github/actions/setup-harn@<full-commit-sha>
  id: harn
  with:
    version: 0.10.31
- env:
    HARN_RECEIPT: ${{ steps.harn.outputs.receipt }}
  run: node -e 'process.stdout.write(process.env.HARN_RECEIPT)'