# Build and Install from Source

> Status: Version-specific
> Documentation baseline: v4.10.0

Build the latest fetched `main` with `build_packages.sh`, then install one
locally generated package on a test host. This procedure was reviewed against
commit `8fc22a6bae4adfcf62d80e90da6387f2aa6db1c7` on 2026-09-20.
The public v4.10.0 release completed IVV on 1 October 2026. This historical
source-build procedure keeps its original reviewed revision; locally built
packages do not inherit the public release verdict. Under the
[strategy approved on 29 September 2026](https://syswarden.io/docs/release-assurance/),
intermediate `Patch`, `Minor` and `Major` releases require IVV (Integration,
Verification and Validation); `Upgrade` generations require full IVVQ, adding
Qualification. A successful local build does not make its revision an accepted
public release. This terminology update does not revalidate or change the
source revision and commands reviewed below.

For stable packages, use the [deployment tutorial](https://syswarden.io/docs/deployment-reference/) and
[public releases](https://github.com/duggytuxy/syswarden/releases).

## 1. Build host and outputs

Use a dedicated Linux x86_64 build machine or VM and an ordinary user account.
Ubuntu 24.04 is the package workflow's reference build host. The script also
accepts Debian/Ubuntu and RHEL/Fedora-family hosts with the required tools.
Alpine is an installation target, not an accepted build host for this script.
ARM, macOS and Windows are outside this procedure.

The default build always generates all three packages:

| Installation target | Architecture | Output for this baseline |
|---|---|---|
| Debian/Ubuntu | amd64 | `syswarden_4.10.0_amd64.deb` |
| RHEL/Fedora family | x86_64 | `syswarden-4.10.0-1.x86_64.rpm` |
| Alpine/OpenRC | x86_64 | `syswarden_4.10.0_x86_64.apk` |

Allow several GB of free disk space, including in `/tmp`, and sufficient RAM
for Go compilation. Time and peak memory depend on the machine. The script
uses fresh private Go caches, downloads locked modules and removes its
temporary workspace on exit. Building is not an offline operation.

The builder does not install SysWarden on the build host. Installing a standard
package on a running target can invoke the installation pipeline, including
dependencies, SSH configuration, firewall policy, hardening, integrations,
services and scheduled jobs. Use a disposable target first.

## 2. Install build prerequisites

Run only the block matching the build host, using its approved distribution
repositories. Resolve unavailable dependencies before proceeding.

Debian/Ubuntu:

```bash
sudo apt-get update
sudo apt-get install -y \
  bash git python3 ca-certificates curl ruby ruby-dev build-essential \
  binutils file rpm rpm2cpio cpio tar gzip xz-utils zip unzip \
  coreutils findutils diffutils grep sed gawk util-linux
```

RHEL/Fedora family:

```bash
sudo dnf install -y \
  bash git python3 ca-certificates curl ruby ruby-devel rubygems gcc make \
  binutils file rpm rpm-build cpio tar gzip xz zip unzip \
  coreutils findutils diffutils grep sed gawk util-linux
```

The v4.10.0 builder requires exactly Go 1.26.6 for linux/amd64, FPM 1.17.0
and nFPM 2.47.0. Install Go from the [official downloads](https://go.dev/dl/)
following the [Go installation instructions](https://go.dev/doc/install).
Select `go1.26.6.linux-amd64.tar.gz`, verify its SHA-256 against the official
listing and extract into a fresh directory. Do not overlay an existing Go
installation. Put that installation's `bin` directory first in `PATH`.

```bash
GOTOOLCHAIN=local go version
```

The required output is `go version go1.26.6 linux/amd64`. The builder refuses
to download a missing Go toolchain implicitly. A newer compiler does not
satisfy this pin.

Install FPM and nFPM under your user account, outside the source checkout.
Keep this shell open for subsequent steps:

```bash
set -euo pipefail
SW_BUILD_TOOLS="${HOME}/.local/share/syswarden-build-tools"
mkdir -p "${SW_BUILD_TOOLS}/bin" "${SW_BUILD_TOOLS}/gems"
export GEM_HOME="${SW_BUILD_TOOLS}/gems"
export GEM_PATH="${GEM_HOME}"
export PATH="${SW_BUILD_TOOLS}/bin:${GEM_HOME}/bin:${PATH}"
gem install --no-document fpm -v 1.17.0
GOTOOLCHAIN=local GOWORK=off GOFLAGS='' GOENV=off \
  GOBIN="${SW_BUILD_TOOLS}/bin" \
  go install github.com/goreleaser/nfpm/v2/cmd/nfpm@v2.47.0 # Version-specific tool pin.
fpm --version
go version -m "$(command -v nfpm)"
```

FPM must report `1.17.0`. nFPM metadata must contain module
`github.com/goreleaser/nfpm/v2` at version `2.47.0` (with a `v` prefix in Go metadata). The builder requires its executable
to be a regular file, not a symlink. These methods are documented by
[FPM](https://fpm.readthedocs.io/en/latest/installation.html) and
[nFPM](https://nfpm.goreleaser.com/docs/install/); SysWarden pins the versions.

## 3. Fetch and freeze a clean source revision

Start from a fresh clone in a parent directory of your choice. Keep notes,
tool installations and logs outside it. Preserve any existing working tree;
do not discard changes or run `git clean` to satisfy the builder.

```bash
git clone https://github.com/duggytuxy/syswarden.git syswarden-source-build
cd syswarden-source-build
git fetch origin main
SW_SOURCE_SHA="$(git rev-parse --verify 'origin/main^{commit}')"
git switch --detach "${SW_SOURCE_SHA}"
git status --short
printf 'Source commit: %s\n' "${SW_SOURCE_SHA}"
./scripts/versioning.sh inspect --repo .
```

`git status --short` must print nothing. Record the full commit and reported
version. This selects the latest `main` fetched at that moment, then freezes
it even if `main` changes during the build. A source ZIP without Git metadata
cannot replace this clone.

To reproduce the reviewed revision instead, select it before building:

```bash
SW_SOURCE_SHA=8fc22a6bae4adfcf62d80e90da6387f2aa6db1c7
git switch --detach "${SW_SOURCE_SHA}"
./scripts/versioning.sh inspect --repo .
```

This revision reports `v4.10.0`. Later revisions may declare another version
or require other tools; read their builder before using these instructions.
Do not edit the changelog, version files or dependency manifests to force a
package name. A commit ID identifies content; it does not prove release status.

## 4. Build the standard packages

From the clean repository root, as the same ordinary user:

```bash
set -euo pipefail
SW_SOURCE_VERSION="$(./scripts/versioning.sh inspect --repo .)"
SW_PACKAGE_VERSION="${SW_SOURCE_VERSION#v}"
SW_BUILD_LOG="$(mktemp "${TMPDIR:-/tmp}/syswarden-build-log.XXXXXXXX")"
bash ./build_packages.sh 2>&1 | tee "${SW_BUILD_LOG}"
git status --short
printf 'Build log: %s\n' "${SW_BUILD_LOG}"
```

`pipefail` prevents `tee` from hiding a failed build. Continue only after exit
status zero, the final `[SUCCESS]` message and an unchanged clean checkout.
Do not build with `sudo`. `dist` and `dist/packages` must be real directories
owned by the build user and group; the script secures them with mode `0700`.

The script materializes the exact commit, builds the CLI, core and TUI, checks
binary provenance and packaging contracts, then writes packages and
`SHA256SUMS.txt` into `dist/packages`. It uses AMD64 level v1 and
`CGO_ENABLED=0`; APK has separate static binaries.

The optional `--rhel-package-owned-profile` flag selects a different RPM
integration model and filename, `syswarden-4.10.0-1.rhelpo.x86_64.rpm`. DEB
and APK remain standard. Both RPM variants share the package name `syswarden`
and cannot coexist. For that variant, follow the exact revision's
[profile reference](https://github.com/duggytuxy/syswarden/blob/8fc22a6bae4adfcf62d80e90da6387f2aa6db1c7/extensions/rhel-package-owned/README.md),
whose activation and removal differ from the standard RPM steps below.

## 5. Verify and retain the build

```bash
(
  cd dist/packages
  sha256sum --check --strict SHA256SUMS.txt
)
git rev-parse HEAD
```

Every listed package must pass. Save the commit, tool versions, log, packages
and checksum manifest together outside the clone before another build.
Rebuilding the same source version can replace the same filenames; the
version alone cannot distinguish two source commits.

Checksums detect changed bytes relative to your retained manifest. They are
not publisher signatures. This script does not create an official signed
release, signed tag, signed update manifest or protected qualification bundle.
Local packages do not inherit official SysWarden package signatures.

Transfer packages over an authenticated channel such as SSH with a verified
host key, retaining the expected hash independently on the build machine.
Recheck that hash on the target. If transferring only one package, verify its
specific filename and digest rather than treating missing manifest entries as
successful verification.

## 6. Prepare the target and recovery path

Before a fresh installation or upgrade:

1. Confirm the target distribution and x86_64 architecture match the package.
2. Keep a verified console and a second operator SSH session open.
3. Take and test a complete pre-installation VM or volume snapshot. For an
   existing installation, retain configuration, lists, trust material, package
   identity and firewall state as private recovery data.
4. Review package hooks and effective SSH, firewall, HA and feed settings.
   Preserve operator access in both host and provider firewalls.
5. Use approved repositories for runtime dependencies. Do not stop an existing
   firewall frontend merely to make installation proceed.

The default `keep` backend preserves firewall service ownership but can still
commit nftables policy and reconcile bounded rules for one already active
supported UFW or firewalld frontend. It does not imply unchanged firewall rules.
An HA cluster or historical migration needs its own version-specific recovery
and coordination plan; this generic procedure does not establish compatibility.

## 7. Install one matching standard package

Run only the block for your target from the directory holding the verified
package. Replace `4.10.0` only when your recorded source version differs.

Debian/Ubuntu:

```bash
SW_PACKAGE_VERSION=4.10.0
sudo apt-get update
sudo apt-get install "./syswarden_${SW_PACKAGE_VERSION}_amd64.deb"
```

RHEL/Fedora family:

```bash
SW_PACKAGE_VERSION=4.10.0
sudo dnf install "./syswarden-${SW_PACKAGE_VERSION}-1.x86_64.rpm"
```

If local policy rejects your unsigned RPM, stop and use a package signed with
an accepted organizational key or the official signed release route. Do not
globally disable signature checking.

Alpine/OpenRC:

```bash
SW_PACKAGE_VERSION=4.10.0
sudo apk add --no-cache cronie cronie-openrc
sudo apk add --allow-untrusted "./syswarden_${SW_PACKAGE_VERSION}_x86_64.apk"
```

Here `--allow-untrusted` applies to the explicitly named unsigned package you
built and independently hash-checked. It does not authenticate origin and must
not be used to accept unverified downloaded packages.

Alpine automatic activation requires the exact supported Cronie/OpenRC state.
If the package reports payload-only installation, retain its output and follow
its emitted fail-fast, two-phase activation instructions after the transaction
completes. Do not substitute a manual `syswarden install` or shortened
scheduler commands. A committed APK payload alone does not prove activation.

An offline systemd image transaction can also defer activation. Follow its
emitted instructions after booting the real target. Package-manager success
alone does not establish runtime readiness.

## 8. Check the installed system

After the normal activation path on a running target:

```bash
sudo syswarden
sudo syswarden config validate --path /etc/syswarden/config
sudo syswarden audit
```

The first command prints the CLI version. Configuration validation is
read-only. Review audit findings, package logs, SSH access and firewall state.
The audit is an operational diagnostic, not release qualification or a
compliance certificate.

On systemd targets:

```bash
sudo systemctl status syswarden-core.service syswarden-firewall.service --no-pager
sudo journalctl -u syswarden-core.service -u syswarden-firewall.service -n 80 --no-pager
```

On Alpine after the emitted activation procedure:

```bash
sudo rc-service syswarden-core status
sudo rc-service syswarden-firewall status
```

Logs can contain private configuration or event data; redact them before
sharing. Check package identity with `dpkg-query -W syswarden`,
`rpm -q syswarden` or `apk info -v syswarden`. For binary provenance, run
`go version -m` on a retained copy of each installed binary under `/opt/syswarden/bin`.
Its `vcs.revision` must match the recorded commit and `vcs.modified` must be
`false`.

## 9. Refresh, troubleshoot or recover

For a later commit, repeat the clean-clone, frozen-revision and build procedure.
`syswarden update` consumes the signed release channel, not arbitrary `main`
source builds.

An unchanged package version may be considered already installed. After
checking the replacement file and recovery snapshot, use APT's `--reinstall`
or DNF's `reinstall` with the exact local filename. On Alpine, first verify
local-file replacement behavior for the installed apk-tools version on a
disposable target. Never change source versioning just to force installation.

| Failure | Next step |
|---|---|
| Missing or wrong tool version | Restore the exact pins and check `PATH`; retain builder checks. |
| Dirty checkout or inherited Git redirection | Use a fresh clean clone in a normal shell; preserve existing work. |
| Unsafe output ownership or paths | Use a fresh clone owned by the build user; avoid symlinked output and root builds. |
| Download, memory or disk failure | Check approved network access and resources, then rerun the same commit. |
| Partial output after failure | Retain the log; do not install partial or stale artifacts. |
| Configuration, scheduler or service failure | Keep recovery access and resolve the exact failure before activation. |
| Lost access or failed migration | Use the verified console and restore the complete pre-installation snapshot. |

Package removal or an in-place downgrade does not restore the previous OS,
configuration and firewall state. Retain the snapshot until acceptance and
recovery checks are complete.

## Source references

- [Reviewed builder](https://github.com/duggytuxy/syswarden/blob/8fc22a6bae4adfcf62d80e90da6387f2aa6db1c7/build_packages.sh)
- [Current builder](https://github.com/duggytuxy/syswarden/blob/main/build_packages.sh)
- [Reviewed package workflow](https://github.com/duggytuxy/syswarden/blob/8fc22a6bae4adfcf62d80e90da6387f2aa6db1c7/.github/workflows/package.yml)
- [Reviewed CLI commands](https://github.com/duggytuxy/syswarden/blob/8fc22a6bae4adfcf62d80e90da6387f2aa6db1c7/testdata/contracts/cli-command-tree.json)
