Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Install-on-demand bootstrap

How an adapter ships spt-core with itself. The contract: the canonical install path 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 when it’s missing runs the official bootstrap: gh release download the platform binary from the release channel, then the binary’s own spt install verb — it places itself at the canonical install path, registers the user PATH, refuses a wrong-platform binary, and is non-interactive and idempotent by construction.

The release channel is private, so the node needs an authenticated GitHub CLI (gh auth login, once per machine, with an account that can read the channel) — see Installing.

The generic contract

if `spt` is on PATH        -> done (optionally check `spt --version` ≥ your floor)
else                       -> gh release download the platform binary + run its `install` verb
then                       -> first invocation may need the absolute path (Windows)
then                       -> register your manifest: spt adapter add --github <org>/<repo>

After first install, spt-core keeps itself current (signed self-update over the same gh channel), so the bootstrap can leave upgrades to spt-core. The remaining bootstrap step is to register your adapter — see Activate the adapter below.

Check-and-install: POSIX sh

Drop this into your adapter’s bootstrap (plugin install step, postinstall script, first-run guard):

if ! command -v spt >/dev/null 2>&1; then
  echo "spt-core not found - installing..."
  gh release download --repo BigscreenVR/spt-bs-releases \
    --pattern 'spt-x86_64-linux' --dir /tmp
  chmod +x /tmp/spt-x86_64-linux
  /tmp/spt-x86_64-linux install
  # current shell may not see the PATH update yet:
  SPT="${SPT_HOME:-$HOME/.spt-core}/bin/spt"
else
  SPT="spt"
fi
"$SPT" --version

Check-and-install: PowerShell

if (-not (Get-Command spt -ErrorAction SilentlyContinue)) {
    Write-Output "spt-core not found - installing..."
    gh release download --repo BigscreenVR/spt-bs-releases `
      --pattern 'spt-x86_64-windows.exe' --dir $env:TEMP
    & (Join-Path $env:TEMP 'spt-x86_64-windows.exe') install
    # 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

Activate the adapter — register your manifest

Installing the binary is the first half of a pack-in; registering your manifest is the second. Installing the binary makes spt available; spt adapter add activates your adapter — registration is what lights up its profiles, [strings] bodies, [digest] extractor, and hooks and makes it show in spt adapter list. So the step right after the binary check is registering the manifest:

# after `spt` is confirmed present (above):
# from a GitHub release — ships built binaries, source-free, versioned:
"$SPT" adapter add --release <your-org>/<your-adapter-repo>               # latest
"$SPT" adapter add --release <your-org>/<your-adapter-repo> --tag v1.0.0  # pinned
# ...or clone a repo whose ROOT holds manifest.toml:
"$SPT" adapter add --github <your-org>/<your-adapter-repo>
# ...or a local directory your harness ships:
"$SPT" adapter add ./adapter

adapter add is manifest-first — a clean add proves the cross-field manifest shape — and it conducts your [update] avenue once (install is the first update). Confirm with spt adapter list: your adapter and its version appear. Keep this idempotent in your bootstrap the same way the binary check is — register when adapter list shows your adapter missing or below the expected version.

--release is the recommended distribution. It fetches a .spt archive asset — a tar whose root holds manifest.toml + strings/ + the binaries the manifest points at — from the named GitHub release, extracts it to the durable registry home, and registers the root. That ships your built binaries, source-free and versioned by tag (--tag, default the latest release), and first-acquisition trusts gh’s authenticated TLS + GitHub exactly like the bootstrap’s first binary fetch. A development monorepo stays a monorepo: your release CI packs the archive (tar -czf adapter.spt manifest.toml strings/ bin/…) and uploads it as a release asset, so the adapter ships straight from your existing repo. Override the asset name with --asset (default adapter.spt).

Cover several platforms in one .spt (since v0.13.2). To ship binaries for more than one OS/arch in a single asset, add a target-triple subdirectory at the archive root per platform and put that platform’s binaries inside it, leaving the shared manifest.toml + strings/ at the root:

adapter.spt
├── manifest.toml                # shared — at the root
├── strings/                     # shared — at the root
├── x86_64-pc-windows-msvc/      # one platform's binaries…
│   └── bin/…                    #   …in the same relative layout a flat .spt uses
└── x86_64-unknown-linux-gnu/
    └── bin/…

On install, spt-core extracts the shared root plus only the current node’s triple, flattened into the install dir — so the bare-name <install_dir>/<program> resolution above is unchanged; mirror, under each triple, exactly the per-platform tree a flat .spt would place at the root. The recognized triples are the platforms spt-core itself ships for — today x86_64-pc-windows-msvc, x86_64-unknown-linux-gnu, and (since v0.30.0) x86_64-unknown-linux-musl — one set shared with platform-targeted update sets, so a new spt-core platform tier extends this list automatically; a root subdirectory whose name is not a recognized triple is treated as a shared root entry (so binaries for other platforms still ship as separate single-platform assets, one selected per node with --asset). A multi-platform archive that lacks the recipient’s triple is refused with a clear NoArtifactForPlatform error — never a silent partial install — and requires min_spt_core_version >= 0.13.2. A flat archive (no triple subdirectories) installs exactly as before.

--github is the alternative for an adapter whose repo root already holds manifest.toml: it clones the repo and registers the clone root (adapter add resolves a directory source to <dir>/manifest.toml at the root). Local development uses the directory form, which takes any path or filename: spt adapter add ./adapters/my-adapter.toml.

What registration holds under adapters/<name>/ follows your [update] avenue: a delegated or gh_release adapter is pointer-mode (the manifest and strings/ are read live from the durable home), and a file_pull (or avenue-less) adapter is copy-mode (the manifest.toml and strings/ are copied in). Publish the binaries your manifest references in the .spt (or repo) too, and reference them by bare name: since v0.8.0, a command template’s program token resolves against the adapter’s install dir before PATH, so a .spt that ships its binaries is self-contained — the shipped binary is found without any PATH placement. (Absolute paths still work; an unshipped tool still falls back to PATH.) This applies to the [session.psyche_resume] per-event turn, the [digest] extractor, and spt adapter digest-proof.

“Install the plugin, get the adapter for free” — include the activation step. The [update] avenues keep a registered adapter current. The straightforward path for a --release-distributed adapter is gh_release (since v0.8.0): declare avenue = "gh_release", repo = "your-org/your-adapter" and spt adapter update ships the latest release .spt to the node — fetched, optionally verified against your signing_key, re-extracted, and re-registered. The other avenues: delegated (your harness’s own updater installs the content — set self_verifies = true to attest it verifies what it installs), and file_pull (its automatic network-pull transport is on the roadmap). Deliver the manifest with adapter add --release (or --github, or a packed local dir) and let gh_release carry updates.

The Windows PATH-refresh gotcha

The install verb registers the binary directory on the user PATH via the registry. Registry PATH changes reach new processes; an already-running process — including the terminal (and your bootstrap) that just ran the install — keeps the PATH it started with.

So: the first invocation after an install must use the absolute path (%LOCALAPPDATA%\spt-core\bin\spt.exe; the verb 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: the printed absolute path, once.

Pinning and install-dir overrides

Pin a release by giving gh release download an explicit tag (gh release download v0.32.0 --repo … --pattern …). The verb’s knobs:

FlagMeaning
--dir <path>Override the install directory
--no-pathSkip user-PATH registration

Trust model

First fetch trusts gh’s authenticated TLS + the private channel’s access control (check the download against the release’s SHA256SUMS for a belt on the braces). From then on, spt update performs full Ed25519 signature verification against the two-key trust anchor embedded in every binary — so the bootstrap is the strong link only once.