# Install cin90

cin90 ships as a single static binary for macOS and Linux.

Windows students use cin90 inside WSL2. Install the Linux artifact in the WSL2
distribution and run all cin90 commands from that Linux shell.

## Install From A Release

The download page on [coderin90.com](https://coderin90.com) is the bootstrap for
a machine that does not have cin90 yet. The binaries are hosted on S3; each
version publishes four artifacts plus their checksums:

- `cin90_<version>_darwin_amd64` · `cin90_<version>_darwin_amd64.sha256`
- `cin90_<version>_darwin_arm64` · `cin90_<version>_darwin_arm64.sha256`
- `cin90_<version>_linux_amd64` · `cin90_<version>_linux_amd64.sha256`
- `cin90_<version>_linux_arm64` · `cin90_<version>_linux_arm64.sha256`

(`<version>` is the version string exactly as the manifest gives it, which is
unprefixed — e.g. `0.1.0-beta.2`, not `v0.1.0-beta.2`, even though the matching
git tag carries the `v`. The artifact URL is
`https://cin90-releases.s3.us-east-1.amazonaws.com/<version>/<artifact>`, and
each artifact's `.sha256` and `.sig` sit beside it at that URL plus the suffix.
The bucket has no listing, so the manifest at
`https://cin90-releases.s3.us-east-1.amazonaws.com/latest.json` is where the
current version and the four binary URLs come from.)

Releases *may* be stored gzip-compressed and served with `Content-Encoding:
gzip`, which cuts a 9.4MB download to 3.8MB. No published version is compressed
yet, and browsers and `cin90 update` decode it without being asked in any case.
**`curl` does not, unless you pass `--compressed`, so use it either way:**

```sh
curl -fsSL --compressed -O \
  https://cin90-releases.s3.<region>.amazonaws.com/<version>/cin90_<version>_<platform>
```

Against a compressed release, omitting `--compressed` saves the compressed
stream as if it were the binary. That is not a silent failure — the checksum
below will not match, and the file will not run — but the checksum is the only
thing that tells you. Against an uncompressed one, `--compressed` is a harmless
no-op, which is why it is the safe habit.

A first install is verified by hand, because the embedded signing key can only
be checked by a binary that already runs. Download the artifact **and** its
`.sha256` sidecar, then compare:

```sh
shasum -a 256 -c cin90_<version>_<platform>.sha256
# or on GNU Linux:  sha256sum -c cin90_<version>_<platform>.sha256
```

The sidecar describes the **raw** binary — the decompressed bytes — so this is
the same check it always was.

An "OK" line means the artifact matches the published checksum. Each release
also publishes `<artifact>.sig` (the detached Ed25519 signature) — the CLI
verifies the checksum and that signature before `cin90 update` replaces the
local executable.

## Update

Run:

```sh
cin90 update
```

The updater first compares the version the manifest names against the running
one and refuses anything that is not newer, so a manifest pointing at an older
genuinely-signed build cannot roll you back. Already on the published version,
it says so and downloads nothing. A deliberate rollback is
`cin90 update --force`.

Past that guard it downloads the platform artifact named by the manifest,
verifies sha256 and the detached signature, writes the replacement
beside the current executable, and then atomically renames it into place. A
failed download, checksum mismatch, or bad signature leaves the current binary
in place.

## Install The Push-Time Hook

Run this once per assignment clone:

```sh
cin90 install-hook
```

It sets the repository's `core.hooksPath` to `.githooks` and writes a `pre-push`
hook there.

Setting `core.hooksPath` stops git reading the repository's old hooks directory
at all, so hooks that were already installed there — yours, or another tool's —
are copied forward as one-line scripts in `.githooks` that run the original.
They keep firing, and a `pre-push` of your own still decides whether the push
goes through. If `.githooks` already contains a hook cin90 did not write, the
install refuses and says so rather than overwriting it.

The directory it copies from is whichever one git is reading, which includes a
`core.hooksPath` you set globally in `~/.gitconfig` — the same directory a
second run comes back to. Only the hook names git actually runs are copied
forward, so a shell library or a spare copy kept alongside them stays where it
is.

Run it again after installing another hook. Something like `pre-commit install`
writes into `.git/hooks`, which git no longer reads, so the new hook would never
fire; a second `cin90 install-hook` re-scans and chains it. Nothing else
changes — the same scripts and the same `core.hooksPath`.

If cin90 cannot capture a push it says so once on stderr and lets the push
through: it is a capture trigger, never a gate. The usual cause is `cin90` not
being on `PATH` for the program running the hook — a GUI git client or an IDE
starts with a minimal environment, so a `PATH` exported only in `~/.zshrc` does
not reach it. Install the binary somewhere already on the system `PATH`, and run
`cin90 reconstruct` to record the session for a push that went uncaptured.

To undo it:

```sh
cin90 uninstall-hook
```

`core.hooksPath` goes back to whatever it was before — another directory, or no
setting at all. The one exception is the mirror image of the clobber above: if
`core.hooksPath` now points somewhere that is not `.githooks`, something else
was installed after cin90, and putting the old value back would silently
disable it. cin90 leaves the setting alone, says so, and forgets what it had
recorded. Either way the scripts in `.githooks` are left where they are, inert,
because nothing points at them any more.
