# Install scradle with one command

```bash
curl -fsSL https://cdn.shadowcradle.com/download/linux-scradle/install.sh | sudo bash
```

Run it again any time: it updates scradle if a newer version is published.

## What it does

1. **Detects your system** from `/etc/os-release` (distribution, version) and
   picks the matching package: `.deb` on Debian/Ubuntu, `.rpm` on Rocky, RHEL,
   AlmaLinux and openSUSE Leap. Anything else gets the self-extracting `.run`
   installer, which needs only a compiler and your kernel headers.
2. **Downloads** the newest release and checks its SHA-256 against the
   `SHA256SUMS` published beside it (and the GPG signature, when the release is
   signed and you have the key). A mismatch aborts before anything is installed.
3. **Installs or updates.** It records the release tag it installed in
   `/var/lib/scradle/release`. A newer tag updates in place, even when the
   package version is unchanged. If the same release is already installed it
   says so and asks whether to force a reinstall (default: no). Pass `--force`
   to reinstall without being asked. An older tag (`--tag`) is a reported
   downgrade.
4. **Runs a self test** and exits non-zero if any check fails: the package is
   registered, DKMS built the module for the running kernel, the module loads,
   `/dev/scradle-ctl` and `/proc/scradle-info` exist, `scradlectl status` runs,
   and the kernel log holds no scradle warning.

The module is built by DKMS against your kernel, so kernel headers must be
installable from your distribution's repositories. It also rebuilds on every
kernel upgrade.

## After installing

Attach a **block device** that a filesystem is mounted from, then snapshot it
to a file on a *different* filesystem:

```bash
lsblk -p                                          # pick a block device, not /dev/nvme0
sudo scradlectl attach /dev/mapper/<luks-or-lvm>  # or /dev/nvme0n1p3, /dev/sdb1 ...
sudo scradlectl snapshot /dev/mapper/<luks-or-lvm> /mnt/other/scradle.cow 1G --ceiling=32G
# read /dev/scradle0, then
sudo scradlectl release /dev/mapper/<luks-or-lvm>
```

`/dev/nvme0` is the controller, not a disk. See [USAGE.md](USAGE.md) before
tracing a root volume, and [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for the
errors above.

The installer installs kernel headers for the **running** kernel only, and only
when they are missing. Versions before release `2026.09.25.3` pulled in the
distribution's newest headers (`linux-headers-generic`), rebuilding other DKMS
modules for a kernel you may not be running. On such a machine:

```bash
sudo apt remove linux-headers-generic linux-headers-6.8.0-142 linux-headers-6.8.0-142-generic   # adjust the version to what apt installed
sudo apt autoremove
```

## Options

Pass options after `bash -s --`:

```bash
curl -fsSL https://cdn.shadowcradle.com/download/linux-scradle/install.sh | sudo bash -s -- --force
```

| Option | Effect |
|---|---|
| `--force` | Reinstall even when this version is already installed |
| `--yes` | Never prompt; upgrades proceed, a same-version reinstall does not |
| `--tag 2026.09.25` | Install that release instead of the newest |
| `--type deb\|rpm\|run` | Skip package-type detection |
| `--dry-run` | Detect, download and verify only; install nothing (works without root) |
| `--self-test` | Run only the self test against what is installed |
| `--no-self-test` | Skip the self test |
| `--allow-container` | Install inside a container (see below) |

It refuses to run inside a container by default: the kernel module loads into
the **host** kernel, not the container's.

Read the script before piping it to a shell if you prefer:
`curl -fsSL https://cdn.shadowcradle.com/download/linux-scradle/install.sh -o install.sh`.

## Where everything lives

```
https://cdn.shadowcradle.com/download/linux-scradle/
    install.sh                        this installer
    index.json                        every package type / os and its newest release
    <pkg-type>/<os>/<tag>/            deb | rpm | arch | run | tar,  e.g. deb/debian-12/2026.09.25/
        scradle-dkms_<ver>_all.deb    the package
        SHA256SUMS  SHA512SUMS        hashes (SHA256SUMS.asc when signed)
        CHANGELOG.md                  commits since the previous release
        manifest.json                 sizes, hashes, commit, package version
    <pkg-type>/<os>/LATEST            the newest tag, as plain text
    <pkg-type>/<os>/history.json      every release for that os
```

`<os>` is `<ID>-<VERSION_ID>` from `/etc/os-release` (`debian-12`,
`ubuntu-24.04`, `rocky-9`, `opensuse-leap-15`); `any` for the `.run` and source
tarball. `<tag>` is the release date, `YYYY.MM.DD` (`YYYY.MM.DD.2` for a second
release that day). Release directories never change once published.

Manual install of one package, for example:

```bash
base=https://cdn.shadowcradle.com/download/linux-scradle/deb/debian-12
tag=$(curl -fsSL $base/LATEST)
curl -fsSLO $base/$tag/scradle-dkms_0.1.0_all.deb -O $base/$tag/SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMS && sudo apt install ./scradle-dkms_0.1.0_all.deb
```

## Cutting a release (maintainers)

```bash
scripts/release.sh --tag --push      # tags today's date and pushes it
```

The tag pipeline builds every package and `publish:cdn` runs
`scripts/cdn-publish.py`, which lays out the tree above, uploads it and checks
every object landed. `scripts/cdn-publish.py --tag YYYY.MM.DD` alone prints the
plan without uploading. Needs the CI variables `SHADOWCRADLE_CDN_ACCESS_KEY` and
`SHADOWCRADLE_CDN_SECRET_KEY` (the same bucket credentials the other Cradle
products use).
