# Install or upgrade shld on a Linux server

Use this guide for administrator setup through a server agent.
Preserve existing configuration and collected evidence.
Ask for missing server access or collection scope before changes.

## Release availability

`v0.1.0-pilot.20261028.2` is published at the immutable URLs below.
The release owner verified all nine files, the signature, both archives, and matching binary hashes on October 28, 2026.
The published `.1` release lacks managed login for the preserved nested state path.
Keep its signed files unchanged.
Bundled documents preserve the pre-publication snapshot and can still say publication is pending.
Use this live guide for confirmed availability; keep the signed archive bytes unchanged.
The canonical API and approval origins passed scoped live checks.
Legacy pilot hosts remain configured, but their live edge behavior remains unverified because of a Cloudflare challenge.
QA verified nested-state login and tape preservation on Linux ARM64 with systemd 255.
This does not establish runtime proof for the reported Debian 13 amd64 host with systemd 257.
Actual server login, monitoring, and inbox receipt still need the separate checks below.
If a verified archive does not support the server, report this blocker:

> Setup blocked: no published, verified shld release supports this server's architecture and required commands.

Do not compile arbitrary source or request legacy notification tokens as a substitute.
There is no supported `shld update` command.

## Discover before changes

1. Confirm server access and the approved collection root with the administrator.
2. Identify the operating system with `uname -s`.
3. Identify the architecture with `uname -m`.
4. Map `x86_64` to `amd64`, or `aarch64` and `arm64` to `arm64`.
5. Stop for another operating system or architecture.
6. Find an existing binary with `command -v shld`.
7. Record its resolved path and checksum without printing credentials.

For an existing binary, run only version and top-level help first:

```sh
shld version
shld --help
```

A `dev` version does not establish command support.
An October 26 trial binary can lack `auth` and `status`.
If top-level help lacks either command, take the upgrade or release-blocker branch now.
Do not invoke any `auth` subcommand on that binary, including help.

Only after top-level help lists both commands, check these subcommands:

```sh
shld auth --help
shld auth login --help
shld status --help
```

Stop for the upgrade branch if a required subcommand or option remains absent.
Help commands do not start authorization.

| Existing installation | Next action |
| --- | --- |
| No binary | Verify the matching release before installation. |
| Old binary without required commands | Complete the backup and upgrade steps before login. |
| Current configured service | Check authorization and monitoring before another login. |

Inspect service metadata without printing its environment or process arguments:

```sh
systemctl show shld.service --no-pager \
  --property=LoadState,ActiveState,UnitFileState,FragmentPath,DropInPaths,DynamicUser,StateDirectory,StateDirectoryMode
```

Record whether the service runs, stops, or does not exist.
Inspect its effective binary, collection root, state path, and loopback address within a private privileged session.
Report only these approved nonsecret values.
Do not print environment files, credential contents, bearer headers, or secret-bearing process arguments.
Preserve the evidence destination and its separate credential.

The supplied unit uses `/usr/local/bin/shld` and `DynamicUser=yes`.
Its default physical state directory is `/var/lib/private/shld`, with mode `0700`.
The compatibility release also accepts the preserved trial path `/var/lib/private/shld/state`.
Keep the original state argument and any reviewed `10-preserve-state.conf` drop-in.
Do not move the tape.
Do not create replacement state.
Do not use `--no-service` to bypass this check.
Its loopback address is `127.0.0.1:8099`.
The CLI discovers a recognized unit's address.
Its fallback address is `127.0.0.1:8787`.
Stop for review of custom units, conflicting overrides, or unknown state layouts.
Do not move tape records or relax path checks to fit the supplied unit.

## Download and verify

Use this verified immutable release prefix:

```text
https://downloads.shld.app/prereleases/v0.1.0-pilot.20261028.2/
```

Use a fresh private download directory on the Linux server.
Require `curl`, OpenSSL, `sha256sum`, and `tar` before download.
Installation needs no Python or build tools.
Download only the matching archive and its verification files:

```sh
set -eu
umask 077
download_dir=$(mktemp -d)
cd "$download_dir"
case "$(uname -m)" in
  x86_64) arch=amd64 ;;
  aarch64|arm64) arch=arm64 ;;
  *) echo 'Unsupported architecture' >&2; exit 1 ;;
esac
release_url=https://downloads.shld.app/prereleases/v0.1.0-pilot.20261028.2
for name in SHA256SUMS SHA256SUMS.sig shld-release-rsa3072-20261028.pub.pem "shld-linux-$arch.tar.gz"; do
  curl --fail --proto '=https' --tlsv1.2 --connect-timeout 10 --max-time 120 --max-filesize 67108864 --output "$name" "$release_url/$name"
done
```

The approved key uses RSA 3072, PKCS1v1.5, and SHA256.
Its SHA256 fingerprint covers the public SPKI DER bytes:

```text
ffa2e4a4b13bc681e121f9414066dbcfdbdcccdcb2511baa17e562c5775fe036
```

Treat this fingerprint as the trust pin.
A key beside a signature does not establish trust by itself.
Verify the key and signature before the archive checksum:

```sh
openssl pkey -pubin -in shld-release-rsa3072-20261028.pub.pem -outform DER -out release-key.der
fingerprint=$(openssl dgst -sha256 release-key.der | sed 's/^.*= //')
test "$fingerprint" = ffa2e4a4b13bc681e121f9414066dbcfdbdcccdcb2511baa17e562c5775fe036
openssl dgst -sha256 -verify shld-release-rsa3072-20261028.pub.pem -signature SHA256SUMS.sig SHA256SUMS
test -f "shld-linux-$arch.tar.gz"
grep -E "^[0-9a-f]{64}  shld-linux-$arch\\.tar\\.gz$" SHA256SUMS > selected-checksum.txt
test "$(wc -l < selected-checksum.txt | tr -d ' ')" = 1
sha256sum --check selected-checksum.txt
```

Stop on a failed command, mismatch, or missing input.
Record the immutable version and verified archive checksum in the private setup record.
Inspect archive paths and file types before extraction:

```sh
tar -tvzf "shld-linux-$arch.tar.gz"
```

Require the root `shld-v0.1.0-pilot.20261028.2-linux-$arch/`.
Require exactly these eleven files beneath that root:

```text
bin/shld
scripts/install.sh
deploy/shld.service
docs/auth.md
docs/operations.md
docs/agent-setup.md
docs/release-verification.md
LICENSE
NOTICE
THIRD_PARTY_NOTICES.md
INSTALL.md
```
Reject absolute paths, parent traversal, links, or unexpected files.
After review, extract the verified archive within the private download directory:

```sh
tar -xzf "shld-linux-$arch.tar.gz"
cd "shld-v0.1.0-pilot.20261028.2-linux-$arch"
```
Check the extracted binary's version and help before host changes.
Require `auth login`, `auth status`, and `status` support.

## Install a new service

Choose the collection root before installation.
Keep private state outside that root.
Review the verified installer and service unit within the approved scope.
Run this command from the extracted release directory:

```sh
sudo ./scripts/install.sh --binary ./bin/shld --systemd --root /srv/application
```

Replace `/srv/application` with the approved existing directory.
Do not use `/` as a default collection root.
The installer copies the binary and unit without starting or enabling the service.
It preserves an existing `/etc/shld/shld.env` file.
Review the effective unit before an authorized start.
Use this command only when authorization covers enablement and startup:

```sh
sudo systemctl enable --now shld.service
```

Continue with authorization below.

## Upgrade an existing installation

An upgrade replaces the binary and unit and interrupts collection.
Disclose these effects before changes.
Use existing setup authorization when it covers replacement and interruption.
Obtain authorization for new scope.

1. Record the prior running state and enablement without changes.
2. Check compatibility with the replacement's state and tape formats.
3. Stop for review if compatibility or the state layout remains unknown.
4. Create a root-owned backup directory outside the collection root, with mode `0700`.
5. Limit backup inputs to the binary, unit, drop-ins, environment, authorization files, and identified state directory.
6. Reject unknown links or mounts instead of following them.
7. Check available disk space against those inputs.
8. Stop the approved service before the consistent backup and replacement.
9. Preserve file modes, ownership, tape bytes, state identity, credentials, and relevant configuration in the private backup.
10. Record checksums without exporting secret contents or backup archives.

Keep the backup on the server under administrator control.
Exclude it from public artifacts and agent transcripts.
The installer overwrites `/usr/local/bin/shld` and `/etc/systemd/system/shld.service`.
It creates no rollback backup and offers no atomic upgrade guarantee.
It retains existing service drop-ins.
Its environment preservation does not establish compatibility with a custom unit.
Keep the recorded collection root, state argument, and evidence destination unchanged.
Compare the effective unit with those recorded values before startup or login.

After review, use the installation command with the same approved collection root.
Restore reviewed configuration if the replacement changes approved root, state, or evidence settings.
Do not overwrite a custom unit without explicit review.
Reload systemd after an authorized unit change:

```sh
sudo systemctl daemon-reload
```

For the preserved nested trial layout, verify the existing tape:

```sh
state_dir=/var/lib/private/shld/state
sudo test -f "$state_dir/events.jsonl"
sudo shld verify --state "$state_dir"
```

For an original parent layout, use `/var/lib/private/shld` instead.
Use only the recorded original path.
Require tape verification before startup.
Record the tape inode, sequence, head, and a hash of its existing prefix before login.
After the disclosed restart, verify the same inode and retained prefix.
Check new appends against the recorded sequence and head.
Make sure the service creates no sibling tape.
Do not start a second writer over that state.
Restore the prior running or stopped state without changes to enablement.
For a previously running service, use `sudo systemctl start shld.service`.
Leave a previously stopped service stopped.
Report monitoring as stopped in that case.

On replacement failure, stop the service before rollback.
Restore the backed-up binary, unit, and configuration.
Reload systemd before restoration of its prior running state.
Restore state only under a reviewed recovery plan that preserves newer records.
Keep newer evidence and failed-upgrade diagnostics private.
Do not overwrite newer tape records with an older backup.

## Authorize and verify

Run login only after the installed binary passes the capability check:

```sh
sudo shld auth login --json --non-interactive
```

An `authorization_required` result uses exit status `2`.
Give the administrator only `verification_uri`, `user_code`, `expires_at`, and the disclosed restart scope.
The administrator reviews the server, company, recipients, and restart notice in the browser.
Treat that approval as authorization for the disclosed restart within the approved setup scope.
Run the same command after approval to redeem the same pending request.
Respect the requested polling interval.
Stop after a quota error.
A recognized running service restarts once after new authorization.
A stopped service stays stopped.
Reuse a valid connection without another grant or restart.
Fresh login uses `https://api.shld.app` and approval through `https://app.shld.app`.
Existing saved and pending connections retain their original origin, including the pilot URLs.

Check these separate results:

```sh
sudo shld auth status --json
shld status --json
```

Use `shld status --json --listen <actual-loopback-address>` if discovery cannot identify a reviewed address.
Report saved authorization, active monitoring, last scan, coverage limits, and notification state as separate results.
A zero exit from `status` does not establish an active service.
Inspect the JSON state.
Provider acceptance does not prove inbox receipt.
Ask for inbox confirmation when acceptance requires it.
Stop for an unsupported service or unknown configuration with the specific recovery action.

QA proved the supplied Linux ARM64 service journey on systemd 255.
Systemd 247 and custom units remain unverified.
This proof does not establish delivery to a real administrator's inbox.
