Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,10 @@ Baudbot is designed as shared engineering infrastructure, not a single-user desk
## Quick Start

```bash
git clone https://github.com/modem-dev/baudbot.git ~/baudbot
sudo ~/baudbot/install.sh
curl -fsSL https://raw.githubusercontent.com/modem-dev/baudbot/main/bootstrap.sh | bash
baudbot install
```

Installer handles setup, dependencies, agent user creation, firewall setup, and initial configuration prompts.

After install:

```bash
Expand Down
72 changes: 72 additions & 0 deletions bin/baudbot
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 ]
Expand Down Expand Up @@ -129,6 +196,11 @@ print_deployed_version() {
}

case "${1:-}" in
install)
shift
bootstrap_install "$@"
;;

start)
shift
if [ "${1:-}" = "--direct" ]; then
Expand Down
11 changes: 8 additions & 3 deletions bin/ci/setup-arch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
11 changes: 8 additions & 3 deletions bin/ci/setup-ubuntu.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
85 changes: 85 additions & 0 deletions bootstrap.sh
Original file line number Diff line number Diff line change
@@ -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"
26 changes: 19 additions & 7 deletions install.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
#!/bin/bash
# Baudbot Interactive Installer
#
# One-command setup:
# One-command setup (bootstrap CLI):
# 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
#
# Or if already cloned:
Expand Down Expand Up @@ -143,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"
Expand Down