Skip to content

fix(installer): survive curl|bash — retry name prompt (customer-reported), read creds from terminal, guard pkg-index refresh - #326

Merged
LukasWodka merged 4 commits into
developfrom
fix/installer-robustness
Jul 10, 2026
Merged

fix(installer): survive curl|bash — retry name prompt (customer-reported), read creds from terminal, guard pkg-index refresh#326
LukasWodka merged 4 commits into
developfrom
fix/installer-robustness

Conversation

@LukasWodka

@LukasWodkaLukasWodka commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Installer robustness for the curl … | bash path — three fixes that stop opaque set -e aborts, including the customer-reported onboarding failure of 2026-07-09 (Moritz, external).

1. fix(installer) — retry the client-name prompt so tty type-ahead can't abort provisioning ⭐ customer-reported

provision_client read the client name with IFS= read -r client_name </dev/tty || true — one shot, status swallowed by || true, no retry, no fallback (location silently defaults to the detected zone, which is why only the name failed hard). Any empty/failed read → the fatal "A name for this client is required to provision it." The customer saw both prompts print, neither captured, install aborted.

Most likely trigger: tty type-ahead — during the ~minute browser-approval wait the CLI reads nothing, so a stray newline queued in the terminal is consumed by the read as an empty name. (An adversarial verification pass ruled out the background-process-group and "login drains the tty" theories: the reader is foreground and tracebloc login never touches the tty — the empty read is an environmental dead/queued-input condition, not our process state.)

Fix: read the name in a bounded retry loop that re-prompts on an empty line (a queued blank is skipped, not accepted) and breaks on a failed read (rc≠0 = EOF / no live input, which re-prompting can't fix) so the actionable set TRACEBLOC_CLIENT_NAME error still fires for genuinely non-interactive runs. Reads route through TB_TTY (same seam as the credential reads below). +2 regression tests.

2. fix(installer) — read credential prompts from the terminal (TB_TTY)

The Step-5 credential prompt read stdin, which under curl … | bash is the piped script → EOF → set -e abort. Now reads from TB_TTY (=/dev/tty), with an actionable error when there's no terminal and no env creds.

3. fix(installer) — don't abort on a flaky package-index refresh

spin_cmd "…" $PM_UPDATE was unguarded under set -e (a flaky mirror aborted the whole install, though the per-package installs below are already guarded). Now || warn.

Regenerated scripts/manifest.sha256 (R8 supply-chain) for all three.

Test plan

  • bats scripts/tests/provision.bats — 18/18 green (incl. the 2 new type-ahead / dead-input tests).
  • bats scripts/tests/install-client-helm.bats + setup-linux.bats — green for the touched paths.
  • Two full-suite failures (_extract_yaml_value: single-quoted with '' escape, validate_config: valid config passes) are pre-existing on develop and macOS-bash-3.2-specific (untouched code) — verified by stashing my changes; Linux CI (standard-checks.yml runs bats + shellcheck) is authoritative.

Immediate customer unblock (independent of merge)

curl -fsSL https://raw.githubusercontent.com/tracebloc/client/main/scripts/install.sh \
| TRACEBLOC_CLIENT_NAME="moritz-macbook" TRACEBLOC_CLIENT_LOCATION=DE bash

Pre-setting the name skips the prompt entirely (the read only runs if [[ -z "$client_name" ]]).

🤖 Generated with Claude Code


Note

Low Risk
Changes are confined to installer shell UX and error handling (no auth backend or cluster logic); behavior for env-supplied credentials and unattended installs is preserved, with new regression tests.

Overview
Hardens the bash installer for curl … | bash and flaky Linux package mirrors so failures surface as clear guidance instead of opaque set -e exits.

Provisioning (provision.sh) routes interactive reads through TB_TTY (default /dev/tty) and retries the client-name prompt on empty lines (fixes customer-reported type-ahead after browser approval) while stopping on EOF so non-interactive runs still hit the TRACEBLOC_CLIENT_NAME error.

Helm credential step (install-client-helm.sh) adds the same TB_TTY pattern, _tty_available / _no_interactive_creds_die, and per-read guards so missing or dead terminals fail with TRACEBLOC_CLIENT_ID / TRACEBLOC_CLIENT_PASSWORD instructions instead of mid-read aborts; “use previous settings” only runs when a TTY exists.

Linux deps (setup-linux.sh) wraps $PM_UPDATE with || warn so a failed index refresh does not kill the whole install while per-package installs remain best-effort.

scripts/manifest.sha256 is updated for the touched libs; bats cover no-TTY, EOF-on-readable-TTY, and name type-ahead regressions.

Reviewed by Cursor Bugbot for commit 508a378. Bugbot is set up for automated code reviews on this repo. Configure here.

@LukasWodka
LukasWodka requested a review from saadqbal as a code ownerJuly 9, 2026 18:40
@LukasWodkaLukasWodka changed the title fix(installer): survive curl|bash — guard package-index refresh + read creds from the terminal (bug-hunt MED)fix(installer): survive curl|bash — retry name prompt (customer-reported), read creds from terminal, guard pkg-index refreshJul 10, 2026

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 153c3c4. Configure here.

Comment threadscripts/lib/install-client-helm.sh
Comment threadscripts/lib/install-client-helm.sh Outdated
LukasWodka added a commit that referenced this pull request Jul 10, 2026
…, not just a missing one
Review feedback on #326 (Asad + Bugbot): the credential reads route through
$TB_TTY but had no EOF guard, unlike the provision.sh name read (which breaks on
rc!=0). _tty_available only checks `-r`, so on a readable-but-dead-input tty
(non-PTY ssh, an IDE terminal, a drained/queued tty — the same class this PR
documents for provision.sh) it returns true, the actionable no-creds error is
skipped, and the first `read <"$TB_TTY"` hits EOF and aborts under set -e —
the exact opaque failure this PR set out to remove, left in place for creds.
Factor the actionable env-var guidance into _no_interactive_creds_die and call
it from BOTH the `! _tty_available` check AND a per-read `|| _no_interactive_creds_die`
guard on all five prompts (the Use-previous read + the ID/password reads). A
dead-input tty now fails fast with the same guidance as no-tty instead of
aborting mid-read. Happy path (input present) is unchanged — the guard only
fires on EOF; all existing cred tests (re-prompt, reuse-defaults, max-attempts)
consume their fed input exactly and still pass.
+1 regression test (readable /dev/stdin backed by /dev/null → EOF → actionable
error, no helm). Regenerated scripts/manifest.sha256 (R8).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LukasWodkaand others added 4 commits July 10, 2026 13:05
…esh flakes
install_system_deps ran `spin_cmd "Updating package index…" $PM_UPDATE`
unguarded — under set -e a transient mirror/network failure there aborted
the whole install. Yet the per-package installs right below are already
guarded (|| log), so a flaky refresh was MORE fatal than a failed install,
which is backwards: a stale index usually still installs from cache. Guard
it with || warn so we continue to the (guarded) installs, which surface a
genuinely missing package with an actionable message.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…h doesn't abort
The Step-5 credential prompt read from stdin. Under `curl … | bash` stdin
is the piped script, not the terminal, so each `read` hit EOF and (under
set -e) aborted the installer with an opaque failure the moment it reached
the prompt — the dual-mode env-var path (TRACEBLOC_CLIENT_ID/PASSWORD) was
the only way through, but nothing told the user that.
Read prompts from TB_TTY (the controlling terminal, /dev/tty) instead, the
same mechanism provision.sh already uses. When no terminal is available and
no env creds were supplied, fail with an actionable message pointing at
TRACEBLOC_CLIENT_ID/PASSWORD rather than the set -e abort. TB_TTY is
overridable so the bats suite can feed canned input on stdin.
Regenerated scripts/manifest.sha256 (R8 supply-chain: any scripts/ change
must re-pin, alongside the setup-linux.sh guard in the previous commit).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…abort provisioning
Customer-reported 2026-07-09 (external onboarding): the installer signed in,
printed the name + location prompts, captured NEITHER, and died with 'A name
for this client is required to provision it.'
provision_client read the name with `IFS= read -r client_name </dev/tty || true`
— a single shot whose status was swallowed by `|| true`, with no retry and no
fallback (location silently defaults to the detected zone, which is why only the
name failed hard). Any empty/failed read on the name → the fatal error. The most
likely trigger is tty type-ahead: during the ~minute browser-approval wait the
CLI reads nothing, so a stray newline queued in the terminal is consumed by the
read as an empty name. (An adversarial pass ruled out the background-process-group
and login-drains-the-tty theories; the reader is foreground and login never
touches the tty — the empty read is an environmental dead/queued-input condition.)
Fix: read the name in a bounded retry loop that RE-PROMPTS on an empty line
(so a queued blank is skipped, not accepted) and BREAKS on a failed read
(rc!=0 = EOF / no live input, which re-prompting can't fix) so the actionable
'set TRACEBLOC_CLIENT_NAME' error still fires. Reads route through TB_TTY
(defaults to /dev/tty; overridable so the bats suite can feed stdin), matching
the install-client-helm.sh credential reads in this branch; prompt WRITES stay
on /dev/tty but are guarded so a test without a real terminal doesn't abort.
The location reads adopt TB_TTY too (their empty->fallback behavior is unchanged).
2 new provision.bats tests: type-ahead blanks are re-prompted then the real name
is captured; a dead-input tty (EOF) fails fast with the guidance. Regenerated
scripts/manifest.sha256 (R8). NOTE: this is Failure 1 of the report; the existing
install-client-helm.sh fix on this branch does NOT cover these provision.sh reads.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…, not just a missing one
Review feedback on #326 (Asad + Bugbot): the credential reads route through
$TB_TTY but had no EOF guard, unlike the provision.sh name read (which breaks on
rc!=0). _tty_available only checks `-r`, so on a readable-but-dead-input tty
(non-PTY ssh, an IDE terminal, a drained/queued tty — the same class this PR
documents for provision.sh) it returns true, the actionable no-creds error is
skipped, and the first `read <"$TB_TTY"` hits EOF and aborts under set -e —
the exact opaque failure this PR set out to remove, left in place for creds.
Factor the actionable env-var guidance into _no_interactive_creds_die and call
it from BOTH the `! _tty_available` check AND a per-read `|| _no_interactive_creds_die`
guard on all five prompts (the Use-previous read + the ID/password reads). A
dead-input tty now fails fast with the same guidance as no-tty instead of
aborting mid-read. Happy path (input present) is unchanged — the guard only
fires on EOF; all existing cred tests (re-prompt, reuse-defaults, max-attempts)
consume their fed input exactly and still pass.
+1 regression test (readable /dev/stdin backed by /dev/null → EOF → actionable
error, no helm). Regenerated scripts/manifest.sha256 (R8).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka
LukasWodkaforce-pushed the fix/installer-robustness branch from 6770524 to 508a378CompareJuly 10, 2026 11:06
@LukasWodka
LukasWodka merged commit 7cf8d78 into developJul 10, 2026
31 checks passed
saadqbal added a commit that referenced this pull request Jul 10, 2026
Publishes everything unreleased since v1.9.0:
- #323 ingestor default tag → 0.6 (landed as 1.9.1, never released)
- #325 stop leaking client password on curl's argv (CWE-214)
- #326 survive curl|bash: retry name prompt, read creds from terminal,
guard pkg-index refresh (customer-reported)
Refs #328
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
saadqbal added a commit that referenced this pull request Jul 10, 2026
… + curl|bash survival) (#327)
* Merge pull request #325 from tracebloc/fix/cred-leak-curl-argv
fix(installer): stop leaking the client password on curl's argv (CWE-214)
* fix(installer): survive curl|bash — retry name prompt (customer-reported), read creds from terminal, guard pkg-index refresh (#326)
* fix(installer): don't abort Linux install when the package-index refresh flakes
install_system_deps ran `spin_cmd "Updating package index…" $PM_UPDATE`
unguarded — under set -e a transient mirror/network failure there aborted
the whole install. Yet the per-package installs right below are already
guarded (|| log), so a flaky refresh was MORE fatal than a failed install,
which is backwards: a stale index usually still installs from cache. Guard
it with || warn so we continue to the (guarded) installs, which surface a
genuinely missing package with an actionable message.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(installer): read credential prompts from the terminal so curl|bash doesn't abort
The Step-5 credential prompt read from stdin. Under `curl … | bash` stdin
is the piped script, not the terminal, so each `read` hit EOF and (under
set -e) aborted the installer with an opaque failure the moment it reached
the prompt — the dual-mode env-var path (TRACEBLOC_CLIENT_ID/PASSWORD) was
the only way through, but nothing told the user that.
Read prompts from TB_TTY (the controlling terminal, /dev/tty) instead, the
same mechanism provision.sh already uses. When no terminal is available and
no env creds were supplied, fail with an actionable message pointing at
TRACEBLOC_CLIENT_ID/PASSWORD rather than the set -e abort. TB_TTY is
overridable so the bats suite can feed canned input on stdin.
Regenerated scripts/manifest.sha256 (R8 supply-chain: any scripts/ change
must re-pin, alongside the setup-linux.sh guard in the previous commit).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(installer): retry the client-name prompt so tty type-ahead can't abort provisioning
Customer-reported 2026-07-09 (external onboarding): the installer signed in,
printed the name + location prompts, captured NEITHER, and died with 'A name
for this client is required to provision it.'
provision_client read the name with `IFS= read -r client_name </dev/tty || true`
— a single shot whose status was swallowed by `|| true`, with no retry and no
fallback (location silently defaults to the detected zone, which is why only the
name failed hard). Any empty/failed read on the name → the fatal error. The most
likely trigger is tty type-ahead: during the ~minute browser-approval wait the
CLI reads nothing, so a stray newline queued in the terminal is consumed by the
read as an empty name. (An adversarial pass ruled out the background-process-group
and login-drains-the-tty theories; the reader is foreground and login never
touches the tty — the empty read is an environmental dead/queued-input condition.)
Fix: read the name in a bounded retry loop that RE-PROMPTS on an empty line
(so a queued blank is skipped, not accepted) and BREAKS on a failed read
(rc!=0 = EOF / no live input, which re-prompting can't fix) so the actionable
'set TRACEBLOC_CLIENT_NAME' error still fires. Reads route through TB_TTY
(defaults to /dev/tty; overridable so the bats suite can feed stdin), matching
the install-client-helm.sh credential reads in this branch; prompt WRITES stay
on /dev/tty but are guarded so a test without a real terminal doesn't abort.
The location reads adopt TB_TTY too (their empty->fallback behavior is unchanged).
2 new provision.bats tests: type-ahead blanks are re-prompted then the real name
is captured; a dead-input tty (EOF) fails fast with the guidance. Regenerated
scripts/manifest.sha256 (R8). NOTE: this is Failure 1 of the report; the existing
install-client-helm.sh fix on this branch does NOT cover these provision.sh reads.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(installer): guard credential reads against a dead-input tty (EOF), not just a missing one
Review feedback on #326 (Asad + Bugbot): the credential reads route through
$TB_TTY but had no EOF guard, unlike the provision.sh name read (which breaks on
rc!=0). _tty_available only checks `-r`, so on a readable-but-dead-input tty
(non-PTY ssh, an IDE terminal, a drained/queued tty — the same class this PR
documents for provision.sh) it returns true, the actionable no-creds error is
skipped, and the first `read <"$TB_TTY"` hits EOF and aborts under set -e —
the exact opaque failure this PR set out to remove, left in place for creds.
Factor the actionable env-var guidance into _no_interactive_creds_die and call
it from BOTH the `! _tty_available` check AND a per-read `|| _no_interactive_creds_die`
guard on all five prompts (the Use-previous read + the ID/password reads). A
dead-input tty now fails fast with the same guidance as no-tty instead of
aborting mid-read. Happy path (input present) is unchanged — the guard only
fires on EOF; all existing cred tests (re-prompt, reuse-defaults, max-attempts)
consume their fed input exactly and still pass.
+1 regression test (readable /dev/stdin backed by /dev/null → EOF → actionable
error, no helm). Regenerated scripts/manifest.sha256 (R8).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* chore(chart): bump client 1.9.1 → 1.9.2 (version + appVersion) (#329)
Publishes everything unreleased since v1.9.0:
- #323 ingestor default tag → 0.6 (landed as 1.9.1, never released)
- #325 stop leaking client password on curl's argv (CWE-214)
- #326 survive curl|bash: retry name prompt, read creds from terminal,
guard pkg-index refresh (customer-reported)
Refs #328
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: lukasWuttke <54042461+LukasWodka@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka
LukasWodka deleted the fix/installer-robustness branch August 14, 2026 13:53
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@LukasWodka@saadqbal