diff --git a/install.sh b/install.sh index a6bb943..8559fd0 100755 --- a/install.sh +++ b/install.sh @@ -226,6 +226,10 @@ download_one() { local relative=$1 url=$2 expected=$3 target="$MODEL_DIR/$1" install -d -m 0755 "$(dirname "$target")" if [[ -f $target ]] && printf '%s %s\n' "$expected" "$target" | sha256sum -c - >/dev/null 2>&1; then + # The installer itself runs with umask 077, but inference runs deliberately + # unprivileged. Models are immutable inputs: globally readable, never + # writable by the runtime container. + chmod 0444 "$target" printf 'Vorhanden und geprüft: %s\n' "$relative" return fi @@ -234,6 +238,7 @@ download_one() { --output "$target.partial" "$url" printf '%s %s\n' "$expected" "$target.partial" | sha256sum -c - mv "$target.partial" "$target" + chmod 0444 "$target" } download_models() { @@ -333,7 +338,8 @@ build_and_start() { # Use docker exec directly here. Some Compose/Docker combinations return a # transient HTTP 409 while upgrading the exec stream immediately after a # freshly built service has been recreated. - docker exec -i mike-ai-router python - <<'PY' + local activation_output + activation_output=$(docker exec -i mike-ai-router python - <<'PY' import json, os, time, urllib.request key = os.environ["ROUTER_API_KEY"] request = urllib.request.Request( @@ -347,6 +353,7 @@ while time.monotonic() < deadline: with urllib.request.urlopen("http://127.0.0.1:8081/ready", timeout=5) as response: if response.status == 200: print("Router und Fast-Profil sind bereit.") + print("INSTALL_READINESS_OK") break except Exception: pass @@ -354,6 +361,10 @@ while time.monotonic() < deadline: else: raise SystemExit("Readiness-Check fehlgeschlagen") PY + ) || die "Fast-Profil konnte nicht aktiviert werden" + printf '%s\n' "$activation_output" + grep -Fxq 'INSTALL_READINESS_OK' <<<"$activation_output" || \ + die "Fast-Profil lieferte keinen bestätigten Readiness-Marker" } hostnamectl set-hostname "$AI_HOSTNAME"