# Which jadpo runs

A project is written for one release line of Jadpo, such as 0.1, and
while Jadpo is at 0.x a minor release may break an app. So the `jadpo`
that runs a command is the one the project names, whatever you have
installed. You don't do anything for that.

The `jadpo` you install (Homebrew, the npm package `@jadpo/jadpo`, or the
shell or PowerShell installer) is a whole `jadpo` that can also start
another release, as the `go` command does. Before a command runs, it
finds the project: the folder you run it from, walked up to `app.jadpo`,
or else the first argument that names a file or folder in a project. It
reads the line from `application { jadpo: "0.1" }`
([The root](the-root.md)).

When the project is on the installed `jadpo`'s own line, or names no
line, the installed `jadpo` runs the command. Otherwise it runs the
release the project needs, in the same folder and with the same
arguments, and exits as that release does. It picks:

1. the release `jadpo.lock` pins, if there is one (see "Pin a release"
   below);
2. else the newest release of the line it already keeps;
3. else the newest release of the line published on GitHub, which it
   fetches once.

A project on the installed `jadpo`'s own line runs with that `jadpo`, so
a newer patch of the line is used once you install it, or pin it.

Outside a project there is no line to read, so `jadpo new`,
`jadpo --version`, `jadpo help` and every other command run with the
installed version. The VS Code extension runs `jadpo lsp` from the
project's folder, so it gets the project's release with no setting.

## Fetching a release

A release comes from `github.com/jadpo-dev/jadpo`'s releases, and is kept
in `jadpo/releases/<version>/` in your cache folder: `$XDG_CACHE_HOME`,
else `~/.cache`, else `%LOCALAPPDATA%` on Windows. Nothing is written
anywhere else, and two commands that need the same release at once fetch
it once.

Nothing in a download runs until it passes two checks. Its SHA-256 has to
be the one the release publishes beside it. Its GitHub artifact
attestation has to verify too: that is a signed record, kept by GitHub
and Sigstore's public log, saying this repository's `release.yml` built
that archive at the release's tag. `jadpo` checks it with Sigstore's own
verifier against the trusted root it carries. A download that fails
either check is refused, and nothing is kept.

While it fetches, stderr shows a live line, `Fetching Jadpo 0.2.3 for
aarch64-apple-darwin`, with a bar once the archive's size is known, or
that line once where stderr doesn't animate. Then it says what it got,
and the command's own output follows:

```text
✓ Fetched Jadpo 0.2.3 (24.1 MB) and checked its SHA-256 and attestation in 2.3 s
```

Under `--json`, nothing is written but the command's own document.

## The releases kept

| Command | Does |
|---|---|
| `jadpo toolchain` (or `list`) | Lists the releases kept, newest first, with each one's size |
| `jadpo toolchain remove <version>` | Deletes a kept release |

Both take `--json`, and both always run with the `jadpo` you installed,
since they look after its cache. A release you remove is fetched again
the next time a project needs it.

## Pin a release

Pin the exact release a project runs with when its builds must be
reproducible:

| Command | Does |
|---|---|
| `jadpo toolchain pin` | Writes the release the project runs with into `jadpo.lock` |
| `jadpo toolchain unpin` | Takes the pin out again |

The pin is the lock's first line, `jadpo version 0.1.2`
([What is committed](what-is-committed-and-why.md)).
`pin` runs as the project's release, so that is the release it writes.
Commit the lock with the code. A pin on another line than `jadpo:` names
is out of date, `record.out_of_date`, until `jadpo toolchain pin` writes
it again.

## Run your own build on every project

People working on the compiler set `JADPO_TOOLCHAIN=local`, so the
`jadpo` they start runs every command itself, whatever line the project
names. A project on another line is then refused by `jadpo check`, with
the errors below.

## When the release can't be run

When `jadpo` can't run the release a project needs, because it is offline
with nothing kept, the line has no release yet, or a download fails its
checks, it stops. It never compiles the project itself. The error names
the release to install, says why it couldn't be fetched, and gives the
next step for that cause. It is `toolchain.version_newer` when the
project needs a later release than this one, and
`toolchain.version_older` when it needs an earlier one.

## What a release must keep, for older launchers

Every `jadpo` ever installed goes on running the releases later projects
name, so it fetches them by what it was built knowing. These are a
contract with every installed `jadpo`, and changing any of them is a
breaking change to all of them. A test pins each
(`crates/jadpo-cli/src/launcher.rs`, `contract`), so a change fails the
gate pointing here.

- **The line.** `application { jadpo: "X.Y" }` in `app.jadpo`, the
  `jadpo:` member of the `application` singleton with its value a string
  of two numbers, read on its own without the rest of the project
  (`jadpo_check::jadpo_version::declared`). Any `app.jadpo` a later
  release accepts must still put the line there in that form.
- **The pin.** A `jadpo version X.Y.Z` line in `jadpo.lock`, a release
  with no pre-release, read without the rest of the lock.
- **Where releases are.** GitHub Releases of `jadpo-dev/jadpo`, tagged
  `vX.Y.Z`, each archive at
  `https://github.com/jadpo-dev/jadpo/releases/download/vX.Y.Z/jadpo-cli-<target>.tar.gz`,
  `.zip` for `x86_64-pc-windows-msvc`, for each target `dist-workspace.toml`
  names; a target dropped from it can never run a later release.
- **The SHA-256 and the archive.** `<archive>.sha256` beside it, its first
  word the archive's SHA-256 in hex (`sha256sum`'s form); inside the
  archive, `jadpo` (`jadpo.exe` on Windows) at its top or in its one
  folder, `jadpo-cli-<target>/`. Nothing else in the archive is used.
- **The attestation.** A GitHub artifact attestation of the archive's
  digest on Sigstore's public-good instance, whose certificate GitHub
  Actions issued to
  `https://github.com/jadpo-dev/jadpo/.github/workflows/release.yml@refs/tags/vX.Y.Z`:
  the repository, the workflow's path and its run at the tag
  (release-please starts it with `--ref "$TAG"`). Renaming the
  repository or the workflow, or running it on a branch, makes every
  installed `jadpo` refuse the release.
- **Finding a line's newest release.** GitHub's API without signing in,
  `https://api.github.com/repos/jadpo-dev/jadpo/releases?per_page=100`,
  pages of 100 up to ten, reading each release's `tag_name`, `draft` and
  `prerelease`; and `/attestations/sha256:<digest>` for the attestation,
  its `attestations[].bundle`. GitHub allows 60 such requests an hour from
  one address. A fetch takes two or three, and a kept release none, so the
  limit is met only on a shared address such as CI's. When it is met, the
  error says GitHub's API refused it for that limit and to run the command
  again in an hour; nothing is fetched or kept.
- **The handshake.** A release a `jadpo` starts gets
  `JADPO_TOOLCHAIN_LAUNCHED` set to its own version and runs the command
  itself when that is its version, so it never hands the command on
  again; `JADPO_TOOLCHAIN=local` runs everything with the `jadpo` started.
  The command line reaches the release whole, before any of it is read,
  so a command or flag a later release adds works through any launcher.
- **The cache.** `jadpo/releases/<X.Y.Z>/jadpo` in the user's cache
  folder, written whole by a rename under `.<X.Y.Z>.lock`; a folder there
  is a release that was checked.
- **The trusted root.** Each `jadpo` checks attestations against the
  Sigstore trusted root it was built with, without refreshing it. After
  Sigstore rotates a key that signs GitHub's attestations, older launchers
  refuse releases signed with the new key: the error says Jadpo X couldn't
  be fetched to run it because its archive's attestation doesn't verify,
  and to run again, then to install the newest `jadpo`, whose trusted
  root may be newer, if it fails the same way. Installing a `jadpo` built
  after the rotation is the fix, and its release notes say so.
