From 6f0e92c2cd18e52046fc24c323104e700e610b73 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Wed, 18 Feb 2026 23:49:03 -0500 Subject: [PATCH 1/6] docs: add curl download quickstart without sudo pipe --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f234b8b..3a5befb 100644 --- a/README.md +++ b/README.md @@ -49,12 +49,20 @@ Baudbot is designed as shared engineering infrastructure, not a single-user desk ## Quick Start +```bash +curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/install.sh -o /tmp/baudbot-install.sh +sudo bash /tmp/baudbot-install.sh +rm -f /tmp/baudbot-install.sh +``` + +Alternative (if you want a local source checkout first): + ```bash git clone https://github.com/modem-dev/baudbot.git ~/baudbot sudo ~/baudbot/install.sh ``` -Installer handles setup, dependencies, agent user creation, firewall setup, and initial configuration prompts. +Installer handles setup, dependencies, agent user creation, firewall setup, and initial configuration prompts. It also publishes a git-free runtime release to `/opt/baudbot/releases/` and points `/opt/baudbot/current` at the active version. After install: From c2d26444e65b313494210a66717653b15edfb2e4 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Thu, 19 Feb 2026 00:06:09 -0500 Subject: [PATCH 2/6] cli: add bootstrap install command to baudbot --- README.md | 8 +++--- bin/baudbot | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++ install.sh | 7 +++++- 3 files changed, 83 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3a5befb..0db946f 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,13 @@ Baudbot is designed as shared engineering infrastructure, not a single-user desk ## Quick Start ```bash -curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/install.sh -o /tmp/baudbot-install.sh -sudo bash /tmp/baudbot-install.sh -rm -f /tmp/baudbot-install.sh +curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/bin/baudbot -o /tmp/baudbot +chmod +x /tmp/baudbot +/tmp/baudbot install ``` +This bootstrap command downloads the installer and only escalates when privileged steps are needed. + Alternative (if you want a local source checkout first): ```bash diff --git a/bin/baudbot b/bin/baudbot index d44a5ed..f8b6361 100755 --- a/bin/baudbot +++ b/bin/baudbot @@ -52,6 +52,7 @@ usage() { echo " sessions List agent tmux and pi sessions" echo "" echo -e "${BOLD}Setup:${RESET}" + echo " install Bootstrap install from GitHub (download script, then escalate)" echo " setup One-time system setup (user, deps, firewall, systemd)" echo " config Interactive secrets and config setup" echo " deploy Deploy source + config to agent runtime" @@ -76,6 +77,72 @@ require_root() { fi } +escalate_prefix() { + if [ "$(id -u)" -eq 0 ]; then + return 0 + fi + + if command -v sudo >/dev/null 2>&1; then + echo "sudo" + return 0 + fi + + if command -v doas >/dev/null 2>&1; then + echo "doas" + return 0 + fi + + echo "" + return 1 +} + +download_file() { + local url="$1" + local dest="$2" + + if command -v curl >/dev/null 2>&1; then + curl -fsSL "$url" -o "$dest" + return 0 + fi + + if command -v wget >/dev/null 2>&1; then + wget -q "$url" -O "$dest" + return 0 + fi + + echo "❌ install requires curl or wget to download installer" + exit 1 +} + +bootstrap_install() { + local install_url="${BAUDBOT_INSTALL_SCRIPT_URL:-https://raw.githubusercontent.com/modem-dev/baudbot/main/install.sh}" + local install_script + local escalator="" + + install_script="$(mktemp /tmp/baudbot-install.XXXXXX.sh)" + trap 'rm -f "$install_script"' RETURN + + download_file "$install_url" "$install_script" + chmod 700 "$install_script" + + echo "Downloaded installer: $install_script" + + if [ "$(id -u)" -eq 0 ]; then + bash "$install_script" "$@" + return 0 + fi + + escalator="$(escalate_prefix || true)" + if [ -z "$escalator" ]; then + echo "❌ install requires root privileges but no sudo/doas was found" + echo "Re-run as root, or install sudo/doas first." + return 1 + fi + + echo "Escalating with $escalator for system setup..." + "$escalator" bash "$install_script" "$@" +} + # Detect systemd has_systemd() { command -v systemctl &>/dev/null && [ -d /run/systemd/system ] @@ -129,6 +196,11 @@ print_deployed_version() { } case "${1:-}" in + install) + shift + bootstrap_install "$@" + ;; + start) shift if [ "${1:-}" = "--direct" ]; then diff --git a/install.sh b/install.sh index 2e5364b..c1c094f 100755 --- a/install.sh +++ b/install.sh @@ -1,7 +1,12 @@ #!/bin/bash # Baudbot Interactive Installer # -# One-command setup: +# One-command setup (bootstrap CLI): +# curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/bin/baudbot -o /tmp/baudbot +# chmod +x /tmp/baudbot +# /tmp/baudbot install +# +# Or if you prefer source checkout first: # git clone https://github.com/modem-dev/baudbot.git ~/baudbot && sudo ~/baudbot/install.sh # # Or if already cloned: From 34b60f823d98277f5e651bea7bb23435b6b72311 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Thu, 19 Feb 2026 00:10:54 -0500 Subject: [PATCH 3/6] install: add bootstrap script for one-command baudbot entrypoint --- README.md | 7 ++--- bootstrap.sh | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++ install.sh | 5 ++-- 3 files changed, 90 insertions(+), 7 deletions(-) create mode 100755 bootstrap.sh diff --git a/README.md b/README.md index 0db946f..f10e27d 100644 --- a/README.md +++ b/README.md @@ -50,12 +50,11 @@ Baudbot is designed as shared engineering infrastructure, not a single-user desk ## Quick Start ```bash -curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/bin/baudbot -o /tmp/baudbot -chmod +x /tmp/baudbot -/tmp/baudbot install +curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/bootstrap.sh | bash +baudbot install ``` -This bootstrap command downloads the installer and only escalates when privileged steps are needed. +The bootstrap command installs a lightweight `baudbot` CLI to `/usr/local/bin`, then `baudbot install` performs full system setup and publishes the runtime release to `/opt/baudbot`. Alternative (if you want a local source checkout first): diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..8d91d07 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# Baudbot bootstrap installer (non-root entrypoint) +# +# Usage: +# curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/bootstrap.sh | bash +# baudbot install +# +# What this does: +# 1) Downloads the bootstrap baudbot CLI from GitHub +# 2) Installs it to /usr/local/bin/baudbot (using sudo/doas if needed) +# 3) Prints next step: `baudbot install` + +set -euo pipefail + +BAUDBOT_CLI_URL="${BAUDBOT_CLI_URL:-https://raw.githubusercontent.com/modem-dev/baudbot/main/bin/baudbot}" +BAUDBOT_TARGET_BIN="${BAUDBOT_BOOTSTRAP_TARGET:-/usr/local/bin/baudbot}" +TARGET_DIR="$(dirname "$BAUDBOT_TARGET_BIN")" +TMP_CLI="$(mktemp /tmp/baudbot-cli.XXXXXX)" + +cleanup() { + rm -f "$TMP_CLI" +} +trap cleanup EXIT + +download_file() { + local url="$1" + local dest="$2" + + if command -v curl >/dev/null 2>&1; then + curl -fsSL "$url" -o "$dest" + return 0 + fi + + if command -v wget >/dev/null 2>&1; then + wget -q "$url" -O "$dest" + return 0 + fi + + echo "❌ bootstrap requires curl or wget" >&2 + exit 1 +} + +escalate_prefix() { + if [ "$(id -u)" -eq 0 ]; then + return 0 + fi + + if command -v sudo >/dev/null 2>&1; then + echo "sudo" + return 0 + fi + + if command -v doas >/dev/null 2>&1; then + echo "doas" + return 0 + fi + + echo "" + return 1 +} + +echo "==> Downloading baudbot bootstrap CLI" +download_file "$BAUDBOT_CLI_URL" "$TMP_CLI" +chmod 0755 "$TMP_CLI" + +if [ -w "$TARGET_DIR" ]; then + mkdir -p "$TARGET_DIR" + install -m 0755 "$TMP_CLI" "$BAUDBOT_TARGET_BIN" +else + ESCALATOR="$(escalate_prefix || true)" + if [ -z "$ESCALATOR" ]; then + echo "❌ cannot write to $TARGET_DIR and no sudo/doas found" >&2 + echo "Try running as root, or set BAUDBOT_BOOTSTRAP_TARGET to a user-writable path." >&2 + exit 1 + fi + + echo "==> Installing to $BAUDBOT_TARGET_BIN using $ESCALATOR" + "$ESCALATOR" mkdir -p "$TARGET_DIR" + "$ESCALATOR" install -m 0755 "$TMP_CLI" "$BAUDBOT_TARGET_BIN" +fi + +echo "✅ Installed baudbot bootstrap CLI to $BAUDBOT_TARGET_BIN" +echo "" +echo "Next step:" +echo " baudbot install" diff --git a/install.sh b/install.sh index c1c094f..0ef0026 100755 --- a/install.sh +++ b/install.sh @@ -2,9 +2,8 @@ # Baudbot Interactive Installer # # One-command setup (bootstrap CLI): -# curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/bin/baudbot -o /tmp/baudbot -# chmod +x /tmp/baudbot -# /tmp/baudbot install +# curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/bootstrap.sh | bash +# baudbot install # # Or if you prefer source checkout first: # git clone https://github.com/modem-dev/baudbot.git ~/baudbot && sudo ~/baudbot/install.sh From b8166236afe7d404ecd740febf721019ab000562 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Thu, 19 Feb 2026 00:15:55 -0500 Subject: [PATCH 4/6] docs: simplify quickstart to bootstrap + install only --- README.md | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/README.md b/README.md index f10e27d..d1d0a23 100644 --- a/README.md +++ b/README.md @@ -54,17 +54,6 @@ curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/bootstrap.sh baudbot install ``` -The bootstrap command installs a lightweight `baudbot` CLI to `/usr/local/bin`, then `baudbot install` performs full system setup and publishes the runtime release to `/opt/baudbot`. - -Alternative (if you want a local source checkout first): - -```bash -git clone https://github.com/modem-dev/baudbot.git ~/baudbot -sudo ~/baudbot/install.sh -``` - -Installer handles setup, dependencies, agent user creation, firewall setup, and initial configuration prompts. It also publishes a git-free runtime release to `/opt/baudbot/releases/` and points `/opt/baudbot/current` at the active version. - After install: ```bash From 18bb9b7736467b0575d7749a568a3ef82aad0483 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Thu, 19 Feb 2026 00:20:27 -0500 Subject: [PATCH 5/6] ci: validate bootstrap install flow on droplets --- AGENTS.md | 2 +- bin/ci/setup-arch.sh | 11 ++++++++--- bin/ci/setup-ubuntu.sh | 11 ++++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index a5e34e0..647679f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -186,7 +186,7 @@ bin/ci/droplet.sh destroy "$DROPLET_ID" "$SSH_KEY_ID" Droplets take ~15s to create, ~10s for SSH, ~90s for full setup+tests. Always destroy after — they cost ~$0.003 per run but add up if forgotten. -The CI scripts (`bin/ci/setup-ubuntu.sh`, `bin/ci/setup-arch.sh`) run `install.sh` with simulated input, verify the result, then run the full test suite. Use them as-is or SSH in and test manually. +The CI scripts (`bin/ci/setup-ubuntu.sh`, `bin/ci/setup-arch.sh`) run the bootstrap flow (`bootstrap.sh` → `baudbot install`) with simulated input, verify the result, then run the full test suite. Use them as-is or SSH in and test manually. ## Security Notes diff --git a/bin/ci/setup-arch.sh b/bin/ci/setup-arch.sh index 5e4a2d5..c42895c 100755 --- a/bin/ci/setup-arch.sh +++ b/bin/ci/setup-arch.sh @@ -18,12 +18,17 @@ tar xzf /tmp/baudbot-src.tar.gz chown -R baudbot_admin:baudbot_admin /home/baudbot_admin/ sudo -u baudbot_admin bash -c 'cd ~/baudbot && git init -q && git config user.email "ci@test" && git config user.name "CI" && git add -A && git commit -q -m "init"' -echo "=== Running install.sh ===" +echo "=== Running bootstrap + baudbot install ===" +# Bootstrap installs /usr/local/bin/baudbot, then baudbot install runs install.sh. +# Use file:// URLs so CI tests the uploaded source bundle (not GitHub main). +BAUDBOT_CLI_URL="file:///home/baudbot_admin/baudbot/bin/baudbot" \ +BAUDBOT_BOOTSTRAP_TARGET="/usr/local/bin/baudbot" \ + bash /home/baudbot_admin/baudbot/bootstrap.sh # Simulate interactive input: admin user, required secrets, skip optionals, decline launch # Prompts: admin user, Anthropic, OpenAI(skip), Gemini(skip), OpenCode(skip), # Slack bot, Slack app, Slack users, AgentMail(skip), email(skip), Sentry(skip), Kernel(skip), launch(n) printf 'baudbot_admin\nsk-ant-testkey\n\n\n\nxoxb-test\nxapp-test\nU01TEST\n\n\n\n\nn\n' \ - | bash /home/baudbot_admin/baudbot/install.sh + | BAUDBOT_INSTALL_SCRIPT_URL="file:///home/baudbot_admin/baudbot/install.sh" baudbot install echo "=== Verifying install ===" # .env exists with correct permissions @@ -55,7 +60,7 @@ echo "$HELP_OUT" | grep -q "baudbot" test -x /home/baudbot_agent/.varlock/bin/varlock # Agent can load env (smoke test — varlock validates schema + .env) sudo -u baudbot_agent bash -c 'export PATH="$HOME/.varlock/bin:$HOME/opt/node-v22.14.0-linux-x64/bin:$PATH" && cd ~ && varlock load --path ~/.config/' -echo " ✓ install.sh verification passed" +echo " ✓ bootstrap + install verification passed" echo "=== Installing test dependencies ===" export PATH="/home/baudbot_agent/opt/node-v22.14.0-linux-x64/bin:$PATH" diff --git a/bin/ci/setup-ubuntu.sh b/bin/ci/setup-ubuntu.sh index 7f8a872..aa5dbc3 100755 --- a/bin/ci/setup-ubuntu.sh +++ b/bin/ci/setup-ubuntu.sh @@ -29,12 +29,17 @@ tar xzf /tmp/baudbot-src.tar.gz chown -R baudbot_admin:baudbot_admin /home/baudbot_admin/ sudo -u baudbot_admin bash -c 'cd ~/baudbot && git init -q && git config user.email "ci@test" && git config user.name "CI" && git add -A && git commit -q -m "init"' -echo "=== Running install.sh ===" +echo "=== Running bootstrap + baudbot install ===" +# Bootstrap installs /usr/local/bin/baudbot, then baudbot install runs install.sh. +# Use file:// URLs so CI tests the uploaded source bundle (not GitHub main). +BAUDBOT_CLI_URL="file:///home/baudbot_admin/baudbot/bin/baudbot" \ +BAUDBOT_BOOTSTRAP_TARGET="/usr/local/bin/baudbot" \ + bash /home/baudbot_admin/baudbot/bootstrap.sh # Simulate interactive input: admin user, required secrets, skip optionals, decline launch # Prompts: admin user, Anthropic, OpenAI(skip), Gemini(skip), OpenCode(skip), # Slack bot, Slack app, Slack users, AgentMail(skip), email(skip), Sentry(skip), Kernel(skip), launch(n) printf 'baudbot_admin\nsk-ant-testkey\n\n\n\nxoxb-test\nxapp-test\nU01TEST\n\n\n\n\nn\n' \ - | bash /home/baudbot_admin/baudbot/install.sh + | BAUDBOT_INSTALL_SCRIPT_URL="file:///home/baudbot_admin/baudbot/install.sh" baudbot install echo "=== Verifying install ===" # .env exists with correct permissions @@ -66,7 +71,7 @@ echo "$HELP_OUT" | grep -q "baudbot" test -x /home/baudbot_agent/.varlock/bin/varlock # Agent can load env (smoke test — varlock validates schema + .env) sudo -u baudbot_agent bash -c 'export PATH="$HOME/.varlock/bin:$HOME/opt/node-v22.14.0-linux-x64/bin:$PATH" && cd ~ && varlock load --path ~/.config/' -echo " ✓ install.sh verification passed" +echo " ✓ bootstrap + install verification passed" echo "=== Installing test dependencies ===" export PATH="/home/baudbot_agent/opt/node-v22.14.0-linux-x64/bin:$PATH" From 59ae9d8794bd17d7313f73a915c22b1bee14a8ac Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Thu, 19 Feb 2026 00:25:37 -0500 Subject: [PATCH 6/6] install: use local source snapshot when repo has no origin --- install.sh | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/install.sh b/install.sh index 0ef0026..8865cea 100755 --- a/install.sh +++ b/install.sh @@ -147,18 +147,26 @@ info "Prerequisites installed" header "Source" -REPO_DIR="$ADMIN_HOME/baudbot" +REPO_DIR="${BAUDBOT_REPO_DIR:-$ADMIN_HOME/baudbot}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd || echo "")" if [ -n "$SCRIPT_DIR" ] && [ -f "$SCRIPT_DIR/setup.sh" ] && [ -f "$SCRIPT_DIR/bin/deploy.sh" ]; then - # Running from an existing clone + # Running from an existing checkout/snapshot REPO_DIR="$SCRIPT_DIR" - info "Using existing clone: $REPO_DIR" + info "Using existing source: $REPO_DIR" else - # Need to clone + # Need to locate or clone source if [ -d "$REPO_DIR/.git" ]; then - info "Repo already exists at $REPO_DIR, pulling latest..." - sudo -u "$ADMIN_USER" git -C "$REPO_DIR" pull --ff-only 2>&1 | tail -1 + if sudo -u "$ADMIN_USER" git -C "$REPO_DIR" remote get-url origin >/dev/null 2>&1; then + info "Repo already exists at $REPO_DIR, pulling latest..." + sudo -u "$ADMIN_USER" git -C "$REPO_DIR" pull --ff-only 2>&1 | tail -1 + elif [ -f "$REPO_DIR/setup.sh" ] && [ -f "$REPO_DIR/bin/deploy.sh" ]; then + info "Repo exists at $REPO_DIR (no origin remote), using local source snapshot" + else + die "Repo at $REPO_DIR has no origin and missing setup files" + fi + elif [ -f "$REPO_DIR/setup.sh" ] && [ -f "$REPO_DIR/bin/deploy.sh" ]; then + info "Using local source snapshot: $REPO_DIR" else REPO_URL="https://github.com/modem-dev/baudbot.git" info "Cloning $REPO_URL → $REPO_DIR"