diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7071dd5..dace5cd 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -54,3 +54,15 @@ jobs:
HONEYBADGER_API_KEY: ${{ secrets.HONEYBADGER_API_KEY }}
NDT7_CLIENT_NAME: ${{ secrets.NDT7_CLIENT_NAME }}
run: npx -p @semantic-release/changelog -p @semantic-release/exec -p @semantic-release/git semantic-release --debug=true
+
+ - name: Increment version and update installer scripts
+ id: increment_version
+ run: |
+ # Get the latest release tag
+ latest_tag=$(git describe --tags --abbrev=0)
+ release_version=${latest_tag#v}
+ remote_file="client_${release_version}_darwin_amd64.tar.gz"
+
+ # Update the installer scripts with the new version and file name
+ sed -i "s/^release_version=.*/release_version=\"${release_version}\"/" ./scripts/installers/*.sh
+ sed -i "s/^remote_file=.*/remote_file=\"${remote_file}\"/" ./scripts/installers/*.sh
diff --git a/scripts/installers/darwin-linux-install.sh b/scripts/installers/darwin-linux-install.sh
new file mode 100755
index 0000000..9a0f11a
--- /dev/null
+++ b/scripts/installers/darwin-linux-install.sh
@@ -0,0 +1,457 @@
+#!/bin/bash
+
+# ======================== imUp Client Installer ======================== #
+# This script installs the imUp client on MacOS and Linux systems. #
+# #
+# ----------------------------- Dependencies ---------------------------- #
+# - cat #
+# - chmod (mac only, sets permissions on the launch agent plist file) #
+# - chown (mac only, sets permissions on the launch agent plist file) #
+# - curl (used to download imUp) #
+# - grep #
+# - launchctl (mac only) #
+# - mkdir (makes the imUp install directory) #
+# - mktemp (used to create a temp directory to store the imUp tarball) #
+# - pkill (used to kill the imUp process during uninstall) #
+# - printf #
+# - sed #
+# - sudo (linux if != root user, and mac always) #
+# - systemctl (linux only) #
+# - tar #
+# - uname #
+# ======================================================================= #
+
+# exit on any error
+set -e
+
+# =================== [START] Constants and Variables =================== #
+# TODO: doesn't do anything yet
+install_script_version=0.2.0
+build="stable"
+
+operating_system=$(uname | tr '[:upper:]' '[:lower:]')
+
+imup_github_releases_url="https://github.com/imup-io/client/releases"
+imup_github_downloads_url="$imup_github_releases_url/download"
+# this is a special URL that redirects to the latest tag
+imup_github_latest_url="$imup_github_releases_url/latest"
+
+# TODO: make sure $HOME exists or let them set a variable for this?
+imup_install_dir="$HOME/imUp"
+
+terms_of_service_error_message="
+Error: You must accept the Terms of Service in order to install the imUp client. https://app.imup.io/terms
+Please set the ACCEPT_THE_TOS environment variable to 'yes' to accept the Terms of Service.
+"
+
+required_dependencies=(
+ "cat"
+ "curl"
+ "grep"
+ "ls"
+ "mkdir"
+ "mktemp"
+ "pkill"
+ "printf"
+ "rm"
+ "sed"
+ "tar"
+ "uname"
+)
+
+required_dependencies_darwin=(
+ "chmod"
+ "chown"
+ "launchctl"
+ "sudo"
+)
+
+required_dependencies_linux=(
+ "systemctl"
+)
+
+declare -a options=()
+# ==================== [END] Constants and Variables ==================== #
+
+# ========================== [START] Functions ========================== #
+# use sudo if the user isn't root
+sudo() {
+ if [ "$EUID" -ne 0 ]
+ then
+ command sudo "$@"
+ else
+ "$@"
+ fi
+}
+
+uninstall_client_darwin() {
+ # terminate the imUp process
+ sudo pkill -f "imUp" || true
+
+ # Remove the imUp.plist file
+ sudo launchctl unload /Library/LaunchAgents/imUp.plist || true
+ sudo rm /Library/LaunchAgents/imUp.plist || true
+
+ # remove the imUp files
+ sudo rm -rf "$imup_install_dir"
+
+ exit 0
+}
+
+uninstall_client_linux() {
+ # terminate the imUp process
+ sudo pkill -f "imUp" || true
+
+ # Remove the imUp.service file
+ sudo systemctl stop imUp.service
+ sudo systemctl disable imUp.service
+ sudo rm -f /etc/systemd/system/imUp.service
+ sudo systemctl daemon-reload
+
+ # remove the imUp files
+ sudo rm -rf "$imup_install_dir" || true
+
+ exit 0
+}
+
+# exit with error message if the user doesn't accept the ToS
+check_user_accepted_tos() {
+ if [ "$ACCEPT_THE_TOS" != "yes" ]
+ then
+ echo "$terms_of_service_error_message"
+ exit 1
+ fi
+}
+
+# if any of the required dependencies are not installed, prints
+# an error message with the missing dependencies and exits
+check_required_dependencies_are_installed() {
+ declare -a missing_dependencies=()
+
+ for dependency in "${required_dependencies[@]}"
+ do
+ if ! command -v "$dependency" &> /dev/null
+ then
+ missing_dependencies+=("$dependency")
+ fi
+ done
+
+ # if on mac, check for mac specific dependencies
+ if [ "$operating_system" == "darwin" ]
+ then
+ for dependency in "${required_dependencies_darwin[@]}"
+ do
+ if ! command -v "$dependency" &> /dev/null
+ then
+ missing_dependencies+=("$dependency")
+ fi
+ done
+ fi
+
+ # if on linux, check for linux specific dependencies
+ if [ "$operating_system" == "linux" ]
+ then
+ for dependency in "${required_dependencies_linux[@]}"
+ do
+ if ! command -v "$dependency" &> /dev/null
+ then
+ missing_dependencies+=("$dependency")
+ fi
+ done
+ fi
+
+ # if there are any missing dependencies, print an error message and exit
+ if [ ${#missing_dependencies[@]} -ne 0 ]
+ then
+ echo "Error: The following dependencies are required to install the imUp client:"
+ for dependency in "${missing_dependencies[@]}"
+ do
+ echo "- $dependency"
+ done
+ exit 1
+ fi
+}
+
+# exits if required environment variables are not set
+check_required_environment_variables() {
+ if [ -z "$API_KEY" ] && [ -z "$EMAIL" ]; then
+ echo "Error: One or both of the 'API_KEY' and 'EMAIL' environment variables need to be set."
+ exit 1
+ fi
+}
+
+# read environment variables and build an array
+# of options to pass to the client
+create_options_array() {
+ if [ -n "$ALLOWLISTED_IPS" ]; then
+ options+=("--allowlisted-ips='$ALLOWLISTED_IPS'")
+ fi
+ if [ -n "$API_KEY" ]; then
+ options+=("--key='$API_KEY'")
+ fi
+ if [ -n "$BLOCKLISTED_IPS" ]; then
+ options+=("--blocklisted-ips='$BLOCKLISTED_IPS'")
+ fi
+ if [ -n "$EMAIL" ]; then
+ options+=("--email='$EMAIL'")
+ fi
+ if [ -n "$GROUP_ID" ]; then
+ options+=("--group-id='$GROUP_ID'")
+ fi
+ if [ -n "$HOST_ID" ]; then
+ options+=("--host-id='$HOST_ID'")
+ fi
+ if [ -n "$IMUP_ADDRESS" ]; then
+ options+=("--api-post-connection-data='$IMUP_ADDRESS'")
+ fi
+ if [ -n "$IMUP_ADDRESS_SPEEDTEST" ]; then
+ options+=("--api-post-speed-test-data='$IMUP_ADDRESS_SPEEDTEST'")
+ fi
+ if [ "$NO_SPEED_TEST" == "true" ]; then
+ options+=("--no-speed-test")
+ fi
+ if [ -n "$PING_ADDRESS" ]; then
+ options+=("--ping-addresses-external='$PING_ADDRESS'")
+ fi
+ if [ -n "$PING_ADDRESS_INTERNAL" ]; then
+ options+=("--ping-address-internal='$PING_ADDRESS_INTERNAL'")
+ fi
+ if [ -n "$PING_DELAY" ]; then
+ options+=("--ping-delay='$PING_DELAY'")
+ fi
+ if [ -n "$PING_ENABLED" ]; then
+ options+=("--ping='$PING_ENABLED'")
+ fi
+ if [ -n "$PING_INTERVAL" ]; then
+ options+=("--ping-interval='$PING_INTERVAL'")
+ fi
+ if [ -n "$PING_REQUESTS" ]; then
+ options+=("--ping-requests='$PING_REQUESTS'")
+ fi
+}
+
+# pretty prints the options array
+print_options_array() {
+ echo "Options:"
+ for option in "${options[@]}"
+ do
+ echo "- $option"
+ done
+ echo ""
+}
+
+log_msg() {
+ msg=$1
+ printf "%s\n" "$msg"
+}
+
+log_msg_blue() {
+ msg=$1
+ printf "\033[34m%s\033[0m\n" "$msg"
+}
+
+log_msg_red() {
+ msg=$1
+ printf "\033[31m%s\033[0m\n" "$msg"
+}
+
+# sets architecture variable based on uname -m
+get_arch() {
+ set +e
+
+ uname -m | grep -q x86_64
+ is_x86="$?"
+
+ uname -a | grep -q arm64
+ is_arm64="$?"
+
+ uname -m | grep -q 'i.*86'
+ is_32bit="$?"
+
+ architecture="$(uname -m)"
+
+ set -e
+
+ # account for running under rosetta
+ if [ "$is_x86" -eq 0 ]; then
+ if [ "$is_arm64" -eq 0 ]; then
+ architecture='arm64'
+ else
+ architecture='amd64'
+ fi
+ elif [ "$is_32bit" -eq 0 ]; then
+ architecture='x32'
+ elif [ "$architecture" = "arm64" ]; then
+ architecture='arm64'
+ elif [ "$architecture" = "aarch64" ]; then
+ architecture='arm64'
+ else
+ log_msg_red "Architecture '$architecture' not supported"
+ exit 1
+ fi
+}
+
+# creates a temporary directory and sets the imup_tarball_filepath variable
+get_temp_tarball_filepath() {
+ # create a temporary directory to store the imUp tarball
+ imup_temp_dir=$(mktemp -d -t 'imup-installer.XXXXXX')
+ imup_tarball_filepath="$imup_temp_dir/imUp-$operating_system-$architecture.tar.gz"
+}
+
+# gets the latest imUp release version and builds the download URL
+get_download_url() {
+ # follow the redirect and get the URL
+ imup_github_latest_tag_url=$(curl -s -L -o /dev/null -w '%{url_effective}' "$imup_github_latest_url")
+ # get the version number from the URL
+ imup_github_latest_release_version=$(echo "$imup_github_latest_tag_url" | sed -e 's/.*\/v//g')
+
+ imup_github_asset_name="client_"$imup_github_latest_release_version"_"$operating_system"_$architecture.tar.gz"
+
+ download_url="$imup_github_downloads_url/v$imup_github_latest_release_version/$imup_github_asset_name"
+}
+
+download_and_install_imup() {
+ # Download and install the client
+ log_msg_blue "Downloading imUp client from $download_url"
+ curl --fail --progress-bar -L $download_url -o $imup_tarball_filepath
+
+ log_msg_blue "Extracting client to $imup_install_dir"
+
+ # make clean install dir
+ rm -rf $imup_install_dir
+ mkdir -p $imup_install_dir
+
+ # extract tarball directly to install dir
+ tar -xzf $imup_tarball_filepath -C $imup_install_dir
+ rm -f $imup_tarball_filepath
+
+ echo ""
+ echo "Installed contents at $imup_install_dir:"
+ ls -la $imup_install_dir
+}
+
+install_macos_launch_agent() {
+ # Set the launch agent properties
+ tmpPLIST_PATH="./imUp.plist"
+ PLIST_PATH="/Library/LaunchAgents/imUp.plist"
+ LABEL="io.imUp"
+ RUN_AT_LOAD="true"
+ USER_ID="0"
+
+ # build the ProgramArguments array for the .plist file
+ program_arguments=""
+ for option in "${options[@]}"; do
+ if [ ! -z "$option" ]; then
+ program_arguments+="
+ $option
+ "
+ fi
+ done
+
+ # create the .plist file
+ echo "Heads up: This next step requires SUDO permissions"
+ echo "Please enter your password when prompted"
+ echo ""
+
+ # create the plist file
+ cat > "$tmpPLIST_PATH" <
+
+
+
+ Label
+ io.imUp
+ ProgramArguments
+
+ $imup_install_dir/imUp
+ $program_arguments
+
+ RunAtLoad
+
+
+
+EOF
+
+
+ # set the correct permissions on the launch agent plist file
+ sudo cp -f $tmpPLIST_PATH $PLIST_PATH
+ sudo chown root:wheel "$PLIST_PATH"
+ sudo chmod 644 "$PLIST_PATH"
+ rm -f $tmpPLIST_PATH
+
+ # load the launch agent
+ echo ""
+ echo "Loading launch agent..."
+ sudo launchctl load "$PLIST_PATH"
+
+ echo "Launch agent installed at $PLIST_PATH"
+}
+
+install_linux_systemd_service() {
+ # create the systemd service file
+ cat > /etc/systemd/system/imUp.service <