# Install-on-demand bootstrap

How an adapter ships spt-core *with itself*. The contract: **the canonical
install one-liner is also every adapter's pack-in installer** — there is no
second mechanism, no vendored binary, no bespoke fetch logic to maintain.
Your adapter checks for `spt`, and runs the official script when it's
missing.

The scripts are non-interactive **by construction** (they will never prompt),
idempotent (safe to re-run), sha256-verify what they download, and register
the user PATH. Served from the permanent canonical URL:

- `https://sabermage.github.io/spt-releases/install.sh`
- `https://sabermage.github.io/spt-releases/install.ps1`

## The generic contract

```text
if `spt` is on PATH        -> done (optionally check `spt --version` ≥ your floor)
else                       -> run the official one-liner for the OS
then                       -> first invocation may need the absolute path (Windows)
```

After first install, spt-core keeps itself current (signed self-update) — the
bootstrap never needs to handle upgrades.

## Check-and-install: POSIX sh

Drop this into your adapter's bootstrap (plugin install step, postinstall
script, first-run guard):

```sh
if ! command -v spt >/dev/null 2>&1; then
  echo "spt-core not found - installing..."
  curl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh
  # current shell may not see the PATH update yet:
  SPT="$HOME/.local/bin/spt"
else
  SPT="spt"
fi
"$SPT" --version
```

## Check-and-install: PowerShell

```powershell
if (-not (Get-Command spt -ErrorAction SilentlyContinue)) {
    Write-Output "spt-core not found - installing..."
    irm https://sabermage.github.io/spt-releases/install.ps1 | iex
    # The user-PATH registration only reaches NEW terminals -- use the
    # absolute install path for everything in THIS process:
    $spt = Join-Path $env:LOCALAPPDATA 'spt-core\bin\spt.exe'
} else {
    $spt = 'spt'
}
& $spt --version
```

## The Windows PATH-refresh gotcha

The installer registers the binary directory on the **user** PATH via the
registry. Already-running processes — including the terminal (and *your
bootstrap*) that just ran the installer — do not see registry PATH changes.

So: **the first invocation after an install must use the absolute path**
(`%LOCALAPPDATA%\spt-core\bin\spt.exe`; the installer prints it). Every new
terminal after that finds `spt` normally. The snippets above bake this in.
On Linux the equivalent (a `~/.profile` entry the current shell hasn't
sourced) is handled the same way: `$HOME/.local/bin/spt` absolutely, once.

## Pinning and air-gapped installs

The scripts take environment knobs — never flags, so the pipe-to-shell form
stays canonical:

| Env var | Meaning |
|---|---|
| `SPT_INSTALL_VERSION` | Install a specific release tag instead of latest |
| `SPT_INSTALL_DIR` | Override the install directory |
| `SPT_INSTALL_ASSET_BASE` | A URL or local directory holding the release assets + `SHA256SUMS` directly (CI, air-gap, mirrors) |
| `SPT_INSTALL_NO_PATH` | `1` = skip PATH registration |

Example — pin a version inside a CI job:

```sh
SPT_INSTALL_VERSION=v0.1.0 \
  curl -fsSL https://sabermage.github.io/spt-releases/install.sh | sh
```

## Trust model

First fetch trusts HTTPS + GitHub and verifies the binary's sha256 against
the release's `SHA256SUMS`. From then on, `spt update` performs full Ed25519
signature verification against the two-key trust anchor embedded in every
binary — the installer never needs to be the strong link twice.
