#!/usr/bin/env bash set -euo pipefail ## # toes installer # Usage: curl -sSL https://toes.space/install | bash # Must be run as the 'toes' user. DEST=~/toes REPO="https://git.nose.space/defunkt/toes" quiet() { "$@" > /dev/null 2>&1; } echo "" echo " ╔══════════════════════════════════╗" echo " ║ toes - personal web appliance ║" echo " ╚══════════════════════════════════╝" echo "" # Must be running as toes if [ "$(whoami)" != "toes" ]; then echo "ERROR: This script must be run as the 'toes' user." echo "Create the user during Raspberry Pi OS setup." exit 1 fi # Must have passwordless sudo (can't prompt when piped from curl) if ! sudo -n true 2>/dev/null; then echo "ERROR: This script requires passwordless sudo." echo "On Raspberry Pi OS, the default user has this already." exit 1 fi # -- Hostname -- CURRENT_HOSTNAME=$(hostname) if [ "$CURRENT_HOSTNAME" != "toes" ]; then echo ">> Setting hostname to 'toes' (was '$CURRENT_HOSTNAME')" sudo hostnamectl set-hostname toes sudo sed -i "s/$CURRENT_HOSTNAME/toes/g" /etc/hosts fi # -- System packages -- echo ">> Updating system packages" quiet sudo apt-get update quiet sudo apt-get install -y git libcap2-bin avahi-utils fish unzip echo ">> Setting fish as default shell" if [ "$(getent passwd toes | cut -d: -f7)" != "/usr/bin/fish" ]; then quiet sudo chsh -s /usr/bin/fish toes fi # -- Bun -- BUN_REAL="$HOME/.bun/bin/bun" BUN_SYMLINK="/usr/local/bin/bun" if [ ! -x "$BUN_REAL" ]; then echo ">> Installing bun" curl -fsSL https://bun.sh/install | bash > /dev/null 2>&1 if [ ! -x "$BUN_REAL" ]; then echo "ERROR: bun installation failed" exit 1 fi fi if [ ! -x "$BUN_SYMLINK" ]; then sudo ln -sf "$BUN_REAL" "$BUN_SYMLINK" fi echo ">> Setting CAP_NET_BIND_SERVICE on bun" sudo setcap 'cap_net_bind_service=+ep' "$BUN_REAL" # -- Clone -- if [ ! -d "$DEST" ]; then echo ">> Cloning toes" git clone "$REPO" "$DEST" else echo ">> Updating toes" cd "$DEST" && git pull origin main fi # -- Directories -- mkdir -p ~/data ~/apps # -- Dependencies & build -- echo ">> Installing dependencies" cd "$DEST" && bun install echo ">> Building client" cd "$DEST" && bun run build # -- Bundled apps -- echo ">> Installing bundled apps" BUNDLED_APPS="clock code cron env stats versions" for app in $BUNDLED_APPS; do if [ -d "$DEST/apps/$app" ]; then echo " $app" cp -r "$DEST/apps/$app" ~/apps/ version_dir=$(ls -1 ~/apps/$app | grep -E '^[0-9]{8}-[0-9]{6}$' | sort -r | head -1) if [ -n "$version_dir" ]; then ln -sfn "$version_dir" ~/apps/$app/current (cd ~/apps/$app/current && bun install --frozen-lockfile) > /dev/null 2>&1 || true fi fi done # -- Systemd -- echo ">> Installing toes service" sudo install -m 644 -o root -g root "$DEST/scripts/toes.service" /etc/systemd/system/toes.service sudo systemctl daemon-reload sudo systemctl enable toes echo ">> Starting toes" sudo systemctl restart toes # -- Kiosk mode -- echo ">> Enabling kiosk mode" sudo raspi-config nonint do_boot_behaviour B4 2>/dev/null || true mkdir -p ~/.config/labwc cat > ~/.config/labwc/autostart <<'EOF' chromium --noerrdialogs --disable-infobars --kiosk http://localhost EOF WAYFIRE_CONFIG="$HOME/.config/wayfire.ini" if [ -f "$WAYFIRE_CONFIG" ]; then sed -i '/^chromium = /d' "$WAYFIRE_CONFIG" if grep -q '^\[autostart\]' "$WAYFIRE_CONFIG"; then sed -i '/^\[autostart\]/a chromium = chromium --noerrdialogs --disable-infobars --kiosk http://localhost' "$WAYFIRE_CONFIG" else cat >> "$WAYFIRE_CONFIG" <<'EOF' [autostart] chromium = chromium --noerrdialogs --disable-infobars --kiosk http://localhost EOF fi fi # -- Done -- echo "" echo " toes is installed! Rebooting in 5 seconds..." echo " After reboot, visit: http://toes.local" echo "" sleep 5 sudo nohup reboot >/dev/null 2>&1 & exit 0