Skip to content

feat(cli): tracebloc prepare-host wrapper (#1178, cli) - #394

Merged
shujaatTracebloc merged 10 commits into
developfrom
feat/cli-prepare-host
Jul 23, 2026
Merged

feat(cli): tracebloc prepare-host wrapper (#1178, cli)#394
shujaatTracebloc merged 10 commits into
developfrom
feat/cli-prepare-host

Conversation

@LukasWodka

@LukasWodkaLukasWodka commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What (#1178, cli half)

Adds tracebloc prepare-host — a discoverable command for the one-time administrator step that readies a shared / HPC host so a non-admin user can then install tracebloc with no root.

It's a thin wrapper: it re-runs the official installer's verified prepare-host step (curl -fsSL https://tracebloc.io/i.sh | bash -s -- prepare-host), exactly as tracebloc upgrade delegates to the installer rather than re-implementing any privileged host prep in the CLI — the privileged surface stays in one cosign-verified place.

Tests

  • Registered in root; copy catalog gains 11-prepare-host.golden and the strings flow into zz-all-strings.golden.
  • go build / go vet / staticcheck (all,-ST1005) / deadcode gate clean; catalog no-drift; gofmt clean.

Pairing

The installer side (the actual prepare-host step this wraps) is tracebloc/client#377. This command is only useful once that lands + ships in i.sh; until then it wraps a step the released installer doesn't yet have (like the B2 cli half, they release together).

Part of the least-privilege install epic — tracebloc/backend#1168, ticket tracebloc/backend#1178 (cli half).

🤖 Generated with Claude Code


Note

Medium Risk
The command runs privileged installer work via bash/curl as root on Unix hosts; env/username handling is carefully guarded but misconfiguration could still affect host prep. Depends on the installer shipping the prepare-host subcommand.

Overview
Adds tracebloc prepare-host as a top-level command for the one-time admin step on shared/HPC hosts so a non-admin can install tracebloc without root. It delegates to the official installer's prepare-host step (same pattern as upgrade), with an optional researcher username to pass TB_PREPARE_USER for docker-group access.

Installer execution downloads i.sh to a temp file (set -e, curl -o) instead of curl | bash, preserving TTY stdin for interactive prompts and failing closed on download errors. Child env strips ambient TB_PREPARE_USER unless a username arg is given; usernames are validated before use. Ctrl-C maps to exit 130; failures surface a manual retry hint that preserves TB_PREPARE_USER when applicable. Windows gets an explanatory no-op instead of invoking bash.

Refactor:installerURL is centralized in doctor.go so installCmd and the prepare-host download share one URL.

Copy catalog gains 12-prepare-host.golden; home help lists the new command; unit tests cover the shell script shape, env handling, and OS guard.

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

…0001 #1178)
Adds `tracebloc prepare-host`: a thin, discoverable wrapper that re-runs the
official installer's verified prepare-host step (curl … | bash -s -- prepare-host),
exactly like `tracebloc upgrade` delegates to the installer rather than
re-implementing privileged host prep in the CLI. Registered in root; copy catalog
gains 11-prepare-host.golden + the strings flow into zz-all-strings. build/vet/
staticcheck/deadcode clean; catalog no-drift; gofmt clean.
Pairs with the client installer prepare-host step (tracebloc/client#377).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodkaLukasWodka self-assigned this Jul 22, 2026
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadinternal/cli/prepare_host.go Outdated
Comment threadinternal/cli/prepare_host.go
Comment threadinternal/cli/prepare_host.go
…allowed (Bugbot #394)
Without pipefail, `curl … | bash -s -- prepare-host` under `bash -c` exits 0 when
curl fails (bash reads empty stdin), so the command reported success while
prepare-host never ran. Prepend `set -o pipefail;` so curl's non-zero propagates
and c.Run() surfaces the failure. Regression guard added.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

divyasinghds
divyasinghds previously approved these changes Jul 23, 2026

@divyasinghdsdivyasinghds left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approve. Thin wrapper delegating to the cosign-verified installer, keeping the privileged surface in one place. The 'set -o pipefail' fix is correct and essential (a failed curl would otherwise report success), with a guard test. Natural home for the --user affordance if you address the prepare-host targeting note I left on client#377.

Cancelling the context (Ctrl-C / parent shutdown) previously killed only
the top-level `bash -c`, leaving the `curl` and the `bash -s` prepare-host
child -- which performs privileged host prep -- running detached after the
CLI had already reported failure and exited (Bugbot #394).
Extract prepareHostCmd(ctx): set SysProcAttr.Setpgid so the pipeline gets
its own process group, and a Cancel that group-signals (kill -PGID SIGINT)
so the whole pipeline stops when the user aborts. Add a test asserting
Setpgid + Cancel are wired.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadinternal/cli/prepare_host.go
…on TTY)
`curl | bash -s -- prepare-host` makes the inner bash read its *program*
from the pipe, so the installer's stdin is no longer the terminal — any
interactive prompt in prepare-host (e.g. which non-admin user gets runtime
access) gets EOF (Bugbot #394, second finding).
Switching to `bash <(curl …)` would fix stdin but reopen the FIRST #394
finding: process-substitution exit codes bypass pipefail, so a failed curl
would silently run nothing. Instead download to a temp file under `set -e`
(so a failed `curl -o` aborts) and run the file (stdin stays on the TTY).
Best of both: fail-closed on download error AND interactive-capable.
The manual-run hint shown on failure uses `bash <(curl …)` — the repo's
recommended idiom — since a human re-running it keeps their own TTY.
Add tests: fail-closed on download error (set -e + curl -o), and never pipe
the script into bash.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

@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.

✅ Bugbot reviewed your changes and found no new issues!

2 issues from previous reviews remain unresolved.

Fix All in Cursor

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e9a4f67. Configure here.

@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

@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.

✅ Bugbot reviewed your changes and found no new issues!

2 issues from previous reviews remain unresolved.

Fix All in Cursor

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit e9a4f67. Configure here.

Comment threadinternal/cli/prepare_host.go
Comment threadinternal/cli/prepare_host.go
Comment threadinternal/cli/prepare_host.go
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

@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.

✅ Bugbot reviewed your changes and found no new issues!

3 issues from previous reviews remain unresolved.

Fix All in Cursor

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 7b599be. Configure here.

…nd build tags
The process-group cancel added for Bugbot #394 used syscall.SysProcAttr.Setpgid
and syscall.Kill(-pid, …), which are POSIX-only — the Windows build failed to
compile (unknown field Setpgid; undefined syscall.Kill).
Extract configureProcessGroup into build-tagged files:
- prepare_host_unix.go (//go:build !windows): sets Setpgid + the group-killing
Cancel (unchanged behavior on linux/darwin).
- prepare_host_windows.go (//go:build windows): no-op — POSIX process groups
don't exist there, and exec.CommandContext still kills the top-level process;
prepare-host is a Linux host op regardless.
Move the Setpgid assertion test into prepare_host_unix_test.go (also
!windows-tagged) so `go test`/`go vet` compile on Windows too.
Verified: `GOOS=windows go build/vet ./...` now pass for amd64 + arm64, and the
native build/test/staticcheck stay green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadinternal/cli/prepare_host_unix.go Outdated
Comment threadinternal/cli/prepare_host_unix.go Outdated
…(Bugbot #394)
The process-group approach was wrong for an interactive command and Bugbot
flagged five follow-ups:
- Setpgid put the installer in its own (background) process group while stdin
was the TTY, so any prepare-host prompt got SIGTTIN and hung (High).
- the SIGINT-only Cancel with no WaitDelay could hang Wait forever if a
privileged child ignored the signal, re-opening the orphaned-work risk.
- a user Ctrl-C was wrapped as "prepare-host didn't complete — retry" instead
of a quiet interrupt.
Drop Setpgid and the custom Cancel entirely: keep the installer in the CLI's
foreground process group so prompts work and a terminal Ctrl-C signals the whole
pipeline (bash + curl + child) at once. Add WaitDelay=5s so a programmatic
cancel can't hang Wait. Treat ctx cancel as exitInterrupted (130), matching the
other cancellable paths. This also removes all syscall usage, so the Windows
build no longer needs the build-tagged split (files deleted).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadinternal/cli/prepare_host.go Outdated
Comment threadinternal/cli/prepare_host.go Outdated
…ion (Bugbot #394)
- prepareHostManualHint duplicated the bootstrap idiom owned by installCmd
(doctor.go), so a URL/idiom change could leave the prepare-host fallback
stale. Build it from installCmd + " prepare-host" (same value, one source).
- Interrupt detection only checked ctx.Err() after c.Run(); on a terminal
Ctrl-C the child dies (bash exits 130) and Run can return before
NotifyContext flips ctx.Err(), so an abort was mis-reported as a failed
install. Add prepareHostInterrupted(ctx, err): interrupt if ctx cancelled OR
the run exited 130 (128+SIGINT). Unit-tested (cancelled ctx, exit 130, exit 1).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

@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.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 447cadc. Configure here.

…ss (Divya #377)
prepare-host was cobra.NoArgs with no flag and no mention of TB_PREPARE_USER,
so an admin had no discoverable way to name the researcher — the installer's
docker-group grant (which reads TB_PREPARE_USER) was always skipped and the
feature couldn't deliver Tier-0 access end-to-end.
Add an optional positional arg: `tracebloc prepare-host <researcher-username>`
(MaximumNArgs(1)). The username is validated (Linux-username shape) and passed
to the installer via the TB_PREPARE_USER environment variable (not the command
string, so it can't be shell-interpreted; the installer quotes it for usermod).
Help text explains the arg and stresses it's the researcher, not the admin.
Without it, prepare-host installs prereqs only and prints how to grant access.
The client installer already grants ONLY TB_PREPARE_USER (never $SUDO_USER), so
this closes the loop.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
ContributorAuthor

bugbot run

Comment threadinternal/cli/prepare_host.go
Comment threadinternal/cli/prepare_host.go Outdated
Comment threadinternal/cli/prepare_host.go
divyasinghds
divyasinghds previously approved these changes Jul 23, 2026

@divyasinghdsdivyasinghds left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approve. This now implements the researcher-username affordance I asked for on client#377, and does it securely:

  • prepare-host [researcher-username] (MaximumNArgs(1)), passed to the installer via the ENVIRONMENT (TB_PREPARE_USER=<user>), not the command string — so it can't be shell-interpreted — and validated by a conservative regex that rejects shell metacharacters, spaces, empty, leading -/., and overlong input. Help text is explicit that the username is the researcher, not the admin.
  • Temp-file-not-pipe (set -e; curl -o "$tmp"; bash "$tmp" prepare-host): fails closed on a download error AND keeps stdin on the TTY — important because prepare-host runs preflight_sudo, whose sudo -v password prompt would hang if stdin were the pipe.
  • Foreground process group (no SysProcAttr) with a clear rationale (a backgrounded interactive child gets SIGTTIN; Ctrl-C reaches the whole pipeline), positive WaitDelay, default-SIGKILL teardown.
  • Interrupt detection handles the ctx.Err()-vs-exit-130 race so a Ctrl-C exits quietly (130) rather than as a scary failure.

Thorough test coverage (fail-closed, no-pipe, foreground-group, interrupt, username validation, arg count) + golden files updated. CI green. LGTM.

Resolves the develop conflict (prepare-host vs the merged `upgrade` command):
- root.go: register both prepare-host and upgrade
- copy_catalog_test.go / 00-home.golden: keep both commands; renumber the
prepare-host catalog entry to 12-prepare-host.golden (11 is now upgrade)
- regenerate goldens
Fixes 3 Bugbot findings on prepare-host:
1. Ambient TB_PREPARE_USER not cleared (prepare_host.go): the no-username path
left c.Env unset, so the child inherited any ambient TB_PREPARE_USER and
could grant access the command says it won't. New prepareHostEnv() strips it
and sets it only when a username is given.
2. Failure hint dropped the grant (prepare_host.go): the manual retry hint had
no TB_PREPARE_USER, so copy-pasting it after `prepare-host <user>` failed
silently did less than asked. prepareHostManualHint(user) now prefixes
TB_PREPARE_USER=<user>.
3. Installer URL duplicated (doctor.go/prepare_host.go): extracted a shared
installerURL const so the automated download can't drift from the hint.
Adds regression tests for the env stripping and the username-carrying hint.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@shujaatTracebloc

Copy link
Copy Markdown
Contributor

Pushed 2707081 — merged latest develop (resolving the conflict with the merged upgrade command) and fixed the 3 open Bugbot findings:

  1. Ambient TB_PREPARE_USER not cleared — the no-username path left c.Env unset, inheriting any ambient TB_PREPARE_USER and granting access the command says it won't. New prepareHostEnv() strips it and sets it only when a username is passed.
  2. Failure hint dropped the grantprepareHostManualHint(user) now prefixes TB_PREPARE_USER=<user> so a copy-pasted retry after prepare-host <user> still grants access.
  3. Installer URL duplicated — extracted a shared installerURL const in doctor.go; the automated download and the manual hint now derive from one source.

Conflict resolution: registered both prepare-host and upgrade in root, renumbered the catalog entry to 12-prepare-host.golden (11 is now upgrade), regenerated goldens. Added regression tests for the env stripping + username-carrying hint. Full go test ./... + go vet + gofmt green locally.

Comment threadinternal/cli/prepare_host.go
prepare-host shells out to bash/curl/mktemp and readies a Linux server / HPC
login node (container runtime, docker group) — a Unix-only concept. On Windows
it appeared in --help then failed with a cryptic missing-bash error and a
Unix-only retry hint. It now stops early with a clear explanation (a
no-op-with-message), mirroring upgrade's Windows handling. Adds a regression
test for the OS guard.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@shujaatTracebloc

Copy link
Copy Markdown
Contributor

Pushed 4d9833f addressing the new Bugbot finding (Missing Windows guard, Low): prepare-host shells out to bash/curl and readies a Linux/HPC host, so on Windows it would fail with a cryptic missing-bash error + Unix-only hint. It now stops early with a clear explanation (no-op-with-message), mirroring upgrade's Windows handling. Added a regression test. Full go test ./... + vet + gofmt green locally.

@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 4d9833f. Configure here.

Comment threadinternal/cli/prepare_host.go
@shujaatTracebloc
shujaatTracebloc merged commit 71ed489 into developJul 23, 2026
20 checks passed
@shujaatTracebloc
shujaatTracebloc deleted the feat/cli-prepare-host branch July 23, 2026 15:15
shujaatTracebloc pushed a commit that referenced this pull request Jul 23, 2026
Resolves the develop conflict (branched before the upgrade #390 and
prepare-host #394 commands merged): copy_catalog_test.go auto-merged to keep
all three commands' catalog entries; regenerated goldens (zz-all-strings +
00-home + 08-client) so the harvested strings and command lists include upgrade,
prepare-host, and the seal-check screens together. No code conflicts; the seal
logic is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
aptracebloc added a commit that referenced this pull request Aug 26, 2026
… not failed (backend#2255) (#586)
helm.Upgrade wrapped every Runner error as "helm upgrade failed", so a
Ctrl-C / cancelled context mid-`helm upgrade` — after helm may have already
applied the values — was reported as a flat failure with no progress,
telling the user nothing changed when the change could be live.
- helm.Upgrade now distinguishes an interrupt (ctx cancelled, or the helm
child exiting 130 before NotifyContext flips ctx.Err()) from a genuine
failure, returning the new helm.ErrInterrupted sentinel plus the resolved
Plan so the caller can surface what was in flight. Mirrors the cli
package's installerRunInterrupted (Bugbot #394/#397).
- resources set shows a live wait line during the apply (progress), and on
an interrupt mid-upgrade reports "may already have applied — re-run to
confirm" (via launcher()) and exits 130, never "helm upgrade failed". A
Ctrl-C before the upgrade runs (probe / repo add-update) also exits a quiet
130 via installerRunInterrupted, but without the "may have applied" note.
- Tests for both halves at the helm and cli layers: cancelled-context and
exit-130 interrupts, a pre-upgrade interrupt, a genuine failure, and the
progress wait-line on a real apply.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

3 participants

@LukasWodka@shujaatTracebloc@divyasinghds