#!/bin/sh # tofa native installer — https://get.tofa.tv # # curl -fsSL https://get.tofa.tv | sh # # Downloads the latest beta build for your OS/arch, installs it as a background # service, and points you at http://localhost:33333 to claim the server to your # tofa account. Supports Linux (x86_64 / arm64, systemd) and macOS (Apple # Silicon). For Docker, Windows, or Intel Macs, see: # https://docs.tofa.tv/run-your-server.html # # This is readable on purpose — you're piping it to a shell, so read it first. set -eu DIST_BASE="https://github.com/tofatv/tofa-dist/releases/download/beta" PORT=33333 say() { printf '\033[1;36m==>\033[0m %s\n' "$1"; } warn() { printf '\033[1;33mwarning:\033[0m %s\n' "$1" >&2; } die() { printf '\033[1;31merror:\033[0m %s\n' "$1" >&2; exit 1; } # A setup token (SETUP_ACCESS_TOKEN) lets you claim the server from ANY device # via a tokened setup URL; without it the server only accepts a claim from its # own local network. We wire it in for headless (systemd) installs, which are # the ones most often reached from another machine. gen_token() { if command -v openssl >/dev/null 2>&1; then openssl rand -hex 24 else LC_ALL=C od -An -tx1 -N24 /dev/urandom | tr -d ' \n' fi } # Best-effort primary LAN IPv4, for the "from another device" claim URL. lan_ip() { _ip="" command -v hostname >/dev/null 2>&1 && _ip="$(hostname -I 2>/dev/null | awk '{print $1}')" if [ -z "$_ip" ] && command -v ip >/dev/null 2>&1; then _ip="$(ip route get 1.1.1.1 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src"){print $(i+1); exit}}')" fi printf '%s' "$_ip" } # --- privilege helper (installer touches /opt, systemd, /Applications) ------- SUDO="" if [ "$(id -u)" -ne 0 ]; then if command -v sudo >/dev/null 2>&1; then SUDO="sudo" else die "need root to install a system service — re-run as root, or install sudo." fi fi command -v curl >/dev/null 2>&1 || die "curl is required." # --- detect platform --------------------------------------------------------- OS="$(uname -s)" ARCH="$(uname -m)" case "$OS" in Linux) case "$ARCH" in x86_64|amd64) PLATFORM="linux-x64" ;; aarch64|arm64) PLATFORM="linux-arm64" ;; *) die "unsupported Linux arch '$ARCH' (need x86_64 or aarch64)." ;; esac command -v systemctl >/dev/null 2>&1 || \ die "this installer targets systemd distros. For others, see https://docs.tofa.tv/run-your-server.html" ;; Darwin) case "$ARCH" in arm64) PLATFORM="macos-arm64" ;; *) die "Intel Macs aren't supported natively yet — use Docker: https://docs.tofa.tv/run-your-server.html" ;; esac ;; *) die "unsupported OS '$OS'. See https://docs.tofa.tv/run-your-server.html" ;; esac TARBALL="tofa-beta-${PLATFORM}.tar.gz" URL="${DIST_BASE}/${TARBALL}" say "Installing tofa (beta) for ${PLATFORM}" # --- download + verify checksum ---------------------------------------------- TMP="$(mktemp -d)" trap 'rm -rf "$TMP"' EXIT cd "$TMP" say "Downloading ${TARBALL}" curl -fsSL "$URL" -o "$TARBALL" || die "download failed: $URL" curl -fsSL "${URL}.sha256" -o "sum" || die "checksum download failed." say "Verifying checksum" EXPECTED="$(awk '{print $1}' sum)" if command -v sha256sum >/dev/null 2>&1; then ACTUAL="$(sha256sum "$TARBALL" | awk '{print $1}')" else ACTUAL="$(shasum -a 256 "$TARBALL" | awk '{print $1}')" fi [ "$EXPECTED" = "$ACTUAL" ] || die "checksum mismatch — refusing to install." tar xzf "$TARBALL" SRC="tofa-beta-${PLATFORM}" # --- install ----------------------------------------------------------------- if [ "$OS" = "Darwin" ]; then say "Installing tofa.app + launchd service (you'll be asked for your password)" $SUDO sh "${SRC}/install.sh" else say "Installing to /opt/tofa + systemd service" if ! getent passwd tofa >/dev/null 2>&1; then $SUDO useradd --system --user-group --shell /usr/sbin/nologin \ --home-dir /home/tofa --create-home tofa fi $SUDO mkdir -p /opt/tofa/lib /home/tofa/.tofa /etc/tofa $SUDO cp "${SRC}/tofa" /opt/tofa/tofa [ -f "${SRC}/tofa-updater" ] && $SUDO cp "${SRC}/tofa-updater" /opt/tofa/tofa-updater [ -f "${SRC}/package_info" ] && $SUDO cp "${SRC}/package_info" /opt/tofa/package_info [ -d "${SRC}/lib" ] && $SUDO cp "${SRC}/lib/"* /opt/tofa/lib/ $SUDO chmod 755 /opt/tofa/tofa [ -f /etc/tofa/tofa.env ] || printf '# tofa environment overrides (read by tofa.service)\n' | $SUDO tee /etc/tofa/tofa.env >/dev/null # Setup token for claiming from another device. Reuse an existing one so a # re-run keeps the same claim URL; otherwise mint and append it. if $SUDO grep -q '^SETUP_ACCESS_TOKEN=' /etc/tofa/tofa.env 2>/dev/null; then TOKEN="$($SUDO sh -c "grep '^SETUP_ACCESS_TOKEN=' /etc/tofa/tofa.env | head -1 | cut -d= -f2-")" else TOKEN="$(gen_token)" printf 'SETUP_ACCESS_TOKEN=%s\n' "$TOKEN" | $SUDO tee -a /etc/tofa/tofa.env >/dev/null fi $SUDO cp "${SRC}/systemd/tofa.service" /etc/systemd/system/tofa.service $SUDO chown -R tofa:tofa /opt/tofa /home/tofa/.tofa say "Starting the tofa service" $SUDO systemctl daemon-reload $SUDO systemctl enable tofa.service >/dev/null 2>&1 || true $SUDO systemctl restart tofa.service fi # --- wait for health, then point at the claim page --------------------------- say "Waiting for tofa to come up (first run downloads its database, ~1 min)…" i=0 until curl -fsS "http://localhost:${PORT}/api/v1/health" >/dev/null 2>&1; do i=$((i + 1)) if [ "$i" -gt 150 ]; then warn "tofa didn't answer on :${PORT} within ~2.5 min." [ "$OS" = "Linux" ] && warn "check: sudo journalctl -u tofa -n 50" exit 1 fi sleep 1 done # On Linux (headless-capable, systemd) we minted a setup token and can offer a # claim URL that works from another device. On macOS (Apple Silicon desktop) # setup happens on the same machine, so localhost is enough. printf '\n\033[1;32mtofa is running.\033[0m\n' if [ "$OS" = "Linux" ] && [ -n "${TOKEN:-}" ]; then LOCAL="http://localhost:${PORT}/setup?setup_token=${TOKEN}" LANIP="$(lan_ip)" printf 'Sign in to claim it to your account:\n' printf ' • On THIS machine: \033[1;36m%s\033[0m\n' "$LOCAL" if [ -n "$LANIP" ]; then printf ' • From another device: \033[1;36mhttp://%s:%s/setup?setup_token=%s\033[0m\n' "$LANIP" "$PORT" "$TOKEN" else printf ' • From another device: http://:%s/setup?setup_token=%s\n' "$PORT" "$TOKEN" fi command -v xdg-open >/dev/null 2>&1 && xdg-open "$LOCAL" >/dev/null 2>&1 || true else LOCAL="http://localhost:${PORT}" printf 'Open \033[1;36m%s\033[0m and sign in to claim this server to your account.\n' "$LOCAL" [ "$OS" = "Darwin" ] && command -v open >/dev/null 2>&1 && open "$LOCAL" 2>/dev/null || true fi