#!/usr/bin/env bash # Build the pinned Timmy worker image (issue #15). # # Every pin is checked before docker is invoked, so an unpinned or floating reference fails here # rather than producing an image nobody can reproduce. set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" RECEIPTS="$REPO_ROOT/deploy/worker/receipts.json" IMAGE="${TIMMY_WORKER_IMAGE:-timmy-vision-worker}" TAG="${TIMMY_WORKER_TAG:-$(date -u +%Y%m%d)}" PUBLISH_HOST="${TIMMY_WORKER_PUBLISH_HOST:-127.0.0.1}" PORT="${TIMMY_MODEL_PORT:-8080}" receipt_field() { python3 - "$RECEIPTS" "$1" <<'PY' import json import sys document = json.load(open(sys.argv[1])) value = document for part in sys.argv[2].split('.'): value = value[int(part)] if part.isdigit() else value[part] print(value) PY } # An explicit value still wins, but an empty one is treated as an invalid pin rather than a default. LLAMA_CPP_COMMIT="${TIMMY_LLAMA_CPP_COMMIT-$(receipt_field llamaCpp.commit)}" MODEL_SHA256="${TIMMY_MODEL_SHA256-$(receipt_field models.0.sha256)}" MMPROJ_SHA256="${TIMMY_MMPROJ_SHA256-$(receipt_field models.1.sha256)}" MODEL_FILE="$(receipt_field models.0.file)" MMPROJ_FILE="$(receipt_field models.1.file)" BUILD_BASE="$(receipt_field baseImages.build.reference)@$(receipt_field baseImages.build.digest)" RUNTIME_BASE="$(receipt_field baseImages.runtime.reference)@$(receipt_field baseImages.runtime.digest)" if [[ ! "$LLAMA_CPP_COMMIT" =~ ^[0-9a-f]{40}$ ]]; then printf 'Refusing to build: the llama.cpp pin must be a 40-hex commit, got %s (floating refs are not reproducible).\n' "${LLAMA_CPP_COMMIT:-}" >&2 exit 64 fi for pin in "$MODEL_SHA256" "$MMPROJ_SHA256"; do if [[ ! "$pin" =~ ^[0-9a-f]{64}$ ]]; then printf 'Refusing to build: every model hash must be a pinned sha256, got %s.\n' "${pin:-}" >&2 exit 64 fi done for base in "$BUILD_BASE" "$RUNTIME_BASE"; do if [[ ! "$base" =~ @sha256:[0-9a-f]{64}$ ]]; then printf 'Refusing to build: base images must be digest-pinned, got %s.\n' "$base" >&2 exit 64 fi done if [[ ! "$PUBLISH_HOST" =~ ^(127\.0\.0\.1|::1|localhost)$ ]]; then printf 'Refusing to publish the worker outside loopback (127.0.0.1): got %s.\n' "$PUBLISH_HOST" >&2 exit 64 fi print_plan() { printf 'image: %s:%s\n' "$IMAGE" "$TAG" printf 'llama.cpp commit: %s\n' "$LLAMA_CPP_COMMIT" printf '%s sha256: %s\n' "$MODEL_FILE" "$MODEL_SHA256" printf '%s sha256: %s\n' "$MMPROJ_FILE" "$MMPROJ_SHA256" printf 'base image (build): %s\n' "$BUILD_BASE" printf 'base image (runtime): %s\n' "$RUNTIME_BASE" printf 'bind address: %s:%s\n' "$PUBLISH_HOST" "$PORT" printf 'publish: %s:%s\n' "$PUBLISH_HOST" "$PORT" printf 'docker build --file deploy/worker/Dockerfile --build-arg LLAMA_CPP_COMMIT=%s --build-arg MODEL_SHA256=%s --build-arg MMPROJ_SHA256=%s --tag %s:%s .\n' \ "$LLAMA_CPP_COMMIT" "$MODEL_SHA256" "$MMPROJ_SHA256" "$IMAGE" "$TAG" } if [[ "${1:-}" == '--print-plan' ]]; then print_plan exit 0 fi print_plan cd "$REPO_ROOT" docker build \ --file deploy/worker/Dockerfile \ --build-arg "LLAMA_CPP_COMMIT=$LLAMA_CPP_COMMIT" \ --build-arg "MODEL_SHA256=$MODEL_SHA256" \ --build-arg "MMPROJ_SHA256=$MMPROJ_SHA256" \ --tag "$IMAGE:$TAG" \ . printf 'Built %s:%s from pinned receipts. Run it with deploy/worker/run_worker_container.sh\n' "$IMAGE" "$TAG"