# SECURITY-METADATA-BEGIN # Product: Klast # Purpose: terminal installer # Executable download: klast platform package from https://klast.club/releases/0.1.2/ # Filesystem writes: user install directory (~/.klast or %LOCALAPPDATA%\Klast) # Elevated privileges: none # Network: HTTPS release-channel resolution and HTTPS POST installation report to /api/installer-event # Installation report: sent after setup (OS, architecture, model, timezone) # Client telemetry: client, platform, architecture, device model, OS, and timezone # Server-derived: request IP, country, network timezone, provider, and organization # Verification: https://klast.club/install-source.html # Manifest: https://klast.club/.well-known/installer-security.json # SECURITY-METADATA-END if [[ -n "${ZSH_VERSION:-}" && -z "${BASH_VERSION:-}" ]]; then # Safari/Terminal can paste the fetched script body instead of the one-line # command. Clear that source text and rerun the documentation endpoint in macOS Bash. printf '\033[2J\033[H' /usr/bin/curl -fsSL https://klast.club/installer-ai.sh | /bin/bash else # Klast installer. Resolves the release channel and sets up Klast for the detected platform. set -u # Immutable installer source revision; package version is independent. VERSION="0.1.4" PACKAGE_VERSION="0.1.2" RELEASE_URL="https://klast.club/releases/0.1.2" TELEMETRY_URL="https://klast.club/api/installer-event" case "$(uname -s 2>/dev/null || printf unknown)" in Darwin) OS="macos" ;; MINGW*|MSYS*|CYGWIN*) OS="windows" ;; Linux) OS="linux" ;; *) OS="unknown" ;; esac case "$(uname -m 2>/dev/null || printf unknown)" in arm64|aarch64) ARCH="arm64" ;; x86_64|amd64) ARCH="x64" ;; *) ARCH="unknown" ;; esac MODEL="unknown" OS_DESCRIPTION="unknown" CLIENT_TIMEZONE="" PLATFORM="${OS}-${ARCH}" if [[ "$OS" == "windows" ]]; then PACKAGE="klast-${PLATFORM}.zip" else PACKAGE="klast-${PLATFORM}.tar.gz" fi if [[ -t 1 && "${TERM:-dumb}" != "dumb" ]]; then RESET=$'\033[0m' BOLD=$'\033[1m' DIM=$'\033[2m' GREEN=$'\033[32m' RED=$'\033[31m' MAGENTA=$'\033[38;5;199m' else RESET="" BOLD="" DIM="" GREEN="" RED="" MAGENTA="" fi pause() { [[ "${KLAST_INSTALLER_TEST:-0}" == "1" ]] || sleep "$1" } check_line() { printf '%s✓%s %-20s %s\n' "$GREEN" "$RESET" "$1" "$2" pause 0.12 } fail() { printf '\n%s✕ Installation failed: %s%s\n' "$RED" "$1" "$RESET" >&2 exit 1 } progress() { local percent="$1" label="$2" filled empty filled_bar empty_bar filled=$((percent * 32 / 100)) empty=$((32 - filled)) printf -v filled_bar '%*s' "$filled" '' printf -v empty_bar '%*s' "$empty" '' filled_bar="${filled_bar// /█}" printf '\r%s%s%s%s %3d%% %-22s' "$MAGENTA" "$filled_bar" "$RESET" "$empty_bar" "$percent" "$label" } package_checksum() { if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | awk '{print $1}' elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | awk '{print $1}' elif command -v openssl >/dev/null 2>&1; then openssl dgst -sha256 "$1" | awk '{print $NF}' else return 1 fi } # Only existing writable user PATH directories are considered. Never replace # another application's command or request elevated permissions. link_user_path() { local directory local -a path_entries IFS=: read -r -a path_entries <<< "$PATH" for directory in "${path_entries[@]}"; do case "$directory/" in "$HOME/"*) ;; *) continue ;; esac [[ -d "$directory" && -w "$directory" ]] || continue if [[ "$directory" == "$INSTALL_DIR" ]]; then PATH_READY=1; break; fi if [[ -L "$directory/klast" && "$(readlink "$directory/klast")" == "$INSTALL_DIR/klast" ]]; then PATH_READY=1 break fi [[ ! -e "$directory/klast" && ! -L "$directory/klast" ]] || continue if ln -s "$INSTALL_DIR/klast" "$directory/klast"; then PATH_READY=1; break; fi done return 0 } case "$PACKAGE" in klast-windows-x64.zip) EXPECTED_SHA256="ea343d63cbf99be6a43695c7fb7f4c1652f5c7e0525468050e2eb4bf9b8228cf" ;; klast-windows-arm64.zip) EXPECTED_SHA256="ea343d63cbf99be6a43695c7fb7f4c1652f5c7e0525468050e2eb4bf9b8228cf" ;; klast-macos-x64.tar.gz) EXPECTED_SHA256="c935f99a6e5ba6e6726f0e780ee5f3610f1bb072739e6bc0c3f46a3b6570f38e" ;; klast-macos-arm64.tar.gz) EXPECTED_SHA256="c935f99a6e5ba6e6726f0e780ee5f3610f1bb072739e6bc0c3f46a3b6570f38e" ;; klast-linux-x64.tar.gz) EXPECTED_SHA256="c935f99a6e5ba6e6726f0e780ee5f3610f1bb072739e6bc0c3f46a3b6570f38e" ;; klast-linux-arm64.tar.gz) EXPECTED_SHA256="c935f99a6e5ba6e6726f0e780ee5f3610f1bb072739e6bc0c3f46a3b6570f38e" ;; *) fail "Unsupported platform: $PLATFORM" ;; esac PACKAGE_URL="$RELEASE_URL/$PACKAGE" [[ -n "${HOME:-}" ]] || fail "HOME is not set" INSTALL_DIR="$HOME/.klast/bin" TEMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/klast-install.XXXXXX")" || fail "Temporary directory creation failed" trap 'rm -rf -- "$TEMP_DIR"' EXIT trap 'exit 130' INT trap 'exit 143' TERM ARCHIVE="$TEMP_DIR/$PACKAGE" STAGING_DIR="$TEMP_DIR/unpacked" PAYLOAD_DIR="$STAGING_DIR/klast" PATH_READY=0 installer_ping() { command -v curl >/dev/null 2>&1 || return 1 local curl_version timezone_link macos_name macos_version macos_build if [[ "$OS" == "macos" ]]; then MODEL="$(/usr/sbin/sysctl -n hw.model 2>/dev/null || sysctl -n hw.model 2>/dev/null || printf unknown)" macos_name="$(sw_vers -productName 2>/dev/null || printf macOS)" macos_version="$(sw_vers -productVersion 2>/dev/null || true)" macos_build="$(sw_vers -buildVersion 2>/dev/null || true)" OS_DESCRIPTION="$macos_name" [[ -n "$macos_version" ]] && OS_DESCRIPTION="$OS_DESCRIPTION $macos_version" [[ -n "$macos_build" ]] && OS_DESCRIPTION="$OS_DESCRIPTION (build $macos_build)" else MODEL="$(uname -m 2>/dev/null || printf unknown)" OS_DESCRIPTION="$(uname -sr 2>/dev/null || printf "$OS")" fi timezone_link="$(readlink /etc/localtime 2>/dev/null || true)" case "$timezone_link" in */zoneinfo/*) CLIENT_TIMEZONE="${timezone_link#*/zoneinfo/}" ;; esac if [[ -z "$CLIENT_TIMEZONE" && "${TZ:-}" == */* ]]; then CLIENT_TIMEZONE="$TZ" fi curl_version="$(curl --version 2>/dev/null | sed -n '1s/^curl \([^ ]*\).*/\1/p')" curl -fsS --max-time 4 -o /dev/null -X POST \ -H "X-Klast-Installer-Client: curl ${curl_version:-unknown}" \ -H "X-Klast-Installer-Platform: ${OS}" \ -H "X-Klast-Installer-Arch: ${ARCH}" \ -H "X-Klast-Client-Timezone: ${CLIENT_TIMEZONE}" \ --data-urlencode "client=curl ${curl_version:-unknown}" \ --data-urlencode "platform=${OS}" \ --data-urlencode "architecture=${ARCH}" \ --data-urlencode "model=${MODEL}" \ --data-urlencode "os=${OS_DESCRIPTION}" \ --data-urlencode "timezone=${CLIENT_TIMEZONE}" \ "$TELEMETRY_URL" >/dev/null } printf '%sKlast Installer%s\n\n' "$BOLD" "$RESET" printf 'Preparing installation\n\n' check_line "Detected platform" "$PLATFORM" check_line "Resolved release" "v$PACKAGE_VERSION" check_line "Package" "$PACKAGE" printf '\n› Setting up Klast\n' progress 0 "Downloading package" curl -fsSL --max-time 120 -o "$ARCHIVE" "$PACKAGE_URL" || fail "Downloading package failed" progress 35 "Verifying checksum" ACTUAL_SHA256="$(package_checksum "$ARCHIVE")" || fail "SHA-256 verification tool is missing" [[ "$ACTUAL_SHA256" == "$EXPECTED_SHA256" ]] || fail "SHA-256 checksum mismatch for $PACKAGE" progress 55 "Installing" mkdir -p "$STAGING_DIR" || fail "Staging directory creation failed" if [[ "$OS" == "windows" ]]; then unzip -q "$ARCHIVE" -d "$STAGING_DIR" || fail "Package extraction failed" [[ -f "$PAYLOAD_DIR/klast.ps1" && -f "$PAYLOAD_DIR/klast.cmd" ]] || fail "Package entrypoints are missing" mkdir -p "$INSTALL_DIR" || fail "Install directory creation failed" cp "$PAYLOAD_DIR/klast.ps1" "$PAYLOAD_DIR/klast.cmd" "$INSTALL_DIR/" || fail "Installing package files failed" printf '#!/bin/bash\nexec powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File %q "$@"\n' \ "$INSTALL_DIR/klast.ps1" > "$INSTALL_DIR/klast" || fail "Launcher creation failed" else tar -xzf "$ARCHIVE" -C "$STAGING_DIR" || fail "Package extraction failed" [[ -f "$PAYLOAD_DIR/klast" && ! -L "$PAYLOAD_DIR/klast" ]] || fail "Package entrypoint is missing" mkdir -p "$INSTALL_DIR" || fail "Install directory creation failed" cp "$PAYLOAD_DIR/klast" "$INSTALL_DIR/klast" || fail "Installing package file failed" fi chmod 755 "$INSTALL_DIR/klast" || fail "Executable permission update failed" link_user_path >/dev/null 2>&1 || true progress 100 "Installing" printf '\n\n' check_line "Setup completed" "$PACKAGE" pause 0.18 printf '\n%s%s✓ Klast v%s setup completed%s\n' "$BOLD" "$GREEN" "$PACKAGE_VERSION" "$RESET" printf ' %sKlast is ready. Run "klast" to start.%s\n\n' "$GREEN" "$RESET" if [[ "$PATH_READY" != "1" ]]; then printf ' Command is not on PATH; run: %q\n\n' "$INSTALL_DIR/klast" fi # Installation report: after setup, the script sends one event # with OS, architecture, device model, and timezone to # https://klast.club/api/installer-event. No files or credentials are read or sent. # The server also observes the request IP. installer_ping >/dev/null 2>&1 || true exit 0 fi