From 68eba913c39ecddcefe13ad44b80579802115c33 Mon Sep 17 00:00:00 2001 From: Lukas Wuttke Date: Mon, 6 Jul 2026 14:36:20 +0200 Subject: [PATCH 1/4] =?UTF-8?q?feat(installer):=20overhaul=20first-run=20U?= =?UTF-8?q?X=20=E2=80=94=20plainer=20output,=20location=20auto-detect,=20r?= =?UTF-8?q?eal=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Line-by-line pass over the one-command install experience, driven by a real production run that failed opaquely on a mistyped location zone ("berlin"). - Bootstrap: reframe supply-chain verification as plain-language reassurance (drop cosign/manifest/keyless jargon and the alarming "cosign not found"), lowercase brand, drop the redundant "Running… environment setup" line. - Welcome banner: drop the inaccurate "nothing modified outside ~/.tracebloc and Docker" promise (the installer also brew-installs Docker + system deps); fix brand casing on the sudo prompt. - Step 1: de-duplicate the Apple-Silicon messaging and warn on the real limitation (no GPU training on macOS) rather than on the chip itself. - Step 3 (provision): auto-detect the carbon-reporting zone from the system timezone via the OS's own zone.tab (no network call, no embedded zone list to drift) and let the user Enter-to-accept / override / skip; surface the ACTUAL client-create failure inline (e.g. an unrecognized zone) instead of a generic "see the log". - Step 4 + summary + PowerShell: advertise the shipped dataset commands (dataset push/rm → data ingest/delete). - Drop internal ticket refs (RFC-0001 R8) that leaked into user-facing errors. Tests: provision, bootstrap, install-cli, summary bats suites green (50/50). Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/install-k8s.ps1 | 10 ++--- scripts/install.sh | 28 ++++++------- scripts/lib/common.sh | 7 +--- scripts/lib/detect-gpu.sh | 13 ++----- scripts/lib/install-cli.sh | 10 ++--- scripts/lib/provision.sh | 80 ++++++++++++++++++++++++++++++++++++-- scripts/lib/summary.sh | 2 +- 7 files changed, 107 insertions(+), 43 deletions(-) diff --git a/scripts/install-k8s.ps1 b/scripts/install-k8s.ps1 index 1c8f01e..b4b4906 100644 --- a/scripts/install-k8s.ps1 +++ b/scripts/install-k8s.ps1 @@ -1373,7 +1373,7 @@ function Print-Summary { Write-Host "" Write-Host " What to do next" -ForegroundColor White Write-Host " 1. Ingest your training and test data with the tracebloc CLI:" - Write-Host " tracebloc dataset push ./data" -ForegroundColor Cyan + Write-Host " tracebloc data ingest ./data" -ForegroundColor Cyan Write-Host " 2. Define your first AI use case and invite vendors" Write-Host "" Hint "Dashboard: https://ai.tracebloc.io Logs: ~\.tracebloc\ Data: /tracebloc/$ns" @@ -1710,7 +1710,7 @@ function Invoke-DiagnoseBundle { # Installs the `tracebloc` CLI via its own released installer (tracebloc/cli), # which downloads the right build for this OS/arch and verifies it (SHA256 + # cosign signature). Lets the user push datasets to the client they just set -# up: tracebloc dataset push ./data +# up: tracebloc data ingest ./data # # NON-FATAL: runs after the client is connected, so a CLI-install hiccup warns # and moves on. The CLI's own installer sets $ErrorActionPreference='Stop' and @@ -1742,7 +1742,7 @@ function Test-TraceblocCli { if (Has "tracebloc") { # `tracebloc version` is the real proof; cosmetic, never fatal. The canonical - # "tracebloc dataset push ./data" next step lives in Print-Summary's "What to + # "tracebloc data ingest ./data" next step lives in Print-Summary's "What to # do next" — don't duplicate it; just confirm the verdict. $ver = "" try { $ver = (& tracebloc version 2>$null | Select-Object -First 1) } catch { $ver = "" } @@ -1756,7 +1756,7 @@ function Test-TraceblocCli { # it is and how to use it now (so the summary's command works from a new window). Ok "tracebloc CLI installed -- open a new PowerShell window to use it." Hint " Installed to: $TRACEBLOC_CLI_INSTALL_DIR" - Hint " Or use it now via: & `"$TRACEBLOC_CLI_INSTALL_DIR\tracebloc.exe`" dataset push .\data" + Hint " Or use it now via: & `"$TRACEBLOC_CLI_INSTALL_DIR\tracebloc.exe`" data ingest .\data" } function Install-TraceblocCli { @@ -1765,7 +1765,7 @@ function Install-TraceblocCli { if (Has "tracebloc") { Info "tracebloc CLI already present -- re-running its installer to pick up the latest." } - Info "Installing the tracebloc CLI (dataset push / cluster info / dataset rm)..." + Info "Installing the tracebloc CLI (data ingest / cluster info / data delete)..." # [System.IO.Path]::GetTempPath() is cross-platform (%TEMP% on Windows, /tmp # on Linux); $env:TEMP is null under Linux pwsh, which the ubuntu Pester run diff --git a/scripts/install.sh b/scripts/install.sh index 22091ba..8fd69f3 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -107,7 +107,7 @@ if [[ "${USING_BRANCH:-0}" == "1" || ! "$REF" =~ ^v[0-9]+\.[0-9]+\.[0-9]+([.-][A else echo "[ERROR] '$REF' is not an immutable release tag (expected vX.Y.Z)." >&2 echo " The bootstrap only trusts content-addressable release tags so a moved" >&2 - echo " branch ref can't change what runs as root on your box (RFC-0001 R8)." >&2 + echo " branch ref can't change what runs as root on your box." >&2 echo " Use a release tag, or for local dev set TRACEBLOC_ALLOW_UNVERIFIED=1." >&2 exit 1 fi @@ -121,7 +121,7 @@ fi case "$REF" in */*|*..*) echo "[ERROR] Ref '$REF' contains a path separator or '..' — refusing to build a" >&2 - echo " fetch URL from it (path-traversal guard, RFC-0001 R8)." >&2 + echo " fetch URL from it (path-traversal guard)." >&2 exit 1 ;; esac @@ -135,7 +135,7 @@ REPO_REL="https://github.com/tracebloc/client/releases/download/${REF}" TMPDIR="$(mktemp -d)" trap 'rm -rf "$TMPDIR"' EXIT -echo "Downloading Tracebloc client installer (ref: $REF)..." +echo "tracebloc client installer · $REF" mkdir -p "$TMPDIR/lib" @@ -203,6 +203,9 @@ _sha256_of() { verify_against_manifest() { local manifest="$TMPDIR/manifest.sha256" + echo "" + echo "Verifying the installer is authentic before anything runs…" + if ! _sha256_of "$TMPDIR/install-k8s.sh" >/dev/null 2>&1; then echo "[ERROR] No sha256 tool (sha256sum / shasum) on PATH — can't verify the" >&2 echo " installer's integrity. Install coreutils (Linux) or ensure" >&2 @@ -216,7 +219,7 @@ verify_against_manifest() { return 0 fi echo "[ERROR] Couldn't fetch manifest.sha256 for ref '$REF' — refusing to run" >&2 - echo " unverified installer scripts (RFC-0001 R8). If this ref pre-dates" >&2 + echo " unverified installer scripts. If this ref pre-dates" >&2 echo " signed manifests, pin a newer release tag." >&2 exit 1 fi @@ -232,7 +235,7 @@ verify_against_manifest() { # many spaces the sha tool emits); take its first field as the digest. expected="$(awk -v p="$rel" '$NF == p {print $1; exit}' "$manifest")" if [[ -z "$expected" ]]; then - echo "[ERROR] $rel has no entry in manifest.sha256 — refusing to run it (RFC-0001 R8)." >&2 + echo "[ERROR] $rel has no entry in manifest.sha256 — refusing to run it." >&2 exit 1 fi actual="$(_sha256_of "$TMPDIR/${f#scripts/}")" @@ -241,11 +244,11 @@ verify_against_manifest() { echo " expected: $expected" >&2 echo " actual: $actual" >&2 echo " Someone may have tampered with the installer. Aborting before any" >&2 - echo " privileged step runs (RFC-0001 R8)." >&2 + echo " privileged step runs." >&2 exit 1 fi done - echo " ✓ all installer scripts verified against the signed manifest" + echo " ✔ All ${#FILES[@]} installer files verified — none were altered" } download_manifest() { @@ -281,7 +284,7 @@ verify_manifest_signature() { fi echo "[ERROR] cosign is required to verify the installer's signed manifest and" >&2 echo " couldn't be found or bootstrapped. Refusing to fall back to an" >&2 - echo " unauthenticated, same-channel checksum (RFC-0001 R8)." >&2 + echo " unauthenticated, same-channel checksum." >&2 echo " Fix: install cosign (https://docs.sigstore.dev/cosign/installation/)" >&2 echo " and re-run, or for local development only set TRACEBLOC_ALLOW_UNVERIFIED=1." >&2 exit 1 @@ -294,7 +297,7 @@ verify_manifest_signature() { return 0 fi echo "[ERROR] manifest.sha256.sig / .cert not published for release '$REF' — can't" >&2 - echo " authenticate the manifest. Pin a release tag that ships them (RFC-0001 R8)." >&2 + echo " authenticate the manifest. Pin a release tag that ships them." >&2 exit 1 fi @@ -305,10 +308,10 @@ verify_manifest_signature() { --certificate "$cert" \ --signature "$sig" \ "$manifest" >/dev/null 2>&1; then - echo " ✓ manifest signature verified (cosign keyless)" + echo " ✔ Signature verified — published by tracebloc (Sigstore keyless)" else echo "[ERROR] cosign signature verification FAILED for manifest.sha256 — refusing" >&2 - echo " to install (RFC-0001 R8)." >&2 + echo " to install." >&2 exit 1 fi } @@ -342,7 +345,7 @@ ensure_cosign() { local bin="$TMPDIR/cosign" local sums="$TMPDIR/cosign_checksums.txt" - echo " cosign not found — bootstrapping pinned ${COSIGN_VERSION} to verify the manifest…" + echo " · Fetching the signature-verification tool (cosign)…" curl -fsSL --tlsv1.2 "$base/$asset" -o "$bin" 2>/dev/null || return 1 curl -fsSL --tlsv1.2 "$base/cosign_checksums.txt" -o "$sums" 2>/dev/null || return 1 @@ -363,5 +366,4 @@ verify_against_manifest chmod +x "$TMPDIR/install-k8s.sh" -echo "Running Tracebloc environment setup..." bash "$TMPDIR/install-k8s.sh" "$@" diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh index 3205348..e72dbf7 100755 --- a/scripts/lib/common.sh +++ b/scripts/lib/common.sh @@ -140,7 +140,7 @@ preflight_sudo() { return 0 fi echo "" - prompt_header "Tracebloc needs administrator permissions to install" + prompt_header "tracebloc needs administrator access to install" hint "Docker and system dependencies." echo "" hint "You may be asked for your password once." @@ -358,10 +358,7 @@ print_banner() { echo "" echo -e " ${DIM}This installer sets up a secure compute environment${RESET}" echo -e " ${DIM}on your machine and connects it to the tracebloc network.${RESET}" - echo "" - echo -e " ${DIM}Nothing will be modified outside:${RESET}" - echo -e " ${DIM} ~/.tracebloc/ (data and config)${RESET}" - echo -e " ${DIM} Docker (container runtime)${RESET}" + echo -e " ${DIM}All data and config stay in ~/.tracebloc/.${RESET}" echo "" log "OS=$OS Arch=$ARCH Cluster='$CLUSTER_NAME' Servers=$SERVERS Agents=$AGENTS" log "Host data dir: $HOST_DATA_DIR → /tracebloc (inside k3s nodes)" diff --git a/scripts/lib/detect-gpu.sh b/scripts/lib/detect-gpu.sh index e525c57..20f7770 100755 --- a/scripts/lib/detect-gpu.sh +++ b/scripts/lib/detect-gpu.sh @@ -9,17 +9,10 @@ detect_gpu() { if [[ "$OS" == "Darwin" ]]; then if [[ "$ARCH" == "arm64" ]]; then GPU_VENDOR="apple_silicon" - echo "" - warn "Apple Silicon detected." - info "GPU acceleration is not yet available on macOS." - info "Your environment will run in CPU mode." - echo "" - hint "For GPU-accelerated model training," - hint "deploy tracebloc on a Linux machine with NVIDIA GPUs." - else - warn "Intel Mac detected." - info "Your environment will run in CPU mode." fi + echo "" + warn "GPU training isn't supported on macOS yet — this machine will run in CPU mode." + hint "For GPU-accelerated training, deploy on a Linux machine with NVIDIA GPUs." return fi diff --git a/scripts/lib/install-cli.sh b/scripts/lib/install-cli.sh index 223780d..d7c25bd 100644 --- a/scripts/lib/install-cli.sh +++ b/scripts/lib/install-cli.sh @@ -5,7 +5,7 @@ # Installs the `tracebloc` command-line tool so the user can push datasets to # the client they just set up: # -# tracebloc dataset push ./data +# tracebloc data ingest ./data # # It does NOT reimplement any install logic — it runs the CLI's own released # installer (github.com/tracebloc/cli), which downloads the right build for @@ -94,7 +94,7 @@ _verify_tracebloc_cli() { # too; otherwise be honest and say how to use it now. # `tracebloc version` is the real proof; keep it cosmetic (never let a failing # version call or a SIGPIPE flip the outcome). The canonical "tracebloc - # dataset push ./data" next step lives in the summary's "What to do next". + # data ingest ./data" next step lives in the summary's "What to do next". local ver; ver="$(tracebloc version 2>/dev/null | head -1 || true)" if has tracebloc; then # Usable right now AND in new terminals — the fully-clean verdict. @@ -113,7 +113,7 @@ _verify_tracebloc_cli() { local rc; rc="$(_cli_rc_for_shell || true)" hint "This shell won't see it yet — open a new terminal, or load it now: source ${rc}" fi - info "Then the 'tracebloc dataset push ./data' step below will work." + info "Then the 'tracebloc data ingest ./data' step below will work." return 0 fi @@ -137,7 +137,7 @@ _verify_tracebloc_cli() { hint " echo '${export_line}' >> ${rc}" hint " source ${rc}" fi - info "Then the 'tracebloc dataset push ./data' step below will work." + info "Then the 'tracebloc data ingest ./data' step below will work." return 0 } @@ -153,7 +153,7 @@ install_tracebloc_cli() { info "tracebloc CLI already present${ver:+ ($ver)} — re-running its installer to pick up the latest." fi - info "Installing the tracebloc CLI (dataset push / cluster info / dataset rm)…" + info "Installing the tracebloc CLI (data ingest / cluster info / data delete)…" local installer installer="$(mktemp)" || { warn "Couldn't install the tracebloc CLI (no temp dir) — your client is set up fine."; return 0; } diff --git a/scripts/lib/provision.sh b/scripts/lib/provision.sh index af48a03..52480fc 100644 --- a/scripts/lib/provision.sh +++ b/scripts/lib/provision.sh @@ -42,6 +42,55 @@ _cli_supports_provisioning() { # out as a function so tests can force the non-interactive path deterministically. _prompt_tty() { [[ -r /dev/tty && -w /dev/tty ]]; } +# _detect_location_zone: best-effort ISO country code for where this machine +# physically runs, derived from the system timezone via the OS's OWN zone.tab — +# no network call (privacy-preserving for on-prem installs) and no embedded zone +# list to drift out of sync with the backend. For single-region countries the +# carbon zone IS the ISO code (DE, FR, US, GB…); the backend validates the final +# value and a miss surfaces via _report_create_failure. Prints " " +# (e.g. "DE Europe/Berlin") on success, nothing otherwise. +_detect_location_zone() { + local tz="" zt code + if [[ -n "${TZ:-}" ]]; then + tz="$TZ" + elif [[ -L /etc/localtime ]]; then + tz="$(readlink /etc/localtime 2>/dev/null)"; tz="${tz#*/zoneinfo/}" + elif [[ -r /etc/timezone ]]; then + IFS= read -r tz /dev/null || tz="" + fi + # A real IANA zone looks like "Area/City"; ignore anything else (e.g. "UTC"). + [[ "$tz" == */* ]] || return 0 + for zt in /usr/share/zoneinfo/zone.tab /usr/share/zoneinfo/zone1970.tab; do + [[ -r "$zt" ]] || continue + code="$(awk -v tz="$tz" '$1 !~ /^#/ && $3 == tz {split($1,a,","); print a[1]; exit}' "$zt" 2>/dev/null)" + if [[ -n "$code" ]]; then printf '%s %s\n' "$code" "$tz"; return 0; fi + done + return 0 +} + +# _report_create_failure LOGFILE LOCATION — surface the REAL reason `client create` +# failed on the terminal instead of a generic "see the log". Nothing sensitive is +# exposed: a failed create minted no credential, so its captured output holds only +# the error. Special-cases the commonest tripwire — an unrecognized carbon zone +# (e.g. a city like "berlin" typed instead of a zone code). +_report_create_failure() { + local out="$1" loc="$2" errline l + echo "" + if grep -qiE 'location.*not a valid choice' "$out" 2>/dev/null; then + warn "\"${loc:-that location}\" isn't a recognized carbon zone — the client wasn't created." + hint "Re-run and press Enter to accept the detected zone, leave it blank to skip, or" + hint "use a zone code like DE, FR, US, GB (all codes: https://api.electricitymap.org/v3/zones)." + return 0 + fi + errline="$(grep -aE 'Error:|HTTP [0-9][0-9][0-9]|refused|timed? ?out|unauthorized|forbidden|denied' "$out" 2>/dev/null | head -4)" + if [[ -n "$errline" ]]; then + warn "The client couldn't be provisioned:" + while IFS= read -r l; do [[ -n "$l" ]] && hint "$l"; done <<<"$errline" + else + warn "The client couldn't be provisioned." + fi +} + # _account_owns_namespace NS — does the signed-in account's client list include a # client whose namespace is NS? `client list` prints "…namespace= location=…"; # match that field exactly. Namespace is the only stable join key between a local @@ -159,8 +208,21 @@ provision_client() { printf '\n Name this machine (shown on your tracebloc dashboard): ' >/dev/tty IFS= read -r client_name /dev/tty - IFS= read -r client_location /dev/tty + printf ' Press Enter to use it, type another zone code, or type "skip": ' >/dev/tty + IFS= read -r client_location /dev/tty + IFS= read -r client_location >"${LOG_FILE:-/dev/null}" 2>&1; then + # Capture the create's output so we can surface the ACTUAL failure reason on the + # terminal (not just "see the log"). On success it's appended to the log and the + # credential stays in its 0600 file, never on stdout; on failure nothing was + # minted, so the captured text is safe to show. + local _create_out; _create_out="$(mktemp 2>/dev/null || printf '%s' "${TMPDIR:-/tmp}/tb-create.$$")" + if ! ( umask 077; tracebloc "${_create_args[@]}" ) >"$_create_out" 2>&1; then + cat "$_create_out" >>"${LOG_FILE:-/dev/null}" 2>/dev/null || true rm -f "$cred_file" # remove any partial the failed create may have written - error "Provisioning the client failed — see ${LOG_FILE:-the install log}. Re-run to retry." + _report_create_failure "$_create_out" "$client_location" + rm -f "$_create_out" + error "Couldn't provision the client. Re-run to retry — full log: ${LOG_FILE:-the install log}." fi + cat "$_create_out" >>"${LOG_FILE:-/dev/null}" 2>/dev/null || true + rm -f "$_create_out" [[ -f "$cred_file" ]] || error "client create did not write the credential file ($cred_file)." # The credential file is the sole source of truth for what was provisioned. diff --git a/scripts/lib/summary.sh b/scripts/lib/summary.sh index 6282dcf..0ef6105 100755 --- a/scripts/lib/summary.sh +++ b/scripts/lib/summary.sh @@ -113,7 +113,7 @@ print_summary() { echo "" echo -e " ${BOLD}What to do next${RESET}" echo -e " ${WHITE}1.${RESET} Ingest your training and test data with the tracebloc CLI:" - echo -e " ${CYAN}tracebloc dataset push ./data${RESET}" + echo -e " ${CYAN}tracebloc data ingest ./data${RESET}" echo -e " ${WHITE}2.${RESET} Define your first AI use case and invite vendors" echo "" echo -e " ${DIM}Dashboard:${RESET} ${CYAN}https://ai.tracebloc.io${RESET} ${DIM}Logs:${RESET} ${DIM}~/.tracebloc/${RESET} ${DIM}Data:${RESET} ${DIM}/tracebloc/${ns}${RESET}" From dc731191fbe6f97e87f47cf72ad05448cadaf645 Mon Sep 17 00:00:00 2001 From: Lukas Wuttke Date: Mon, 6 Jul 2026 18:13:56 +0200 Subject: [PATCH 2/4] =?UTF-8?q?feat(installer):=20copy=20spec=20interim=20?= =?UTF-8?q?=E2=80=94=20collaborators,=20client=20noun,=20goal-led=20next?= =?UTF-8?q?=20steps,=20location=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Interim slice of the installer UX v2 spec (full restructure gated on the next CLI release): - Banner: drop the vendor value-prop lines (bash + ps1) — the trust claim lives in the verified connected summary. - vendors → other collaborators everywhere user-facing; training phrased as gated on the user's approval. - Next steps rewritten around the goal: ingest data → create your use case (real /my-use-cases URL) → invite other collaborators; dashboard + CLI help demoted to footnotes. - "Name this machine" → "Name this client" (entity noun rule). - Location prompt: fix the live skip-path bug — the released CLI (v0.5.1) hard-requires --location when it can't prompt, so a blank/skip answer doomed the create with a buried error. Now: Enter accepts the detected zone; no detection → re-ask (3 tries) instead of minting a doomed request; unattended runs fall back to the detected zone silently. Tests: 67/68 bats green (1 pre-existing environmental failure on validate_config, identical on develop). Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/install-k8s.ps1 | 9 +++---- scripts/lib/common.sh | 3 --- scripts/lib/install-client-helm.sh | 2 +- scripts/lib/provision.sh | 39 +++++++++++++++++++++--------- scripts/lib/summary.sh | 20 +++++++++++---- 5 files changed, 46 insertions(+), 27 deletions(-) diff --git a/scripts/install-k8s.ps1 b/scripts/install-k8s.ps1 index b4b4906..a9962bd 100644 --- a/scripts/install-k8s.ps1 +++ b/scripts/install-k8s.ps1 @@ -250,9 +250,6 @@ function Print-Banner { Write-Host " " -NoNewline; Write-Host "tracebloc" -ForegroundColor Cyan -NoNewline; Write-Host " -- client setup" Write-Host " " -NoNewline; Write-Host ([string]([char]0x2500) * 40) -ForegroundColor DarkGray Write-Host "" - Write-Host " Test AI models from external vendors on your" - Write-Host " infrastructure -- without exposing your data." - Write-Host "" Hint "This installer sets up a secure compute environment" Hint "on your machine and connects it to the tracebloc network." Write-Host "" @@ -1107,7 +1104,7 @@ function Install-ClientHelm { PromptHeader "To connect this machine, you need a tracebloc client." Hint "A client links your secure environment to the tracebloc" - Hint "platform so vendors can submit models for evaluation." + Hint "platform so other collaborators can submit models for evaluation." Write-Host "" Hint "Create one here (free):" Write-Host " " -NoNewline; Write-Host "https://ai.tracebloc.io/clients" -ForegroundColor White @@ -1367,14 +1364,14 @@ function Print-Summary { Write-Host " Your client is live. Confirm it shows as Online:" Write-Host " https://ai.tracebloc.io/clients" -ForegroundColor Cyan Write-Host "" - Hint "Models that vendors submit train on this machine -- your data never leaves it." + Hint "Models other collaborators submit train on this machine -- your data never leaves it." Write-Host "" Hint "After a reboot, start Docker Desktop to bring your client back (enable 'Start Docker Desktop when you sign in' in Settings -> General to automate)." Write-Host "" Write-Host " What to do next" -ForegroundColor White Write-Host " 1. Ingest your training and test data with the tracebloc CLI:" Write-Host " tracebloc data ingest ./data" -ForegroundColor Cyan - Write-Host " 2. Define your first AI use case and invite vendors" + Write-Host " 2. Create your use case and invite other collaborators: https://ai.tracebloc.io/my-use-cases" Write-Host "" Hint "Dashboard: https://ai.tracebloc.io Logs: ~\.tracebloc\ Data: /tracebloc/$ns" Write-Host "" diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh index e72dbf7..5f15a54 100755 --- a/scripts/lib/common.sh +++ b/scripts/lib/common.sh @@ -353,9 +353,6 @@ print_banner() { echo -e " ${BOLD}${CYAN}tracebloc${RESET} — client setup" echo -e " ${DIM}────────────────────────────────────────${RESET}" echo "" - echo -e " Test AI models from external vendors on your" - echo -e " infrastructure — without exposing your data." - echo "" echo -e " ${DIM}This installer sets up a secure compute environment${RESET}" echo -e " ${DIM}on your machine and connects it to the tracebloc network.${RESET}" echo -e " ${DIM}All data and config stay in ~/.tracebloc/.${RESET}" diff --git a/scripts/lib/install-client-helm.sh b/scripts/lib/install-client-helm.sh index 7e5b000..4a36d1f 100644 --- a/scripts/lib/install-client-helm.sh +++ b/scripts/lib/install-client-helm.sh @@ -387,7 +387,7 @@ install_client_helm() { prompt_header "Connect this machine to a tracebloc client." hint "A client links your secure environment to the tracebloc" - hint "platform so vendors can submit models for evaluation." + hint "platform so other collaborators can submit models for evaluation." echo "" hint "Already have one? Enter its credentials below — or set" hint "TRACEBLOC_CLIENT_ID / TRACEBLOC_CLIENT_PASSWORD to skip this prompt." diff --git a/scripts/lib/provision.sh b/scripts/lib/provision.sh index 52480fc..85fc964 100644 --- a/scripts/lib/provision.sh +++ b/scripts/lib/provision.sh @@ -78,8 +78,8 @@ _report_create_failure() { echo "" if grep -qiE 'location.*not a valid choice' "$out" 2>/dev/null; then warn "\"${loc:-that location}\" isn't a recognized carbon zone — the client wasn't created." - hint "Re-run and press Enter to accept the detected zone, leave it blank to skip, or" - hint "use a zone code like DE, FR, US, GB (all codes: https://api.electricitymap.org/v3/zones)." + hint "Re-run and press Enter to accept the detected zone, or use a zone code" + hint "like DE, FR, US, GB (all codes: https://api.electricitymap.org/v3/zones)." return 0 fi errline="$(grep -aE 'Error:|HTTP [0-9][0-9][0-9]|refused|timed? ?out|unauthorized|forbidden|denied' "$out" 2>/dev/null | head -4)" @@ -205,30 +205,45 @@ provision_client() { # /dev/tty (works under `curl | bash`, whose stdin isn't the terminal) > fail closed. local client_name="${TRACEBLOC_CLIENT_NAME:-}" client_location="${TRACEBLOC_CLIENT_LOCATION:-}" if [[ -z "$client_name" ]] && _prompt_tty; then - printf '\n Name this machine (shown on your tracebloc dashboard): ' >/dev/tty + printf '\n Name this client (shown on your tracebloc dashboard): ' >/dev/tty IFS= read -r client_name /dev/tty - printf ' Press Enter to use it, type another zone code, or type "skip": ' >/dev/tty + printf ' Press Enter to use it, or type another zone code: ' >/dev/tty IFS= read -r client_location /dev/tty - IFS= read -r client_location /dev/tty + IFS= read -r client_location Date: Mon, 6 Jul 2026 18:24:39 +0200 Subject: [PATCH 3/4] test+chore: sync manifest.sha256 and assertions to the v2 copy strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Regenerate scripts/manifest.sha256 (7 script hashes changed by the copy pass) — the Static analysis drift check diffs it against the tree. - Update two bats assertions to the new user-facing strings: the no-name fatal now says 'name for this client' (noun rule) and the bootstrap happy path prints 'installer files verified'. Caught by Linux CI only: on macOS, bats silently passes failing [[ ]] assertions (bash 3.2 errexit quirk), so local green was misleading. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/manifest.sha256 | 14 +++++++------- scripts/tests/install-bootstrap.bats | 2 +- scripts/tests/provision.bats | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/manifest.sha256 b/scripts/manifest.sha256 index 74b9170..08faa1e 100644 --- a/scripts/manifest.sha256 +++ b/scripts/manifest.sha256 @@ -1,16 +1,16 @@ b946562948a7e6581147ec5a661456d841872464dd49df924236ac7825db70d9 scripts/install-k8s.sh -5bdcbae06d6fddca16ca616bdbd83ba68d4206361ea5c2e73c09ac492ea2dfa0 scripts/lib/common.sh +dd0d6c1a533cc30a63524d1cd5796009e70be055c30ec9022d2267dee7be29c5 scripts/lib/common.sh a370fc0c170f34f21d36d67e5434443b4ac8c671fba259826878b9840838b9c4 scripts/lib/preflight.sh -e14e4dfc43f0ac8648a01b09f597d8c6b9e3d3dfb5a2953b099de7a09b4c29f5 scripts/lib/detect-gpu.sh +19be2771df0e1a41b4fa9678e1cf6a77492304f66f73cf705a2ae42b1dac2ba3 scripts/lib/detect-gpu.sh ff17020ab0fe30bde430a54b33150084bb52c9543eabce8897706df94f0c48f5 scripts/lib/gpu-nvidia.sh fd8d37d698c242ab081095625aa430bb91971db646e35393a02bbadd41325898 scripts/lib/gpu-amd.sh 2552776f16a7e697034a1ba8137cc87797ebfb8b29fee84f2aa6662fd6fefbbd scripts/lib/setup-macos.sh 2e64a2c47bdd14c861f8b5e86b06d1dfe4eb349ea6f76d9d22ecf3bb1ec12e04 scripts/lib/setup-linux.sh dd2e1f517b0f6bb665eacebfb7885c6a81443f69c2ebc772433a6baed53c7a8e scripts/lib/cluster.sh 5de1afac8b1062a7f8d0691b46ad39ccdc2287e0e422b53c48628ecfd47d852e scripts/lib/gpu-plugins.sh -3e5bc6dac689f269a9ba06e1be5a831465ff74e8d78465ec7f893ea097a5b785 scripts/lib/install-client-helm.sh -f2350fbafbc5ad22d7a87ec55c83b5138e2d039a6eb5580bb8410fd01b3536e2 scripts/lib/install-cli.sh -63ac8ab924a453f2ae0840c2087a97d7c592c8083260a162c6c51322df96876e scripts/lib/provision.sh -58bd83c06ca16e1d9346700a3f6bcafc67f20130f0bc1f160d2faedab19fd2cf scripts/lib/summary.sh +ae327ea8530930cce34d18d4001b5321bb6615d24060eebc81004f6e2fd7e1f5 scripts/lib/install-client-helm.sh +f890cc3917b09b4b779fcd1a53b902370cb291488a7905f9d710daa871a9eb72 scripts/lib/install-cli.sh +097a875c87ba60810a2fee5aeda37d9c851ff6d65422348b614afa0c161b09b8 scripts/lib/provision.sh +7c5ca5fc1b3f3d5b2b699811da603b40b8879b317b1c756b8ed15da104192828 scripts/lib/summary.sh f22b3f57722feaf3a05a8527988c4404c27b772dd8ba8d6d4c6fe08077081a4a scripts/lib/diagnose.sh -8811bd7086b7fffc6a7b158c3261a2e9659bb24490da69d402694f3680fbc2f1 scripts/install-k8s.ps1 +8c8973117018dee6518df1b0067d681feeee27e10e42be84e52479725b9ffe5a scripts/install-k8s.ps1 diff --git a/scripts/tests/install-bootstrap.bats b/scripts/tests/install-bootstrap.bats index d658456..2e8f02a 100644 --- a/scripts/tests/install-bootstrap.bats +++ b/scripts/tests/install-bootstrap.bats @@ -157,7 +157,7 @@ run_boot_no_cosign() { @test "happy path: immutable tag + valid manifest + good signature runs install-k8s.sh" { REF="v9.9.9" COSIGN_RESULT=0 run_boot [ "$status" -eq 0 ] - [[ "$output" == *"verified against the signed manifest"* ]] + [[ "$output" == *"installer files verified"* ]] [ -f "$SBX/k8s-ran" ] # privileged step reached only after verify } diff --git a/scripts/tests/provision.bats b/scripts/tests/provision.bats index 62435e0..06ac913 100644 --- a/scripts/tests/provision.bats +++ b/scripts/tests/provision.bats @@ -246,7 +246,7 @@ _stub_tracebloc() { _stub_tracebloc 'TRACEBLOC_CLIENT_ID=1\nTRACEBLOC_CLIENT_PASSWORD=p\nTB_NAMESPACE=ns\n' run provision_client [ "$status" -ne 0 ] - [[ "$output" == *"name for this machine is required"* ]] + [[ "$output" == *"name for this client is required"* ]] # and it must not have called client create (no argv recorded) [ ! -s "$CREATE_ARGS_FILE" ] } From f6e2607bfffadf24d4534e7d121d97bd0c01b1d7 Mon Sep 17 00:00:00 2001 From: Asad Iqbal Date: Tue, 7 Jul 2026 11:48:07 +0500 Subject: [PATCH 4/4] fix(installer): harden location fallback + create-output temp path (review nits) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two small robustness fixes from PR review, both in provision_client: - Trim name/location BEFORE the silent auto-detect fallback. A whitespace-only location answer (spaces then Enter) was non-empty at the fallback check, so it slipped through untrimmed and only got emptied afterward — re-dooming the create with a buried error, the exact failure this PR fixes. Now it degrades to the detected zone. - When mktemp is unavailable, capture client-create output inside the install dir (HOST_DATA_DIR, which we own) instead of a predictable world-writable /tmp path that is a symlink-clobber target under sudo. Manifest regenerated for the provision.sh change. Co-Authored-By: Claude Opus 4.8 --- scripts/lib/provision.sh | 25 ++++++++++++++++--------- scripts/manifest.sha256 | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/scripts/lib/provision.sh b/scripts/lib/provision.sh index 85fc964..3730c7b 100644 --- a/scripts/lib/provision.sh +++ b/scripts/lib/provision.sh @@ -231,18 +231,22 @@ provision_client() { fi fi fi - # No location yet (unattended env-name path, or a TTY-less run): fall back to - # the timezone-detected zone silently — the released CLI requires a zone, and - # this matches the target spec (silent auto-derive, never prompt). If detection - # also comes up empty, the create's own error is surfaced by - # _report_create_failure below rather than failing blind here. + # Trim surrounding whitespace from the (possibly typed) values FIRST, so a + # whitespace-only answer (spaces then Enter) counts as "unset" for the silent + # fallback below instead of slipping through as a non-empty, doomed location. + client_name="${client_name#"${client_name%%[![:space:]]*}"}"; client_name="${client_name%"${client_name##*[![:space:]]}"}" + client_location="${client_location#"${client_location%%[![:space:]]*}"}"; client_location="${client_location%"${client_location##*[![:space:]]}"}" + + # No location yet (unattended env-name path, a TTY-less run, or a whitespace-only + # answer): fall back to the timezone-detected zone silently — the released CLI + # requires a zone, and this matches the target spec (silent auto-derive, never + # prompt). The detected code is already whitespace-free. If detection also comes + # up empty, the create's own error is surfaced by _report_create_failure below + # rather than failing blind here. if [[ -z "$client_location" ]]; then client_location="$(_detect_location_zone)"; client_location="${client_location%% *}" fi - # Trim surrounding whitespace from the (possibly typed) values. - client_name="${client_name#"${client_name%%[![:space:]]*}"}"; client_name="${client_name%"${client_name##*[![:space:]]}"}" - client_location="${client_location#"${client_location%%[![:space:]]*}"}"; client_location="${client_location%"${client_location##*[![:space:]]}"}" [[ -n "$client_name" ]] || error "A name for this client is required to provision it. Re-run in a terminal to be prompted, or set TRACEBLOC_CLIENT_NAME (and optionally TRACEBLOC_CLIENT_LOCATION) for an unattended install." # --name is required; pass --location only when we have one (the CLI defaults it # otherwise). Build as an array so values with spaces survive intact. @@ -254,7 +258,10 @@ provision_client() { # terminal (not just "see the log"). On success it's appended to the log and the # credential stays in its 0600 file, never on stdout; on failure nothing was # minted, so the captured text is safe to show. - local _create_out; _create_out="$(mktemp 2>/dev/null || printf '%s' "${TMPDIR:-/tmp}/tb-create.$$")" + # Prefer mktemp; if it's unavailable, fall back INSIDE the install dir (which we + # own and just wrote the 0600 credential into) rather than a predictable + # world-writable /tmp path — that path is a symlink-clobber target under sudo. + local _create_out; _create_out="$(mktemp 2>/dev/null)" || _create_out="${HOST_DATA_DIR}/.client-create.$$.out" if ! ( umask 077; tracebloc "${_create_args[@]}" ) >"$_create_out" 2>&1; then cat "$_create_out" >>"${LOG_FILE:-/dev/null}" 2>/dev/null || true rm -f "$cred_file" # remove any partial the failed create may have written diff --git a/scripts/manifest.sha256 b/scripts/manifest.sha256 index 08faa1e..f2b4ac9 100644 --- a/scripts/manifest.sha256 +++ b/scripts/manifest.sha256 @@ -10,7 +10,7 @@ dd2e1f517b0f6bb665eacebfb7885c6a81443f69c2ebc772433a6baed53c7a8e scripts/lib/cl 5de1afac8b1062a7f8d0691b46ad39ccdc2287e0e422b53c48628ecfd47d852e scripts/lib/gpu-plugins.sh ae327ea8530930cce34d18d4001b5321bb6615d24060eebc81004f6e2fd7e1f5 scripts/lib/install-client-helm.sh f890cc3917b09b4b779fcd1a53b902370cb291488a7905f9d710daa871a9eb72 scripts/lib/install-cli.sh -097a875c87ba60810a2fee5aeda37d9c851ff6d65422348b614afa0c161b09b8 scripts/lib/provision.sh +fac5860148ccd199fca2f17042227a33046c3f18df23edf8a67a232d07d2cda3 scripts/lib/provision.sh 7c5ca5fc1b3f3d5b2b699811da603b40b8879b317b1c756b8ed15da104192828 scripts/lib/summary.sh f22b3f57722feaf3a05a8527988c4404c27b772dd8ba8d6d4c6fe08077081a4a scripts/lib/diagnose.sh 8c8973117018dee6518df1b0067d681feeee27e10e42be84e52479725b9ffe5a scripts/install-k8s.ps1