diff --git a/scripts/raspberry-pi/README.md b/scripts/raspberry-pi/README.md new file mode 100644 index 0000000..0e739f9 --- /dev/null +++ b/scripts/raspberry-pi/README.md @@ -0,0 +1,276 @@ +Here is a `README.md` you can place next to your install script (or in your repo) to document installation, configuration, updates, and debugging. + +--- + +# SoundTouch Service (systemd install) + +This setup installs `soundtouch-service` from the official GitHub release and runs it as a hardened systemd service. + +It supports: + +* Automatic start on boot +* Binding to privileged ports (80 / 443) without running as root +* Config via environment file +* Clean updates +* Safe re-runs of the installer + +--- + +# Installation + +Run the installer script: + +```bash +sudo bash install-soundtouch-service.sh +``` + +You can override defaults: + +```bash +sudo \ + VERSION=v0.17.0 \ + HOSTNAME_FQDN=soundtouch.local \ + HTTP_PORT=80 \ + HTTPS_PORT=443 \ + bash install-soundtouch-service.sh +``` + +--- + +# Configuration + +Configuration lives in: + +``` +/etc/soundtouch-service/soundtouch-service.env +``` + +Example: + +```bash +PORT=80 +HTTPS_PORT=443 +DATA_DIR=/var/lib/soundtouch-service + +LOG_PROXY_BODY=false +REDACT_PROXY_LOGS=true +RECORD_INTERACTIONS=true +DISCOVERY_INTERVAL=5m + +SERVER_URL=http://soundtouch.local +HTTPS_SERVER_URL=https://soundtouch.local +``` + +--- + +# Important: Applying Configuration Changes + +If you change the environment file, you must reload and restart the service. + +Full roundtrip: + +```bash +sudo systemctl daemon-reload +sudo systemctl restart soundtouch-service +``` + +Usually `daemon-reload` is only needed if the **unit file** changed. + +If only the `.env` file changed: + +```bash +sudo systemctl restart soundtouch-service +``` + +--- + +# Service Management + +Check status: + +```bash +systemctl status soundtouch-service +``` + +Enable at boot: + +```bash +sudo systemctl enable soundtouch-service +``` + +Disable: + +```bash +sudo systemctl disable soundtouch-service +``` + +Stop / start manually: + +```bash +sudo systemctl stop soundtouch-service +sudo systemctl start soundtouch-service +``` + +--- + +# Logs & Debugging + +View recent logs: + +```bash +journalctl -u soundtouch-service -e --no-pager +``` + +Follow logs live: + +```bash +journalctl -u soundtouch-service -f +``` + +Show logs from current boot: + +```bash +journalctl -u soundtouch-service -b +``` + +If the service fails to start: + +```bash +systemctl status soundtouch-service --no-pager +``` + +Look for: + +* `bind: permission denied` → capability issue +* `address already in use` → port conflict +* permission errors in DATA_DIR → ownership issue + +--- + +# Port Conflicts + +Check if 80/443 are in use: + +```bash +sudo ss -tulpn | grep -E ':80|:443' +``` + +If another service is using the port, either: + +* stop/disable that service +* or change `PORT` / `HTTPS_PORT` in the env file + +Then restart the service. + +--- + +# Updating to a New Version + +To upgrade: + +```bash +sudo VERSION=vX.Y.Z bash install-soundtouch-service.sh +``` + +The script will: + +* Download the new binary +* Overwrite the old one +* Restart the service + +No need to reconfigure anything. + +--- + +# Reinstall / Reset + +To fully reset: + +```bash +sudo systemctl stop soundtouch-service +sudo rm -rf /var/lib/soundtouch-service/* +sudo systemctl start soundtouch-service +``` + +To completely remove: + +```bash +sudo systemctl disable --now soundtouch-service +sudo rm /etc/systemd/system/soundtouch-service.service +sudo rm -rf /etc/soundtouch-service +sudo rm -rf /var/lib/soundtouch-service +sudo rm /usr/local/bin/soundtouch-service +sudo systemctl daemon-reload +``` + +--- + +# Architecture Auto-Detection + +The installer auto-detects: + +* `linux-armv7` +* `linux-arm64` +* `linux-amd64` + +Override manually if needed: + +```bash +sudo ARCH_ASSET=linux-arm64 bash install-soundtouch-service.sh +``` + +--- + +# Security Notes + +The service: + +* Runs as a dedicated `soundtouch` system user +* Uses `AmbientCapabilities=CAP_NET_BIND_SERVICE` +* Does not require `setcap` +* Does not run as root +* Uses systemd sandboxing (`ProtectSystem`, `PrivateTmp`, etc.) + +--- + +# Quick Troubleshooting Checklist + +If something does not work: + +1. Check status: + + ``` + systemctl status soundtouch-service + ``` + +2. Check logs: + + ``` + journalctl -u soundtouch-service -e + ``` + +3. Confirm ports: + + ``` + ss -tulpn | grep -E ':80|:443' + ``` + +4. Confirm env file: + + ``` + cat /etc/soundtouch-service/soundtouch-service.env + ``` + +5. Restart cleanly: + + ``` + sudo systemctl restart soundtouch-service + ``` + +--- + +If you’d like, I can also provide: + +* A `make update` style wrapper +* A rollback mechanism +* Or a self-update script with checksum verification diff --git a/scripts/raspberry-pi/install.sh b/scripts/raspberry-pi/install.sh new file mode 100644 index 0000000..1486bf6 --- /dev/null +++ b/scripts/raspberry-pi/install.sh @@ -0,0 +1,248 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ============================================================================== +# Bose-SoundTouch soundtouch-service installer (systemd, headless) +# +# Example usage (override defaults via env vars): +# +# sudo \ +# VERSION=v0.17.0 \ +# HOSTNAME_FQDN=soundtouch.local \ +# HTTP_PORT=80 \ +# HTTPS_PORT=443 \ +# DATA_DIR=/var/lib/soundtouch-service \ +# LOG_PROXY_BODY=false \ +# REDACT_PROXY_LOGS=true \ +# RECORD_INTERACTIONS=true \ +# DISCOVERY_INTERVAL=5m \ +# bash install-soundtouch-service.sh +# +# Notes: +# - This script downloads a release binary for your CPU (auto-detects armv7/arm64/amd64). +# - It installs a systemd unit that can bind privileged ports (80/443) using: +# AmbientCapabilities=CAP_NET_BIND_SERVICE +# so you do NOT need setcap and do NOT need to run as root. +# - Safe to re-run; it will update binary/config/unit and restart the service. +# ============================================================================== + +VERSION="${VERSION:-v0.17.0}" +SERVICE_NAME="${SERVICE_NAME:-soundtouch-service}" +BIN_PATH="${BIN_PATH:-/usr/local/bin/soundtouch-service}" + +CONFIG_DIR="${CONFIG_DIR:-/etc/soundtouch-service}" +ENV_FILE="${ENV_FILE:-$CONFIG_DIR/soundtouch-service.env}" +DATA_DIR="${DATA_DIR:-/var/lib/soundtouch-service}" + +SERVICE_USER="${SERVICE_USER:-soundtouch}" +SERVICE_GROUP="${SERVICE_GROUP:-soundtouch}" + +# Ports +HTTP_PORT="${HTTP_PORT:-80}" +HTTPS_PORT="${HTTPS_PORT:-443}" + +# URLs (default uses current hostname + .local) +HOSTNAME_FQDN="${HOSTNAME_FQDN:-$(hostname).local}" +SERVER_URL="${SERVER_URL:-http://${HOSTNAME_FQDN}}" +HTTPS_SERVER_URL="${HTTPS_SERVER_URL:-https://${HOSTNAME_FQDN}}" + +# Additional env vars (mirrors the project's docker-compose.yml) +LOG_PROXY_BODY="${LOG_PROXY_BODY:-false}" +REDACT_PROXY_LOGS="${REDACT_PROXY_LOGS:-true}" +RECORD_INTERACTIONS="${RECORD_INTERACTIONS:-true}" +DISCOVERY_INTERVAL="${DISCOVERY_INTERVAL:-5m}" + +# Override if you want to force a specific asset suffix: +# ARCH_ASSET=linux-armv7|linux-arm64|linux-amd64 +ARCH_ASSET="${ARCH_ASSET:-}" + +log() { printf "\n==> %s\n" "$*"; } +die() { echo "ERROR: $*" >&2; exit 1; } + +need_root() { + [[ "${EUID}" -eq 0 ]] || die "Please run as root (e.g. sudo bash $0)." +} + +ensure_cmd() { + command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1" +} + +apt_install_if_missing() { + log "Installing dependencies: $*" + apt-get update -y + apt-get install -y --no-install-recommends "$@" +} + +detect_arch_asset() { + # Upstream release naming expects: linux-armv7, linux-arm64, linux-amd64 + # Map uname -m to those. + local m + m="$(uname -m)" + + case "$m" in + armv7l|armv6l) + echo "linux-armv7" + ;; + aarch64) + echo "linux-arm64" + ;; + x86_64|amd64) + echo "linux-amd64" + ;; + *) + die "Unsupported architecture from uname -m: $m (set ARCH_ASSET manually)" + ;; + esac +} + +download_url_for() { + local asset="$1" + # Release asset pattern used by you earlier: + # soundtouch-service-v0.17.0-linux-armv7 + echo "https://github.com/gesellix/Bose-SoundTouch/releases/download/${VERSION}/soundtouch-service-${VERSION}-${asset}" +} + +ensure_user_group() { + log "Ensuring service user/group exist: ${SERVICE_USER}:${SERVICE_GROUP}" + if ! getent group "${SERVICE_GROUP}" >/dev/null; then + groupadd --system "${SERVICE_GROUP}" + fi + if ! id -u "${SERVICE_USER}" >/dev/null 2>&1; then + useradd --system \ + --home "${DATA_DIR}" \ + --create-home \ + --shell /usr/sbin/nologin \ + --gid "${SERVICE_GROUP}" \ + "${SERVICE_USER}" + fi +} + +ensure_dirs() { + log "Creating directories" + mkdir -p "${CONFIG_DIR}" "${DATA_DIR}" + chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "${DATA_DIR}" + chmod 0755 "${CONFIG_DIR}" "${DATA_DIR}" +} + +download_binary() { + local asset url tmp + asset="${ARCH_ASSET:-$(detect_arch_asset)}" + url="$(download_url_for "$asset")" + + log "Downloading binary for ${asset}: ${url}" + tmp="$(mktemp -d)" + trap 'rm -rf "${tmp}"' EXIT + + if command -v curl >/dev/null 2>&1; then + curl -fsSL -o "${tmp}/soundtouch-service" "${url}" + else + wget -O "${tmp}/soundtouch-service" "${url}" + fi + + chmod +x "${tmp}/soundtouch-service" + install -m 0755 "${tmp}/soundtouch-service" "${BIN_PATH}" + log "Installed binary to ${BIN_PATH}" +} + +write_env_file() { + log "Writing env file: ${ENV_FILE}" + cat > "${ENV_FILE}" < "/etc/systemd/system/${SERVICE_NAME}.service" </ + https:/// + +Logs: + journalctl -u ${SERVICE_NAME}.service -e --no-pager +EOF +} + +main() { + need_root + ensure_cmd systemctl + ensure_cmd ss + + if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then + apt_install_if_missing curl + fi + + ensure_user_group + ensure_dirs + download_binary + write_env_file + write_systemd_unit + reload_enable_start + show_status +} + +main "$@"