#!/usr/bin/env bash
# HyperStorageNode bridge — M0 + M1 end-to-end.
# full : live append + replay round-trip over a real Hypercore
# verify : restart the daemon on the SAME Hypercore dir, replay -> GOLD #1
# (durable deterministic replay)
# fork : checkout at seq K (read fewer blocks -> earlier graph) -> GOLD #2
# Exits 0 iff all three pass.
set -euo pipefail
cd "$(dirname "$0")"
# Short paths: AF_UNIX sun_path caps at ~108 bytes.
WORK="$(mktemp -d /tmp/m1-XXXXXX)"
SOCK="$WORK/svc.sock"
STORE1="$WORK/store1"
STORE2="$WORK/store2"
SA="$WORK/a.sock"; SB="$WORK/b.sock"; PEER="$WORK/peer.sock"
DPID=""; PA=""; PB=""
cleanup() {
# `bare` is an nvm shim that execs a child runtime; kill the shims AND match
# the daemons by their unique socket paths.
for p in "$DPID" "$PA" "$PB"; do [ -n "$p" ] && kill -9 "$p" 2>/dev/null || true; done
pkill -9 -f "daemon.js $SOCK" 2>/dev/null || true
pkill -9 -f "daemon-ab.js $SA" 2>/dev/null || true
pkill -9 -f "daemon-ab.js $SB" 2>/dev/null || true
rm -rf "$WORK"
}
trap cleanup EXIT
start_daemon() { # $1=storage-dir
bare daemon.js "$SOCK" "$1" > "$WORK/d.log" 2>&1 &
DPID=$!
for _ in $(seq 1 100); do
grep -q listening "$WORK/d.log" && return 0
sleep 0.05
done
echo "daemon failed to start:"; cat "$WORK/d.log"; return 1
}
stop_daemon() {
[ -n "$DPID" ] && kill -9 "$DPID" 2>/dev/null || true
pkill -9 -f "daemon.js $SOCK" 2>/dev/null || true
DPID=""
sleep 0.2 # let the core close and the fd release before rebind
}
# deps + build
[ -d node_modules/autobase ] || npm install --silent
g++ -std=gnu++20 -O2 -I/usr/local/include m0_roundtrip.cc \
-L/usr/local/lib/opencog -Wl,-rpath,/usr/local/lib/opencog \
-latomspace -lsexpr -latombase -latom_types \
-o m0_roundtrip
# Phase 1 — live append + replay onto Hypercore STORE1
start_daemon "$STORE1"
./m0_roundtrip "$SOCK" full
stop_daemon
# Phase 2 — restart on the SAME store, replay only (durable determinism, GOLD #1)
start_daemon "$STORE1"
./m0_roundtrip "$SOCK" verify
stop_daemon
# Phase 3 — fresh store, fork/checkout at seq K (GOLD #2)
start_daemon "$STORE2"
./m0_roundtrip "$SOCK" fork
stop_daemon
# Phase 4 — fresh store, causal lineage of a high-level atom (GOLD #3)
start_daemon "$WORK/store3"
./m0_roundtrip "$SOCK" lineage
stop_daemon
# Phase 5 — M4: two Autobase daemons (agents) converge to one linearized log.
bare daemon-ab.js "$SA" "$WORK/abA" --peer-listen "$PEER" > "$WORK/a.log" 2>&1 &
PA=$!
for _ in $(seq 1 100); do grep -q "AB key=" "$WORK/a.log" && break; sleep 0.05; done
AKEY=$(sed -n 's/.*AB key=\([0-9a-f]*\).*/\1/p' "$WORK/a.log")
[ -n "$AKEY" ] || { echo "daemon A failed:"; cat "$WORK/a.log"; exit 1; }
bare daemon-ab.js "$SB" "$WORK/abB" --bootstrap "$AKEY" --peer-connect "$PEER" > "$WORK/b.log" 2>&1 &
PB=$!
for _ in $(seq 1 100); do grep -q "AB key=" "$WORK/b.log" && break; sleep 0.05; done
BLOCAL=$(sed -n 's/.*local=\([0-9a-f]*\).*/\1/p' "$WORK/b.log")
[ -n "$BLOCAL" ] || { echo "daemon B failed:"; cat "$WORK/b.log"; exit 1; }
# Pattern-B ceremony: A (the writer) addWriters B's announced local key.
bare abctl.js "$SA" "addwriter $BLOCAL" >/dev/null
for _ in $(seq 1 60); do
[ "$(bare abctl.js "$SB" writable)" = "true" ] && break
sleep 0.1
done
./m0_roundtrip "$SA" m4 "$SB"
for p in "$PA" "$PB"; do kill -9 "$p" 2>/dev/null || true; done
pkill -9 -f "daemon-ab.js $SA" 2>/dev/null || true
pkill -9 -f "daemon-ab.js $SB" 2>/dev/null || true
PA=""; PB=""
echo "M0-M4 ALL PASS"