Self-Extracting Installer
Feature Name
self-extracting-installer
Goal
Produce a single .run self-extracting archive per supported architecture as
part of the client release pipeline. The archive bundles the client binary,
arch-matched frpc, configuration templates, systemd units, and an artifact
manifest so that operators on systemd Linux distros without dpkg or rpm can
install Nixstasis with one command and no manual file placement. Operators
should invoke downloaded installers with sh nixstasis-<version>-linux-<arch>.run
because GitHub Release downloads do not preserve the executable bit.
Source Of Intent
docs/src/planned-features.md, featureself-extracting-installer- Prior review findings M6 and Q3 from
nistasis.issues_resolved.md
Users
- Operators running systemd Linux distros without native deb/rpm support.
- CI pipelines that need a single download artifact for fleet provisioning.
- Developers validating the install experience without building from source.
Requirements
- Produce a
.runself-extracting archive for each release architecture (linux/amd64,linux/arm64). - Each archive contains a flat staging directory with:
nixstasisbinary (copied from GoReleaserdist/build output)frpcbinary (arch-matched, frombuild/root-dir/usr/libexec/nixstasis/)frpc.toml(frombuild/root-dir/usr/share/nixstasis/)config.example.yaml(frombuild/root-dir/usr/share/nixstasis/)nixstasis-poll.service(frombuild/root-dir/lib/systemd/system/)nixstasis-poll.path(frombuild/root-dir/lib/systemd/system/)nixstasis-registration.service(frombuild/root-dir/lib/systemd/system/)install.sh(FHS placement script)artifacts.json(manifest)
install.shmaps flat archive files to their FHS paths:
nixstasis->/usr/bin/nixstasisfrpc->/usr/libexec/nixstasis/frpcfrpc.toml->/usr/share/nixstasis/frpc.toml(always replaced on upgrade; client-owned, not operator-edited)config.example.yaml->/usr/share/nixstasis/config.example.yaml- Seeds
/etc/nixstasis/config.yamlfromconfig.example.yamlif not already present (matching nfpm postinstall behavior) nixstasis-poll.service->/lib/systemd/system/nixstasis-poll.servicenixstasis-poll.path->/lib/systemd/system/nixstasis-poll.pathnixstasis-registration.service->/lib/systemd/system/nixstasis-registration.service
install.shmust be idempotent and safe for upgrades:
- Overwrite binaries and systemd units unconditionally.
- Preserve existing
/etc/nixstasis/config.yamlunless--force-configis passed. - Print installed file paths and versions to stdout.
artifacts.jsoncontains:
version: release version string (sourced from GoReleaserdist/metadata.json)arch: target architecturebuild_date: ISO 8601 timestampfiles: array of{path, sha256, mode}entries for every bundled file (paths are flat archive-relative names, not FHS destinations)
- The release workflow produces
.runarchives intodist/afterverify_artifacts.shpasses, and uploads them alongside existing release artifacts. verify_artifacts.shis extended to validate.runarchive contents and manifest integrity.frpcis consumed frombuild/root-dir/usr/libexec/nixstasis/frpc_<arch>(already staged byfetch_frpc.shbefore GoReleaser runs), not downloaded separately.
Constraints
- Do not embed
frpcin the Go client binary. FRPS_SERVER_ADDRremains a runtime env var, not baked into the archive.- Systemd units must retain
PrivateTmp=true. build/root-dirstays as the GoReleaser staging source for file templates.packages/frpremains the shared source of truth for FRP version and checksums.- The self-extracting archive is an additional release artifact; it does not
replace
.deb,.rpm, or.tar.gzoutputs. makeselfis the archive tool. It is available in Ubuntu 24.04 viaapt-get install makeselfand produces POSIX-compatible.runfiles.
Non-Goals
- Replacing
.debor.rpmpackaging for distros that support them. - Interactive TUI installer or configuration wizard.
- Automatic
systemctl enableorsystemctl starton install. - Uninstall support (can be added later).
- macOS or Windows support.
- Signing the
.runarchive (can be added later with GPG).
Design
Archive Assembly
A new script packages/client/scripts/release/build_installer.sh assembles
the .run archive:
- Accept
DIST_DIR(GoReleaser dist directory, defaultdist) andARCH(amd64orarm64) as inputs. - Create a temporary staging directory.
- Copy the compiled
nixstasisbinary from the GoReleaser build output indist/nixstasis_linux_<arch>/nixstasis. - Copy
frpcfrombuild/root-dir/usr/libexec/nixstasis/frpc_<arch>and rename tofrpc. - Copy config files from
build/root-dir/:usr/share/nixstasis/frpc.toml->frpc.tomlusr/share/nixstasis/config.example.yaml->config.example.yaml
- Copy systemd units from
build/root-dir/lib/systemd/system/:nixstasis-poll.servicenixstasis-poll.pathnixstasis-registration.service
- Copy
install.shfromscripts/release/install.sh. - Read version from
dist/metadata.json(GoReleaser output). - Generate
artifacts.jsonby computing sha256 and recording mode for each file in staging. - Run
makeself --nox11 <staging> <output> <label>to producenixstasis-<version>-linux-<arch>.runintodist/.
Install Script
packages/client/scripts/release/install.sh is a POSIX shell script that:
- Checks for root privileges (
id -uequals 0; avoids$EUIDwhich is bash-only). - Requires a running systemd host.
- Creates target directories if they do not exist.
- Installs binaries and systemd units with correct permissions.
- Conditionally installs config files:
- Always install
/usr/share/nixstasis/frpc.tomlfrom the archive so client upgrades can update the FRP template. - Seed
/etc/nixstasis/config.yamlfromconfig.example.yamlif it does not exist (unless--force-config, which overwrites both).
- Prints a summary of installed files and a reminder to configure
/etc/nixstasis/config.yamland runsystemctl enable.
Manifest Format
{
"version": "0.1.0",
"arch": "amd64",
"build_date": "2026-05-13T12:00:00Z",
"files": [
{"path": "nixstasis", "sha256": "abc123...", "mode": "0755"},
{"path": "frpc", "sha256": "def456...", "mode": "0755"},
{"path": "frpc.toml", "sha256": "...", "mode": "0644"},
{"path": "config.example.yaml", "sha256": "...", "mode": "0644"},
{"path": "nixstasis-poll.service", "sha256": "...", "mode": "0644"},
{"path": "nixstasis-poll.path", "sha256": "...", "mode": "0644"},
{"path": "nixstasis-registration.service", "sha256": "...", "mode": "0644"},
{"path": "install.sh", "sha256": "...", "mode": "0755"}
]
}
CI Integration
In release_client.yml, after verify_artifacts.sh:
apt-get install -y makeself- Run
build_installer.shforamd64andarm64. .runfiles are written todist/.- For snapshot builds:
dist/is already uploaded asnixstasis-client-snapshot. - For tag builds: GoReleaser builds artifacts with publishing skipped, then the
workflow creates the GitHub release with the verified files from
dist/.
Verification Extension
verify_artifacts.sh gains a new section that:
- Finds all
.runfiles in$DIST_DIR. - Extracts each to a temp directory with
--noexec --target <dir>. - Validates
artifacts.jsonexists and is valid JSON (usingjqorpython3 -m json.tool). - Validates every file listed in
artifacts.jsonexists and its sha256 matches. - Validates the archive contains
install.sh,nixstasis, andfrpc. - Requires at least one
.runfile only whenVERIFY_INSTALLERS=true, so the existing pre-installer artifact verification step can still validate tar, deb, and rpm outputs before installers are built.
Risks And Tradeoffs
makeselfis a CI runtime dependency; pinning its version prevents surprising format changes. This feature uses the Ubuntu 24.04 package first; explicit version pinning can be added later if release reproducibility needs it.- Self-extracting archives are less auditable than plain tarballs; operators
who prefer inspection can use
--noexec --target <dir>to extract without running. install.shconfig preservation adds conditional logic that must be tested for both fresh install and upgrade paths.- No uninstall script means operators must manually remove files or wait for a future feature.
EUIDis bash-only;install.shusesid -ufor POSIX compatibility.
Dependencies
packages/frp/bin/download_frp.sh(shared FRP acquisition, already used)packages/client/scripts/fetch_frpc.sh(stages frpc into build/root-dir).github/workflows/release_client.yml(release pipeline)packages/client/scripts/release/verify_artifacts.sh(artifact validation)packages/client/build/root-dir/(FHS layout source)prod.env(FRP version pins)packages/client/.goreleaser.yaml(archive structure reference)
Affected Docs
packages/client/README.md(document.runinstaller usage)docs/src/planned-features.md(keep feature status and delivered behavior reconciled as the feature moves fromin-spectoin-progressandcompleted)
Suggested Validation
- CI step that builds
.runfrom snapshot artifacts, extracts, and verifies manifest integrity. - Extraction test on Ubuntu 24.04 (CI runner) confirming all files land.
- Manual smoke test on a systemd distro without
dpkg/rpmto confirm FHS placement. - Upgrade test: install v1, then install v2, confirm binaries are replaced but config is preserved.
- Config seeding test: fresh install seeds
config.yamlfrom example; upgrade preserves existingconfig.yaml.