#!/usr/bin/env bash # Throwaway 6krrt router instance on 8081, for exercising the admin portal # against real data without touching production. # # sandbox.sh start [worktree] start it, wait for /health, print the pid # sandbox.sh stop kill the instance this script started # sandbox.sh status is it up, and on what pid # # 8080 is production and 8081 is the throwaway, per CLAUDE.md. This script # refuses to bind 8080 at all, and only ever kills a pid it recorded itself # -- never one matched by name or port, because Restart=always will fight # you and on this machine that process is the user's model access. set -euo pipefail REPO=/home/alee/Sources/6krrt PORT=8081 PIDFILE="${TMPDIR:-/tmp}/6krrt-sandbox-${PORT}.pid" LOGFILE="${TMPDIR:-/tmp}/6krrt-sandbox-${PORT}.log" usage() { sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//'; exit 2; } start() { local worktree="${1:-$PWD}" [ -d "$worktree/src" ] || { echo "no src/ under $worktree" >&2; exit 1; } if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then echo "already running, pid $(cat "$PIDFILE")"; exit 0 fi # cwd decides config/config.yaml and the relative router.db; PYTHONPATH # decides which src/ and which admin/frontend/*.html get served. Pointing # both at the worktree keeps every write inside it. # # Backgrounded directly rather than inside `( ... & echo $! )`. Two bugs # came out of the subshell form, both found by running it: # - `sandbox.sh start | tail` hung until the SERVER exited, because the # child inherited the script's stdout and held the pipe open. Hence # "$LOGFILE" 2>&1 & local pid=$! echo "$pid" >"$PIDFILE" if ! curl -s -m 30 --retry 20 --retry-delay 1 --retry-connrefused \ -o /dev/null "http://127.0.0.1:$PORT/health"; then echo "failed to come up; last log lines:" >&2 tail -20 "$LOGFILE" >&2 exit 1 fi # Prove the recorded pid is the process actually holding the port. Without # this the two bugs above were silent: everything printed success while # stop/status operated on a pid that was never the server. local owner owner="$(ss -ltnpH "sport = :$PORT" 2>/dev/null | grep -o 'pid=[0-9]*' | head -1 | cut -d= -f2)" if [ -n "$owner" ] && [ "$owner" != "$pid" ]; then echo "warning: recorded pid $pid but :$PORT is held by $owner; recording $owner" >&2 pid="$owner" echo "$pid" >"$PIDFILE" fi echo "up on $PORT, pid $pid, log $LOGFILE" } stop() { [ -f "$PIDFILE" ] || { echo "no pidfile; nothing this script started"; exit 0; } local pid; pid="$(cat "$PIDFILE")" kill "$pid" 2>/dev/null && echo "stopped pid $pid" || echo "pid $pid already gone" rm -f "$PIDFILE" } status() { if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null; then echo "running, pid $(cat "$PIDFILE")" else echo "not running" fi curl -s -m 5 -o /dev/null -w "health on $PORT: %{http_code}\n" \ "http://127.0.0.1:$PORT/health" || true } case "${1:-}" in start) shift; start "$@" ;; stop) stop ;; status) status ;; *) usage ;; esac