#!/bin/sh # Sleevecast Dock installer for Linux and macOS. # curl -fsSL https://get.sleevecast.com/dock.sh | sh # It checks this machine and installs or starts what the Dock needs: Docker Engine 26+ with Compose v2 on Linux, # Docker Desktop on macOS. Then it installs the Dock in ~/.sleevecast/dock and enrolls it (a code to approve in the # browser). It asks once before installing anything. Run it again any time: it only does what is missing, and an # enrolled Dock is updated in place with its enrollment, keys and sleeves kept. # ... | sh -s -- --check only report what is there and what is missing; change nothing # ... | sh -s -- --yes install what is missing without asking set -eu DOCK_IMAGE='ghcr.io/blackcrowsoftware/sleevecast-dock@sha256:b25e3eebc0dc739fce05375279ce92c31a392ebcc9aa1630e125a4ce3d13b70d' PROXY_IMAGE='tecnativa/docker-socket-proxy:v0.5.0@sha256:1f5038b54f06c3e18422902cf00ba21803d1c97805aae032e5e6673d532d3459' MIN_DOCKER=26 API=https://api.sleevecast.com CONSOLE=https://app.sleevecast.com CHECK=0; YES=0 for a in "$@"; do case "$a" in --check) CHECK=1 ;; --yes|-y) YES=1 ;; -h|--help) echo "Usage: curl -fsSL https://get.sleevecast.com/dock.sh | sh [-s -- --check | --yes]"; exit 0 ;; *) echo "Unknown option: $a (use --check or --yes)" >&2; exit 2 ;; esac done if [ -t 1 ]; then B=$(printf '\033[1m'); G=$(printf '\033[32m'); Y=$(printf '\033[33m'); R=$(printf '\033[31m'); N=$(printf '\033[0m') else B=''; G=''; Y=''; R=''; N=''; fi ok() { printf ' %s[ ok ]%s %s\n' "$G" "$N" "$*"; } info() { printf ' %s\n' "$*"; } warn() { printf ' %s[warn]%s %s\n' "$Y" "$N" "$*"; } need() { printf ' %s[need]%s %s\n' "$Y" "$N" "$1"; ACTIONS="$ACTIONS $2"; PLAN="$PLAN - $3"; } fail() { printf ' %s[fail]%s %s\n' "$R" "$N" "$*"; FAILED=1; } step() { printf '\n%s%s%s\n' "$B" "$*" "$N"; } die() { printf '\n%s%s%s\n' "$R" "$*" "$N" >&2; exit 1; } have() { command -v "$1" >/dev/null 2>&1; } wants() { case " $ACTIONS " in *" $1 "*) return 0 ;; *) return 1 ;; esac; } fetch() { if have curl; then curl -fsSL "$@"; else wget -qO- "$1"; fi; } tty_ok() { (exec < /dev/tty) 2>/dev/null; } ACTIONS=''; PLAN=''; FAILED=0 DOCKER=docker if [ "$(id -u)" -eq 0 ]; then SUDO=''; else SUDO='sudo'; fi ME=$(id -un) printf '%sSleevecast Dock installer%s\n' "$B" "$N" # ---------------------------------------------------------------- checks step 'Checking this machine' case "$(uname -s)" in Linux) PLATFORM=linux ;; Darwin) PLATFORM=mac ;; *) die 'This installer is for Linux and macOS. On Windows, run in PowerShell: irm https://get.sleevecast.com/dock.ps1 | iex' ;; esac case "$(uname -m)" in x86_64|amd64) ARCH=amd64 ;; aarch64|arm64) ARCH=arm64 ;; *) die "This processor ($(uname -m)) is not supported; the Dock runs on 64-bit Intel/AMD and ARM." ;; esac if [ "$PLATFORM" = linux ]; then DISTRO=''; DISTRO_LIKE=''; CODENAME=''; PRETTY='Linux' if [ -r /etc/os-release ]; then DISTRO=$(. /etc/os-release; echo "${ID:-}") DISTRO_LIKE=$(. /etc/os-release; echo "${ID_LIKE:-}") CODENAME=$(. /etc/os-release; echo "${UBUNTU_CODENAME:-${VERSION_CODENAME:-}}") PRETTY=$(. /etc/os-release; echo "${PRETTY_NAME:-Linux}") fi case " $DISTRO $DISTRO_LIKE " in *" ubuntu "*) FAMILY=ubuntu ;; *" debian "*|*" raspbian "*) FAMILY=debian ;; *" rhel "*|*" centos "*) [ "$DISTRO" = rhel ] && FAMILY=rhel || FAMILY=centos ;; *" fedora "*) [ "$DISTRO" = fedora ] && FAMILY=fedora || FAMILY=centos ;; *" arch "*) FAMILY=arch ;; *" alpine "*) FAMILY=alpine ;; *" suse "*|*" opensuse "*|*" sles "*) FAMILY=suse ;; *) FAMILY=other ;; esac [ "$DISTRO" = amzn ] && FAMILY=other ok "$PRETTY ($ARCH)" WSL=0; grep -qi microsoft /proc/sys/kernel/osrelease 2>/dev/null && WSL=1 [ $WSL = 1 ] && info 'This is Linux inside WSL. On Windows the PowerShell installer (with Docker Desktop) is usually simpler.' mem_kb=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo 2>/dev/null || echo 0) mem_gb=$((mem_kb / 1048576)) if [ "$mem_kb" -ge 3800000 ]; then ok "${mem_gb} GB memory"; else warn "${mem_gb} GB memory; sleeves want about 4 GB free"; fi ddir=/var/lib/docker; [ -d $ddir ] || ddir=/var/lib; [ -d $ddir ] || ddir=/ free_gb=$(df -Pk "$ddir" 2>/dev/null | awk 'NR==2 {print int($4/1048576)}') if [ "${free_gb:-0}" -ge 10 ]; then ok "${free_gb} GB free disk"; else warn "${free_gb:-?} GB free disk; images and memory want about 10 GB"; fi else macos=$(sw_vers -productVersion 2>/dev/null || echo '?') ok "macOS $macos ($ARCH)" mem_gb=$(( $(sysctl -n hw.memsize 2>/dev/null || echo 0) / 1073741824 )) if [ "$mem_gb" -ge 8 ]; then ok "${mem_gb} GB memory"; else warn "${mem_gb} GB memory; Docker Desktop plus sleeves want 8 GB or more"; fi fi if [ "$PLATFORM" = linux ] && [ -n "$SUDO" ] && ! have sudo; then fail 'You are not root and sudo is not installed; run this as root or install sudo.' fi if ! have curl && ! have wget; then fail 'Neither curl nor wget is installed.'; fi if fetch "$API/healthz" >/dev/null 2>&1; then ok 'Sleevecast is reachable (outbound HTTPS)' else fail "Cannot reach $API over HTTPS. The Dock needs outbound port 443 to *.sleevecast.com (check a proxy or firewall)."; fi # Docker: is it installed, recent, running, and can this user use it? server_version() { $1 version --format '{{.Server.Version}}' 2>/dev/null; } major_of() { echo "$1" | sed 's/^[^0-9]*//' | cut -d. -f1; } DOCKER_STATE=missing; SERVER='' if [ "$PLATFORM" = mac ]; then APP='' for d in /Applications/Docker.app "$HOME/Applications/Docker.app"; do [ -d "$d" ] && APP=$d && break; done [ -n "$APP" ] && ! have docker && PATH="$PATH:$APP/Contents/Resources/bin" && export PATH if [ -z "$APP" ]; then need 'Docker Desktop is not installed' mac_install "Install Docker Desktop for Mac (downloaded from docker.com; asks for your password once)" need 'Docker Desktop is not running' mac_start 'Start Docker Desktop and wait for its engine' elif SERVER=$(server_version docker); then DOCKER_STATE=running if [ "$(major_of "$SERVER")" -lt $MIN_DOCKER ] 2>/dev/null; then fail "Docker Desktop's engine is $SERVER; the Dock needs $MIN_DOCKER+. Update Docker Desktop (Check for updates in its menu)." else ok "Docker Desktop is running (Docker $SERVER)"; fi else need 'Docker Desktop is installed but not running' mac_start 'Start Docker Desktop and wait for its engine'; DOCKER_STATE=stopped; fi else if ! have docker && ! have dockerd; then case "$FAMILY" in other) fail "Docker is not installed, and this installer cannot install it on $PRETTY. Install Docker Engine $MIN_DOCKER+ (https://docs.docker.com/engine/install/) and run this again." ;; *) need 'Docker Engine is not installed' docker_install "Install Docker Engine with Compose (Docker's official packages for $PRETTY)" ;; esac else if SERVER=$(server_version docker); then DOCKER_STATE=running elif [ -n "$SUDO" ] && SERVER=$(sudo -n docker version --format '{{.Server.Version}}' 2>/dev/null); then DOCKER_STATE=root-only elif docker version 2>&1 | grep -qi 'permission denied'; then DOCKER_STATE=root-only-unknown else DOCKER_STATE=stopped; fi if [ -n "$SERVER" ] && [ "$(major_of "$SERVER")" -lt $MIN_DOCKER ] 2>/dev/null; then if [ "$FAMILY" = other ]; then fail "Docker $SERVER is too old (the Dock needs $MIN_DOCKER+). Update it from https://docs.docker.com/engine/install/ and run this again." else need "Docker $SERVER is too old (the Dock needs $MIN_DOCKER or newer)" docker_install "Replace Docker $SERVER with current Docker Engine from Docker's packages (images, containers and volumes are kept)"; fi elif [ "$DOCKER_STATE" = running ] || [ "$DOCKER_STATE" = root-only ]; then ok "Docker $SERVER is running" elif [ "$DOCKER_STATE" = root-only-unknown ]; then info 'Docker is installed; its version is checked once sudo is available.' fi if [ "$DOCKER_STATE" = stopped ] && ! wants docker_install; then need 'Docker is installed but not running' docker_start 'Start the Docker service and enable it at boot'; fi fi if [ "$(id -u)" -ne 0 ] && { [ "$DOCKER_STATE" = root-only ] || [ "$DOCKER_STATE" = root-only-unknown ] || wants docker_install || wants docker_start; }; then if id -nG "$ME" 2>/dev/null | tr ' ' '\n' | grep -qx docker && [ "$DOCKER_STATE" != root-only ]; then : else need "Your user ($ME) cannot use Docker without sudo" docker_group "Add $ME to the docker group (takes effect at your next login; this run uses sudo)"; fi fi fi if [ "$DOCKER_STATE" = running ] || [ "$DOCKER_STATE" = root-only ]; then [ "$DOCKER_STATE" = root-only ] && DOCKER="sudo docker" if [ "$PLATFORM" = linux ] && [ "$(major_of "$SERVER")" -ge $MIN_DOCKER ] 2>/dev/null; then if $DOCKER compose version >/dev/null 2>&1; then ok "Docker Compose $($DOCKER compose version --short 2>/dev/null)" else need 'Docker Compose v2 is missing' compose_install 'Install the Docker Compose v2 plugin'; fi fi os_type=$($DOCKER info --format '{{.OSType}}' 2>/dev/null || echo linux) [ "$os_type" = linux ] || fail "Docker runs $os_type containers; the Dock needs Linux containers." fi # Sleeves run amd64 images today; ARM Linux runs them through QEMU emulation. if [ "$PLATFORM" = linux ] && [ "$ARCH" = arm64 ]; then if [ -e /proc/sys/fs/binfmt_misc/qemu-x86_64 ]; then ok 'x86-64 emulation for sleeves (QEMU)' else case "$FAMILY" in ubuntu|debian|fedora|centos|rhel|arch|suse) need 'x86-64 emulation for sleeves is missing' qemu_install 'Install QEMU user emulation (sleeve images are x86-64 for now)' ;; *) warn 'Sleeve images are x86-64 for now; install QEMU user emulation (binfmt) so they can run here' ;; esac; fi fi DIR="$HOME/.sleevecast/dock" if [ -f "$DIR/compose.yaml" ]; then ok "Dock folder $DIR exists (this run updates it)"; else info "The Dock will be installed in $DIR"; fi if [ "$FAILED" = 1 ]; then die 'Fix the [fail] items above, then run the installer again.'; fi if [ "$CHECK" = 1 ]; then if [ -z "$ACTIONS" ]; then printf '\n%sEverything the Dock needs is here.%s Run the installer without --check to install it.\n' "$G" "$N" else printf '\nThe installer would:%s\n - Install and enroll the Dock\n' "$PLAN"; fi exit 0 fi # ---------------------------------------------------------------- install what is missing if [ -n "$ACTIONS" ]; then printf '\nTo finish, the installer will:%s\n - Install and enroll the Dock\n' "$PLAN" if [ "$YES" != 1 ]; then tty_ok || die 'Run with --yes to install without a prompt: curl -fsSL https://get.sleevecast.com/dock.sh | sh -s -- --yes' printf '\nContinue? [Y/n] ' read -r answer < /dev/tty || answer=n case "$answer" in ''|y|Y|yes|YES) ;; *) echo 'Nothing was changed.'; exit 0 ;; esac fi if [ "$PLATFORM" = linux ] && [ -n "$SUDO" ]; then echo 'Some steps need administrator rights (sudo).' if tty_ok; then sudo -v < /dev/tty; else sudo -n true || die 'sudo needs a password, but there is no terminal to ask for it.'; fi fi # Docker refused this user, so its version and Compose could only be checked now, with sudo. if [ "$DOCKER_STATE" = root-only-unknown ]; then DOCKER="sudo docker" if SERVER=$(server_version "$DOCKER"); then [ "$(major_of "$SERVER")" -ge $MIN_DOCKER ] 2>/dev/null || die "Docker $SERVER is older than $MIN_DOCKER. Run the installer again: it can now see this and offers to replace it." $DOCKER compose version >/dev/null 2>&1 || ACTIONS="$ACTIONS compose_install" else ACTIONS="$ACTIONS docker_start"; fi fi fi remove_pkgs_apt() { old='' for p in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc moby-engine moby-cli moby-buildx moby-compose moby-containerd moby-runc; do dpkg -s "$p" >/dev/null 2>&1 && old="$old $p" done if [ -n "$old" ]; then stop_old_docker echo " removing the distribution's packages:$old"; $SUDO apt-get remove -y -qq $old >/dev/null fi } # Stop the old daemon and its containerd before their packages go, so the new ones start cleanly. stop_old_docker() { if have systemctl && [ -d /run/systemd/system ]; then $SUDO systemctl stop docker.socket docker.service containerd.service >/dev/null 2>&1 || true elif have rc-service; then $SUDO rc-service docker stop >/dev/null 2>&1 || true elif have service; then $SUDO service docker stop >/dev/null 2>&1 || true; fi } use_docker_source() { echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/$FAMILY $1 stable" | $SUDO tee /etc/apt/sources.list.d/docker.list >/dev/null } # The major version apt would install for docker-ce ("5:27.3.1-1~ubuntu.24.04~noble" gives 27; nothing gives 0). apt_docker_major() { v=$(apt-cache policy docker-ce 2>/dev/null | awk '/Candidate:/ {print $2}') v=${v#*:}; v=${v%%.*} case "$v" in ''|*[!0-9]*) echo 0 ;; *) echo "$v" ;; esac } # The LTS release whose Docker packages fit this machine: jammy or noble for Ubuntu, bookworm for Debian. lts_codename() { ver=$(. /etc/os-release; echo "${VERSION_ID:-0}") case "$FAMILY" in ubuntu) if [ "${ver%%.*}" -ge 24 ] 2>/dev/null; then echo noble; else echo jammy; fi ;; debian) echo bookworm ;; esac } install_docker() { step 'Installing Docker Engine' case "$FAMILY" in ubuntu|debian) export DEBIAN_FRONTEND=noninteractive remove_pkgs_apt $SUDO apt-get update -qq $SUDO apt-get install -y -qq ca-certificates curl >/dev/null $SUDO install -m 0755 -d /etc/apt/keyrings $SUDO curl -fsSL "https://download.docker.com/linux/$FAMILY/gpg" -o /etc/apt/keyrings/docker.asc $SUDO chmod a+r /etc/apt/keyrings/docker.asc [ -n "$CODENAME" ] || die 'Cannot tell this release name (VERSION_CODENAME) for the Docker package source.' use_docker_source "$CODENAME" # Docker stops publishing for a release when it reaches end of life (Ubuntu 23.04 stays at Docker 25), and a # brand-new release may have no packages yet. Either way, take the packages of the nearest LTS release. if ! $SUDO apt-get update -qq 2>/dev/null || [ "$(apt_docker_major)" -lt $MIN_DOCKER ] 2>/dev/null; then lts=$(lts_codename) if [ -n "$lts" ] && [ "$lts" != "$CODENAME" ]; then echo " Docker's packages for $PRETTY ($CODENAME) are missing or older than $MIN_DOCKER; using those for $lts" use_docker_source "$lts" $SUDO apt-get update -qq || die "Docker's package source for $lts could not be read; see https://docs.docker.com/engine/install/" fi fi [ "$(apt_docker_major)" -ge $MIN_DOCKER ] 2>/dev/null || die "Docker's packages for $PRETTY are older than $MIN_DOCKER. Install Docker Engine $MIN_DOCKER+ (https://docs.docker.com/engine/install/) and run this again." $SUDO apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin >/dev/null ;; fedora|centos|rhel) old='' for p in docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine podman-docker moby-engine moby-cli; do rpm -q "$p" >/dev/null 2>&1 && old="$old $p" done if [ -n "$old" ]; then stop_old_docker; echo " removing the distribution's packages:$old"; $SUDO dnf remove -y -q $old >/dev/null; fi $SUDO curl -fsSL "https://download.docker.com/linux/$FAMILY/docker-ce.repo" -o /etc/yum.repos.d/docker-ce.repo $SUDO dnf install -y -q --allowerasing docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin ;; arch) $SUDO pacman -Sy --noconfirm --needed docker docker-compose ;; alpine) $SUDO apk add --no-cache docker docker-cli-compose ;; suse) $SUDO zypper --non-interactive install docker docker-compose ;; esac } install_compose() { step 'Installing Docker Compose' case "$ARCH" in amd64) a=x86_64 ;; arm64) a=aarch64 ;; esac url="https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$a" tmp=$(mktemp -d) fetch "$url" > "$tmp/docker-compose" want=$(fetch "$url.sha256" | awk '{print $1}') got=$( (sha256sum "$tmp/docker-compose" 2>/dev/null || shasum -a 256 "$tmp/docker-compose") | awk '{print $1}') [ -n "$want" ] && [ "$want" = "$got" ] || { rm -rf "$tmp"; die 'The Docker Compose download did not match its checksum; nothing was installed.'; } $SUDO install -d -m 0755 /usr/local/lib/docker/cli-plugins $SUDO install -m 0755 "$tmp/docker-compose" /usr/local/lib/docker/cli-plugins/docker-compose rm -rf "$tmp" } start_docker() { step 'Starting Docker' # restart, not start: after replacing an old Docker, its daemon can still be the one running. if have systemctl && [ -d /run/systemd/system ]; then $SUDO systemctl enable docker >/dev/null 2>&1 || true if ! $SUDO systemctl restart docker; then $SUDO journalctl -u docker -u containerd -n 25 --no-pager 2>/dev/null | sed 's/^/ /' || true die 'The Docker service did not start (its log is above). Fix that, then run the installer again.' fi elif have rc-service; then $SUDO rc-update add docker default >/dev/null 2>&1 || true; $SUDO rc-service docker restart elif have service; then $SUDO service docker restart else die 'Cannot start the Docker service here (no systemd, OpenRC or service). Start dockerd, then run this again.'; fi } add_group() { grep -q '^docker:' /etc/group || { have groupadd && $SUDO groupadd docker || $SUDO addgroup docker; } if have usermod; then $SUDO usermod -aG docker "$ME"; else $SUDO addgroup "$ME" docker; fi echo " added $ME to the docker group; it applies from your next login (this run uses sudo for Docker)" } install_qemu() { step 'Installing x86-64 emulation (QEMU)' case "$FAMILY" in ubuntu|debian) $SUDO apt-get install -y -qq qemu-user-static binfmt-support >/dev/null ;; fedora|centos|rhel) $SUDO dnf install -y -q qemu-user-static ;; arch) $SUDO pacman -Sy --noconfirm --needed qemu-user-static qemu-user-static-binfmt ;; suse) $SUDO zypper --non-interactive install qemu-linux-user ;; esac [ -e /proc/sys/fs/binfmt_misc/qemu-x86_64 ] || warn 'Emulation is installed but not active yet; it will be after a restart.' } install_mac() { step 'Installing Docker Desktop' tmp=$(mktemp -d) echo ' downloading Docker Desktop (about 600 MB)...' curl -fL --progress-bar -o "$tmp/Docker.dmg" "https://desktop.docker.com/mac/main/$ARCH/Docker.dmg" hdiutil attach -nobrowse -quiet -mountpoint "$tmp/mnt" "$tmp/Docker.dmg" echo ' installing (macOS asks for your password)...' if tty_ok; then sudo "$tmp/mnt/Docker.app/Contents/MacOS/install" --accept-license --user="$ME" < /dev/tty else sudo -n "$tmp/mnt/Docker.app/Contents/MacOS/install" --accept-license --user="$ME"; fi || { hdiutil detach -quiet "$tmp/mnt"; rm -rf "$tmp"; die 'Docker Desktop did not install.'; } hdiutil detach -quiet "$tmp/mnt" || true rm -rf "$tmp" APP=/Applications/Docker.app PATH="$PATH:$APP/Contents/Resources/bin"; export PATH } wait_for_docker() { i=0 while [ $i -lt "$1" ]; do if SERVER=$(server_version "$DOCKER"); then return 0; fi sleep 3; i=$((i + 3)) done return 1 } wants docker_install && install_docker wants compose_install && install_compose if [ "$PLATFORM" = linux ] && { wants docker_install || wants docker_start; }; then start_docker; fi wants docker_group && add_group wants qemu_install && install_qemu wants mac_install && install_mac if wants mac_start; then step 'Starting Docker Desktop' open -g -a "${APP:-Docker}" echo ' waiting for the Docker engine (the first start takes a minute or two)...' fi # Whatever happened above, from here on Docker must answer. if [ "$PLATFORM" = linux ] && [ "$(id -u)" -ne 0 ] && ! server_version docker >/dev/null; then DOCKER="sudo docker"; fi wait_for_docker 180 || die 'Docker did not start. Start it (Docker Desktop on macOS; "sudo systemctl start docker" on Linux) and run this again.' [ "$(major_of "$SERVER")" -ge $MIN_DOCKER ] 2>/dev/null || die "Docker $SERVER is still older than $MIN_DOCKER; update it and run this again." $DOCKER compose version >/dev/null 2>&1 || die 'Docker Compose v2 is still missing; install the docker-compose-plugin package and run this again.' [ -n "$ACTIONS" ] && ok "Docker $SERVER with Compose $($DOCKER compose version --short 2>/dev/null) is ready" [ -n "${SLEEVECAST_PREREQS_ONLY:-}" ] && { echo 'Prerequisites done (SLEEVECAST_PREREQS_ONLY is set; the Dock was not installed).'; exit 0; } # ---------------------------------------------------------------- the Dock step 'Installing the Dock' mkdir -p "$DIR" name=$(hostname | tr 'A-Z' 'a-z') os=$(uname -s | tr 'A-Z' 'a-z') cat > "$DIR/compose.yaml.tmp" <<'COMPOSE' # Sleevecast Dock, written by the installer. The Dock creates its leaf node and sleeves itself (labelled # sleevecast.dock). Docker access goes only through the socket proxy, on a network nothing else joins. name: sleevecast-dock services: socket-proxy: image: tecnativa/docker-socket-proxy:v0.5.0@sha256:1f5038b54f06c3e18422902cf00ba21803d1c97805aae032e5e6673d532d3459 restart: unless-stopped environment: CONTAINERS: "1" IMAGES: "1" NETWORKS: "1" VOLUMES: "1" INFO: "1" VERSION: "1" POST: "1" ALLOW_START: "1" ALLOW_STOP: "1" ALLOW_RESTARTS: "1" EXEC: "0" BUILD: "0" SWARM: "0" SECRETS: "0" PLUGINS: "0" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro networks: [ctl] read_only: true tmpfs: [/run, /tmp] # the proxy renders its haproxy config into /tmp at start dock: image: ghcr.io/blackcrowsoftware/sleevecast-dock@sha256:b25e3eebc0dc739fce05375279ce92c31a392ebcc9aa1630e125a4ce3d13b70d restart: unless-stopped depends_on: [socket-proxy] environment: DOCKER_HOST: tcp://socket-proxy:2375 DOCK_NAME: "__DOCK_NAME__" DOCK_HOST_OS: "__HOST_OS__" volumes: - data:/data networks: [ctl, bus] read_only: true tmpfs: [/tmp] cap_drop: [ALL] security_opt: ["no-new-privileges:true"] networks: ctl: name: sleevecast-ctl internal: true bus: name: sleevecast-bus volumes: data: name: sleevecast-dock-data COMPOSE sed -e "s|ghcr.io/blackcrowsoftware/sleevecast-dock@sha256:b25e3eebc0dc739fce05375279ce92c31a392ebcc9aa1630e125a4ce3d13b70d|$DOCK_IMAGE|" -e "s|tecnativa/docker-socket-proxy:v0.5.0@sha256:1f5038b54f06c3e18422902cf00ba21803d1c97805aae032e5e6673d532d3459|$PROXY_IMAGE|" -e "s|__DOCK_NAME__|$name|" -e "s|__HOST_OS__|$os|" \ "$DIR/compose.yaml.tmp" > "$DIR/compose.yaml" rm -f "$DIR/compose.yaml.tmp" cd "$DIR" $DOCKER compose pull --quiet $DOCKER compose up -d --remove-orphans if $DOCKER compose exec -T dock /app/dock status >/dev/null 2>&1; then ok 'This Dock is already enrolled; it was updated in place.' else step 'Enrolling the Dock' # curl | sh gives the script itself as stdin; take the terminal for the enrollment prompts. if tty_ok; then $DOCKER compose exec dock /app/dock enroll --name "$name" < /dev/tty else $DOCKER compose exec -T dock /app/dock enroll --name "$name"; fi fi dc='docker compose'; [ "$DOCKER" = docker ] || dc='sudo docker compose' printf '\n%sDone.%s Next: open %s/welcome to add your model key and start your first sleeve.\n\n' "$G" "$N" "$CONSOLE" echo "Dock folder: $DIR" echo "Status: $dc exec dock /app/dock status (run in that folder)" echo "Model key: $dc exec dock /app/dock keys set anthropic-key (or add it on the Keys page)" echo "Uninstall: $dc exec dock /app/dock uninstall; $dc down" if wants docker_group; then echo "Docker without sudo works after you log out and back in (or run: newgrp docker)."; fi