#!/usr/bin/env bash # Phase 6 + 7 smoke test: dashboard HTTP, per-container web UIs, # approve-by-POST, SSE endpoint, and orphan-approval GC. # # Runs as root on a host with services.hive-c0re enabled and the hm1nd # container declared. Idempotent — wipes any prior alice state. set -euo pipefail AGENT=alice PKG=htop HOST=${HOST:-localhost} DASH_PORT=${DASH_PORT:-7000} MGR_PORT=${MGR_PORT:-8000} cleanup() { sudo hive-c0re kill "$AGENT" 2>/dev/null || true sudo nixos-container destroy "h-${AGENT}" 2>/dev/null || true sudo rm -rf \ "/var/lib/hyperhive/agents/${AGENT}" \ "/var/lib/hyperhive/applied/${AGENT}" } echo "=== precheck: hm1nd container is up ===" if ! sudo machinectl status hm1nd >/dev/null 2>&1; then echo " starting via systemd..." sudo systemctl start container@hm1nd.service for i in 1 2 3 4 5 6 7 8 9 10; do sudo machinectl status hm1nd >/dev/null 2>&1 && break sleep 0.5 done fi echo " ✓ hm1nd is up" cleanup echo "=== dashboard reachable ===" curl -sf "http://${HOST}:${DASH_PORT}/" | grep -q "HYPERHIVE" || { echo "FAIL: dashboard not serving expected content" exit 1 } echo " ✓ http://${HOST}:${DASH_PORT}/ → HTML with HYPERHIVE banner" echo "=== /messages/stream SSE endpoint ===" curl -sI "http://${HOST}:${DASH_PORT}/messages/stream" \ | grep -qi "content-type: text/event-stream" || { echo "FAIL: /messages/stream is not SSE" exit 1 } echo " ✓ Content-Type: text/event-stream" echo "=== manager UI reachable ===" curl -sf "http://${HOST}:${MGR_PORT}/" | grep -q "hm1nd" || { echo "FAIL: manager UI not reachable at :${MGR_PORT}" exit 1 } echo " ✓ http://${HOST}:${MGR_PORT}/ → hm1nd label" echo "=== spawn ${AGENT} ===" sudo hive-c0re spawn "$AGENT" echo "=== pick up ${AGENT}'s port from dashboard ===" sleep 2 ALICE_PORT=$(curl -sf "http://${HOST}:${DASH_PORT}/" \ | sed -nE "s|.*href=\"http://[^:]+:([0-9]+)/\">${AGENT}<.*|\\1|p" \ | head -1) if [ -z "$ALICE_PORT" ]; then echo "FAIL: ${AGENT} port not found in dashboard" exit 1 fi echo " ✓ ${AGENT} port: ${ALICE_PORT}" echo "=== ${AGENT} UI reachable ===" for i in 1 2 3 4 5 6 7 8 9 10; do curl -sf "http://${HOST}:${ALICE_PORT}/" | grep -q "${AGENT}" && break sleep 1 done curl -sf "http://${HOST}:${ALICE_PORT}/" | grep -q "${AGENT}" || { echo "FAIL: ${AGENT} UI not reachable at :${ALICE_PORT}" exit 1 } echo " ✓ http://${HOST}:${ALICE_PORT}/ → ${AGENT} label" echo "=== ${PKG} not in path pre-approve ===" if sudo nixos-container run "h-${AGENT}" -- which "$PKG" 2>/dev/null; then echo "FAIL: ${PKG} already in path" exit 1 fi echo " ✓" echo "=== manager submits approval (via hm1nd) ===" sudo nixos-container run hm1nd -- bash -c " set -euo pipefail cd /agents/${AGENT}/config cat > agent.nix <<'EOF' { pkgs, ... }: { environment.systemPackages = [ pkgs.${PKG} ]; } EOF git commit -am 'add ${PKG}' SHA=\$(git rev-parse HEAD) echo \" manager commit SHA=\$SHA\" hive-m1nd request-apply-commit ${AGENT} \$SHA " echo "=== pick approval id from dashboard ===" ID=$(curl -sf "http://${HOST}:${DASH_PORT}/" \ | sed -nE 's|.*hive-c0re approve ([0-9]+).*|\1|p' \ | head -1) if [ -z "$ID" ]; then echo "FAIL: approval id not found in dashboard" exit 1 fi echo " ✓ approval id: ${ID}" echo "=== POST /approve/${ID} (browser button equivalent) ===" HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' \ -X POST "http://${HOST}:${DASH_PORT}/approve/${ID}") if [ "$HTTP_CODE" != "303" ] && [ "$HTTP_CODE" != "302" ]; then echo "FAIL: expected 303 redirect, got HTTP ${HTTP_CODE}" exit 1 fi echo " ✓ HTTP ${HTTP_CODE} (redirect)" echo "=== verify ${PKG} now in h-${AGENT} ===" sudo nixos-container run "h-${AGENT}" -- which "$PKG" echo "=== orphan-GC: cleanup ${AGENT}, refresh dashboard, orphan disappears ===" sudo nixos-container run hm1nd -- bash -c " set -euo pipefail cd /agents/${AGENT}/config echo '{ ... }: { }' > agent.nix git commit -am 'noop' SHA=\$(git rev-parse HEAD) hive-m1nd request-apply-commit ${AGENT} \$SHA " ORPHAN_ID=$(sudo hive-c0re pending \ | sed -nE 's/^[[:space:]]*"id":[[:space:]]*([0-9]+).*/\1/p' \ | tail -1) echo " queued orphan id: ${ORPHAN_ID}" cleanup # First dashboard render triggers GC. curl -sf "http://${HOST}:${DASH_PORT}/" >/dev/null sleep 0.5 if sudo hive-c0re pending | grep -q "\"id\": ${ORPHAN_ID}"; then echo "FAIL: orphan ${ORPHAN_ID} still pending after dashboard render" exit 1 fi echo " ✓ orphan ${ORPHAN_ID} cleaned up" echo echo "=== summary ===" echo " dashboard http://${HOST}:${DASH_PORT}/" echo " manager UI http://${HOST}:${MGR_PORT}/" echo " (alice torn down)"