#!/usr/bin/env bash # connect.sh — join a hack-house room without leaving the password on disk. # # The password lives only in RAM: it is read into a shell variable (never a # file), and the interactive prompt keeps it out of your shell history. Supply # it three ways, most to least private: # 1) interactive (recommended): ./connect.sh alice 100.117.177.50 # → prompts "room password:" with no echo # 2) environment: HH_PASSWORD=secret ./connect.sh alice # 3) flag: ./connect.sh alice -p secret # # Caveat: however it arrives, the client receives the password as a CLI argument, # so it is briefly visible in the process list (ps) to other *local* users for # the lifetime of the session. Nothing is ever written to disk. # # Usage: ./connect.sh [NAME] [HOST] [-p PASSWORD] [-P PORT] [--tls] [--insecure] [--sync] [--no-build] # NAME display handle; omit to be prompted for one on join # HOST server IP/host (default: 127.0.0.1) # -P port (default: 4173, or $HH_PORT) # --tls use wss/https instead of the default plaintext-over-Tailscale # --sync git-pull the latest code (gitea then origin, ff-only) before building # --no-build run the prebuilt binary as-is (skip the fresh debug rebuild) set -euo pipefail cd "$(dirname "$0")/.." DEFAULT_PORT=4173 DEFAULT_HOST=127.0.0.1 NAME="" HOST="" PORT="${HH_PORT:-$DEFAULT_PORT}" PASSWORD="${HH_PASSWORD:-}" NO_TLS=1 # rooms run --no-tls over Tailscale/LAN by default INSECURE=0 SYNC=0 # opt-in: pull latest code before building (handy for demos/dev) NO_BUILD=0 # rebuild a fresh debug binary first so the UI is never stale while [[ $# -gt 0 ]]; do case "$1" in -p|--password) PASSWORD="$2"; shift 2 ;; -P|--port) PORT="$2"; shift 2 ;; --tls) NO_TLS=0; shift ;; --insecure) INSECURE=1; shift ;; --sync) SYNC=1; shift ;; --no-build) NO_BUILD=1; shift ;; -h|--help) sed -n '2,/^set /{/^set /d;s/^# \{0,1\}//;p}' "$0"; exit 0 ;; -*) echo "✖ unknown option: $1" >&2; exit 2 ;; *) if [[ -z "$NAME" ]]; then NAME="$1" elif [[ -z "$HOST" ]]; then HOST="$1" else echo "✖ unexpected argument: $1" >&2; exit 2; fi shift ;; esac done HOST="${HOST:-$DEFAULT_HOST}" # No password yet? Prompt with no echo — straight into RAM, out of history. if [[ -z "$PASSWORD" ]]; then read -rsp "⛧ room password: " PASSWORD < /dev/tty echo fi [[ -n "$PASSWORD" ]] || { echo "✖ a password is required" >&2; exit 1; } # --sync: pull the latest code before building so a fresh join runs current # source. Pulls are best-effort and fast-forward only — an unreachable remote or # diverged history just warns and is skipped, never blocking the join. Each pull # is capped (timeout) and GIT_TERMINAL_PROMPT=0 stops it hanging on credentials. if [[ "$SYNC" -eq 1 ]]; then BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo HEAD)" TO=""; command -v timeout >/dev/null 2>&1 && TO="timeout 10" for remote in gitea origin; do if git remote get-url "$remote" >/dev/null 2>&1; then echo "⛧ syncing $BRANCH from $remote…" >&2 GIT_TERMINAL_PROMPT=0 $TO git pull --ff-only "$remote" "$BRANCH" 2>&1 \ || echo " (skipped — couldn't fast-forward from $remote/$BRANCH)" >&2 fi done fi # Build a fresh debug binary so the UI always matches the current source — an # incremental build is ~instant when nothing changed. --no-build skips this and # runs a prebuilt binary as-is (handy for remote joiners without a toolchain), # preferring release, then debug. if [[ "$NO_BUILD" -eq 0 ]]; then echo "⛧ building client (use --no-build to skip)…" >&2 cargo build --quiet || { echo "✖ build failed" >&2; exit 1; } BIN=./target/debug/hack-house else BIN=./target/release/hack-house [[ -x "$BIN" ]] || BIN=./target/debug/hack-house fi [[ -x "$BIN" ]] || { echo "✖ no hack-house binary — run: cargo build" >&2; exit 1; } args=(connect "$HOST" "$PORT") [[ -n "$NAME" ]] && args+=("$NAME") # omit → client prompts for a handle args+=(--password "$PASSWORD") [[ "$NO_TLS" -eq 1 ]] && args+=(--no-tls) [[ "$INSECURE" -eq 1 ]] && args+=(--insecure) # exec replaces this shell, so our $PASSWORD copy dies here; only the child holds # it (via argv), and we never exported it into the child's environment. exec "$BIN" "${args[@]}"