diff --git a/verify b/verify index 1b868238..e0f0a6ef 100755 --- a/verify +++ b/verify @@ -1,19 +1,31 @@ #!/usr/bin/env bash # ====================================================================== -# WiFi-DensePose: Trust Kill Switch +# WiFi-DensePose / RuView — Trust Kill Switch # -# One-command proof replay that makes "it is mocked" a falsifiable, -# measurable claim that fails against evidence. +# One-command proof replay across every layer of the stack: +# 1. Python signal-processing pipeline (the original v1 proof) +# 2. Production-code mock scan (np.random.rand/randn in non-test paths) +# 3. Rust workspace tests (cargo test --workspace --no-default-features) +# 4. PyO3 BFLD binding (cargo check -p wifi-densepose-py) +# 5. ADR-125 §2.1.d invariant — identity_risk_score never crosses +# 6. Published crates.io tarball SHAs +# 7. Published npm packages +# 8. Published Docker image multi-arch manifest +# 9. Embedded HOMECORE binary in the Docker image (homecore-server) # # Usage: -# ./verify Run the full proof pipeline -# ./verify --verbose Show detailed feature statistics -# ./verify --audit Also scan codebase for mock/random patterns +# ./verify Run every phase. +# ./verify --quick Skip slow phases (cargo test, docker pull). +# ./verify --rust-only Only the Rust workspace test phase. +# ./verify --docker-only Only the Docker manifest + binary phase. +# ./verify --verbose Show detailed feature stats in the Python proof. +# ./verify --audit Also scan codebase for mock/random patterns. +# ./verify --generate-hash Regenerate the v1 expected hash (rare). # # Exit codes: -# 0 PASS -- pipeline hash matches published expected hash -# 1 FAIL -- hash mismatch or error -# 2 SKIP -- no expected hash file to compare against +# 0 ALL PHASES PASS (or SKIP gracefully when optional deps missing) +# 1 Any phase that ran returned FAIL +# 2 Phase 1 was forced to SKIP (no expected hash file) # ====================================================================== set -euo pipefail @@ -22,199 +34,296 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROOF_DIR="${SCRIPT_DIR}/archive/v1/data/proof" VERIFY_PY="${PROOF_DIR}/verify.py" V1_SRC="${SCRIPT_DIR}/archive/v1/src" +V2_DIR="${SCRIPT_DIR}/v2" +PY_DIR="${SCRIPT_DIR}/python" -# Colors (disabled if not a terminal) -if [ -t 1 ]; then - RED='\033[0;31m' - GREEN='\033[0;32m' - YELLOW='\033[1;33m' - CYAN='\033[0;36m' - BOLD='\033[1m' - RESET='\033[0m' -else - RED='' - GREEN='' - YELLOW='' - CYAN='' - BOLD='' - RESET='' +# Phase toggles (set via flags) +RUN_PYTHON=1 +RUN_SCAN=1 +RUN_RUST=1 +RUN_PYO3=1 +RUN_INVARIANT=1 +RUN_CRATES=1 +RUN_NPM=1 +RUN_DOCKER=1 +RUN_HOMECORE=1 + +QUICK=0 +VERBOSE_FLAGS=() +EXIT_CODE=0 +declare -a SUMMARY +declare -a EXTRA_ARGS + +for arg in "$@"; do + case "$arg" in + --quick) QUICK=1 ;; + --rust-only) RUN_PYTHON=0; RUN_SCAN=0; RUN_PYO3=0; RUN_INVARIANT=0; RUN_CRATES=0; RUN_NPM=0; RUN_DOCKER=0; RUN_HOMECORE=0 ;; + --docker-only) RUN_PYTHON=0; RUN_SCAN=0; RUN_RUST=0; RUN_PYO3=0; RUN_INVARIANT=0; RUN_CRATES=0; RUN_NPM=0 ;; + --verbose|--audit|--generate-hash) EXTRA_ARGS+=("$arg") ;; + -h|--help) + sed -n '2,30p' "$0"; exit 0 ;; + *) echo "unknown flag: $arg" >&2; exit 2 ;; + esac +done +if [ $QUICK -eq 1 ]; then + RUN_RUST=0 + RUN_DOCKER=0 fi +# Colors (no-op without TTY) +if [ -t 1 ]; then + RED=$'\033[0;31m'; GREEN=$'\033[0;32m'; YELLOW=$'\033[1;33m' + CYAN=$'\033[0;36m'; BOLD=$'\033[1m'; RESET=$'\033[0m' +else + RED=''; GREEN=''; YELLOW=''; CYAN=''; BOLD=''; RESET='' +fi + +note_pass() { SUMMARY+=("${GREEN}PASS${RESET} $1"); } +note_fail() { SUMMARY+=("${RED}FAIL${RESET} $1"); EXIT_CODE=1; } +note_skip() { SUMMARY+=("${YELLOW}SKIP${RESET} $1"); } + +phase() { echo ""; echo -e "${CYAN}[PHASE $1] $2${RESET}"; echo ""; } + echo "" echo -e "${BOLD}======================================================================" -echo " WiFi-DensePose: Trust Kill Switch" -echo " One-command proof that the signal processing pipeline is real." +echo " WiFi-DensePose / RuView — Trust Kill Switch (multi-layer proof)" echo -e "======================================================================${RESET}" -echo "" + +PYTHON="$(command -v python3 || command -v python || true)" +[ -z "$PYTHON" ] && { echo -e "${RED}python3 not found — install Python 3${RESET}"; exit 1; } +$PYTHON --version >/dev/null 2>&1 || { echo "python broken"; exit 1; } +echo " python: $($PYTHON --version 2>&1)" +echo " repo: $SCRIPT_DIR" +git_head="$(cd "$SCRIPT_DIR" && git rev-parse --short HEAD 2>/dev/null || echo unknown)" +echo " HEAD: $git_head" # ------------------------------------------------------------------ -# PHASE 1: Environment checks +# PHASE 1: Python signal-processing proof pipeline (the original) # ------------------------------------------------------------------ -echo -e "${CYAN}[PHASE 1] ENVIRONMENT CHECKS${RESET}" -echo "" - -ERRORS=0 - -# Check Python -if command -v python3 &>/dev/null; then - PYTHON=python3 -elif command -v python &>/dev/null; then - PYTHON=python -else - echo -e " ${RED}FAIL${RESET}: Python 3 not found. Install python3." - exit 1 -fi - -PY_VERSION=$($PYTHON --version 2>&1) -echo " Python: $PY_VERSION ($( command -v $PYTHON ))" - -# Check numpy -if $PYTHON -c "import numpy; print(f' numpy: {numpy.__version__} ({numpy.__file__})')" 2>/dev/null; then - : -else - echo -e " ${RED}FAIL${RESET}: numpy not installed. Run: pip install numpy" - ERRORS=$((ERRORS + 1)) -fi - -# Check scipy -if $PYTHON -c "import scipy; print(f' scipy: {scipy.__version__} ({scipy.__file__})')" 2>/dev/null; then - : -else - echo -e " ${RED}FAIL${RESET}: scipy not installed. Run: pip install scipy" - ERRORS=$((ERRORS + 1)) -fi - -# Check proof files exist -echo "" -if [ -f "${PROOF_DIR}/sample_csi_data.json" ]; then - SIZE=$(wc -c < "${PROOF_DIR}/sample_csi_data.json" | tr -d ' ') - echo " Reference signal: sample_csi_data.json (${SIZE} bytes)" -else - echo -e " ${RED}FAIL${RESET}: Reference signal not found at ${PROOF_DIR}/sample_csi_data.json" - ERRORS=$((ERRORS + 1)) -fi - -if [ -f "${PROOF_DIR}/expected_features.sha256" ]; then - EXPECTED=$(cat "${PROOF_DIR}/expected_features.sha256" | tr -d '[:space:]') - echo " Expected hash: ${EXPECTED}" -else - echo -e " ${YELLOW}WARN${RESET}: No expected hash file found" -fi - -if [ -f "${VERIFY_PY}" ]; then - echo " Verify script: ${VERIFY_PY}" -else - echo -e " ${RED}FAIL${RESET}: verify.py not found at ${VERIFY_PY}" - ERRORS=$((ERRORS + 1)) -fi - -echo "" - -if [ $ERRORS -gt 0 ]; then - echo -e "${RED}Cannot proceed: $ERRORS prerequisite(s) missing.${RESET}" - exit 1 -fi - -echo -e " ${GREEN}All prerequisites satisfied.${RESET}" -echo "" - -# ------------------------------------------------------------------ -# PHASE 2: Run the proof pipeline -# ------------------------------------------------------------------ -echo -e "${CYAN}[PHASE 2] PROOF PIPELINE REPLAY${RESET}" -echo "" - -# Pass through any flags (--verbose, --audit, --generate-hash) -PIPELINE_EXIT=0 -$PYTHON "${VERIFY_PY}" "$@" || PIPELINE_EXIT=$? - -echo "" - -# ------------------------------------------------------------------ -# PHASE 3: Mock/random scan of production codebase -# ------------------------------------------------------------------ -echo -e "${CYAN}[PHASE 3] PRODUCTION CODE INTEGRITY SCAN${RESET}" -echo "" -echo " Scanning ${V1_SRC} for np.random.rand / np.random.randn calls..." -echo " (Excluding v1/src/testing/ -- test helpers are allowed to use random.)" -echo "" - -MOCK_FINDINGS=0 - -# Scan for np.random.rand and np.random.randn in production code -# We exclude testing/ directories -while IFS= read -r line; do - if [ -n "$line" ]; then - echo -e " ${YELLOW}FOUND${RESET}: $line" - MOCK_FINDINGS=$((MOCK_FINDINGS + 1)) +if [ $RUN_PYTHON -eq 1 ]; then + phase 1 "Python signal-processing pipeline (SHA-256 round-trip)" + if [ -f "$VERIFY_PY" ] && [ -f "$PROOF_DIR/sample_csi_data.json" ]; then + $PYTHON -c "import numpy, scipy" 2>/dev/null \ + || { echo -e " ${RED}numpy or scipy missing — pip install numpy scipy${RESET}"; note_skip "Phase 1: missing numpy/scipy"; } + if $PYTHON -c "import numpy, scipy" 2>/dev/null; then + P1_EXIT=0 + $PYTHON "$VERIFY_PY" "${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"}" || P1_EXIT=$? + case $P1_EXIT in + 0) note_pass "Phase 1: v1 pipeline hash matches expected" ;; + 2) note_skip "Phase 1: no expected hash file"; [ $EXIT_CODE -eq 0 ] && EXIT_CODE=2 ;; + *) note_fail "Phase 1: v1 pipeline hash mismatch (exit $P1_EXIT)" ;; + esac + fi + else + note_skip "Phase 1: verify.py or reference signal not present" fi -done < <( - find "${V1_SRC}" -name "*.py" -type f \ - ! -path "*/testing/*" \ - ! -path "*/tests/*" \ - ! -path "*/test/*" \ - ! -path "*__pycache__*" \ - -exec grep -Hn 'np\.random\.rand\b\|np\.random\.randn\b' {} \; 2>/dev/null || true -) - -if [ $MOCK_FINDINGS -eq 0 ]; then - echo -e " ${GREEN}CLEAN${RESET}: No np.random.rand/randn calls in production code." -else - echo "" - echo -e " ${YELLOW}WARNING${RESET}: Found ${MOCK_FINDINGS} random generator call(s) in production code." - echo " These should be reviewed -- production signal processing should" - echo " never generate random data." fi -echo "" +# ------------------------------------------------------------------ +# PHASE 2: Production code mock-pattern scan +# ------------------------------------------------------------------ +if [ $RUN_SCAN -eq 1 ]; then + phase 2 "Production-code mock scan (np.random.rand / np.random.randn)" + if [ -d "$V1_SRC" ]; then + findings=0 + while IFS= read -r line; do + [ -n "$line" ] && { echo -e " ${YELLOW}FOUND${RESET}: $line"; findings=$((findings + 1)); } + done < <( + find "$V1_SRC" -name "*.py" -type f \ + ! -path "*/testing/*" ! -path "*/tests/*" ! -path "*/test/*" ! -path "*__pycache__*" \ + -exec grep -Hn 'np\.random\.rand\b\|np\.random\.randn\b' {} \; 2>/dev/null || true + ) + if [ "$findings" -eq 0 ]; then + note_pass "Phase 2: no random generators in production code" + else + note_fail "Phase 2: $findings random-generator call(s) in production code" + fi + else + note_skip "Phase 2: archive/v1/src not present" + fi +fi + +# ------------------------------------------------------------------ +# PHASE 3: Rust workspace tests +# ------------------------------------------------------------------ +if [ $RUN_RUST -eq 1 ]; then + phase 3 "Rust workspace tests (cargo test --workspace --no-default-features)" + if command -v cargo >/dev/null 2>&1 && [ -d "$V2_DIR" ]; then + echo " Running (may take ~2-3 minutes; pass --quick to skip)..." + rust_out="$(cd "$V2_DIR" && cargo test --workspace --no-default-features --quiet 2>&1)" || P3_EXIT=$? + passed=$(echo "$rust_out" | grep -oE 'test result: ok\. [0-9]+ passed' \ + | awk '{sum += $4} END {print sum+0}') + failed=$(echo "$rust_out" | grep -oE 'test result: FAILED\. [0-9]+ passed; [0-9]+ failed' \ + | awk '{sum += $5} END {print sum+0}') + if [ "${P3_EXIT:-0}" -eq 0 ] && [ "${failed:-0}" -eq 0 ] && [ "${passed:-0}" -gt 0 ]; then + note_pass "Phase 3: $passed Rust tests passed, 0 failed" + else + echo "$rust_out" | tail -20 + note_fail "Phase 3: Rust workspace tests failed (passed=$passed failed=$failed)" + fi + else + note_skip "Phase 3: cargo or v2/ not present" + fi +fi + +# ------------------------------------------------------------------ +# PHASE 4: PyO3 BFLD binding compiles +# ------------------------------------------------------------------ +if [ $RUN_PYO3 -eq 1 ]; then + phase 4 "PyO3 BFLD binding (cargo check -p wifi-densepose-py)" + if command -v cargo >/dev/null 2>&1 && [ -f "$PY_DIR/Cargo.toml" ]; then + if (cd "$PY_DIR" && cargo check --quiet 2>&1 | tail -10); then + note_pass "Phase 4: wifi-densepose-py compiles cleanly" + else + note_fail "Phase 4: wifi-densepose-py cargo check failed" + fi + else + note_skip "Phase 4: cargo or python/ not present" + fi +fi + +# ------------------------------------------------------------------ +# PHASE 5: ADR-125 §2.1.d invariant — identity_risk_score never crosses +# ------------------------------------------------------------------ +if [ $RUN_INVARIANT -eq 1 ]; then + phase 5 "ADR-125 §2.1.d invariant — identity_risk_score never crosses HAP/MCP boundary" + bad=0 + for f in scripts/ruview-sensing-server.py scripts/c6-presence-watcher.py; do + if [ -f "$SCRIPT_DIR/$f" ]; then + # Each file must set identity_risk_score to None / null somewhere + if ! grep -q '"identity_risk_score": None\|"identity_risk_score":None\|identity_risk_score=None' "$SCRIPT_DIR/$f" 2>/dev/null; then + # Only flag the sensing-server (the watcher uses it differently) + [ "$f" = "scripts/ruview-sensing-server.py" ] && { echo " $f missing identity_risk_score=None"; bad=$((bad+1)); } + fi + # Nothing must publish a non-None identity_risk_score + if grep -E '"identity_risk_score":\s*[0-9]' "$SCRIPT_DIR/$f" 2>/dev/null; then + echo " $f leaks a numeric identity_risk_score" + bad=$((bad+1)) + fi + fi + done + if [ "$bad" -eq 0 ]; then + note_pass "Phase 5: identity_risk_score is None at every gateway script" + else + note_fail "Phase 5: $bad invariant violation(s)" + fi +fi + +# ------------------------------------------------------------------ +# PHASE 6: Published crates.io packages +# ------------------------------------------------------------------ +if [ $RUN_CRATES -eq 1 ]; then + phase 6 "Published crates.io packages" + if command -v curl >/dev/null 2>&1; then + crates_expected=( "wifi-densepose-core" "wifi-densepose-signal" \ + "wifi-densepose-sensing-server" "wifi-densepose-hardware" \ + "wifi-densepose-nn" "wifi-densepose-bfld" "wifi-densepose-vitals" \ + "wifi-densepose-wifiscan" "wifi-densepose-train" \ + "cog-ha-matter" "cog-person-count" "cog-pose-estimation" ) + ok=0; miss=0 + for crate in "${crates_expected[@]}"; do + ver=$(curl -sf "https://crates.io/api/v1/crates/$crate" 2>/dev/null \ + | $PYTHON -c 'import sys,json; print(json.load(sys.stdin).get("crate",{}).get("max_version","?"))' 2>/dev/null) || ver="" + if [ -n "$ver" ] && [ "$ver" != "?" ]; then + echo " $crate $ver" + ok=$((ok+1)) + else + echo -e " ${YELLOW}miss${RESET} $crate" + miss=$((miss+1)) + fi + done + if [ "$miss" -eq 0 ]; then + note_pass "Phase 6: $ok/$ok crates on crates.io" + else + note_fail "Phase 6: $miss of ${#crates_expected[@]} crates missing" + fi + else + note_skip "Phase 6: curl not available" + fi +fi + +# ------------------------------------------------------------------ +# PHASE 7: Published npm packages +# ------------------------------------------------------------------ +if [ $RUN_NPM -eq 1 ]; then + phase 7 "Published npm packages (@ruvnet/rvagent)" + if command -v curl >/dev/null 2>&1; then + ver=$(curl -sf "https://registry.npmjs.org/@ruvnet/rvagent" 2>/dev/null \ + | $PYTHON -c 'import sys,json; print(json.load(sys.stdin).get("dist-tags",{}).get("latest","?"))' 2>/dev/null) || ver="" + if [ -n "$ver" ] && [ "$ver" != "?" ]; then + echo " @ruvnet/rvagent $ver" + note_pass "Phase 7: @ruvnet/rvagent v$ver on npm" + else + note_fail "Phase 7: @ruvnet/rvagent not on registry" + fi + else + note_skip "Phase 7: curl not available" + fi +fi + +# ------------------------------------------------------------------ +# PHASE 8: Docker Hub multi-arch manifest +# ------------------------------------------------------------------ +if [ $RUN_DOCKER -eq 1 ]; then + phase 8 "Docker Hub multi-arch manifest (ruvnet/wifi-densepose:latest)" + if command -v docker >/dev/null 2>&1; then + manifest="$(docker manifest inspect ruvnet/wifi-densepose:latest 2>&1)" || manifest="" + archs=$(echo "$manifest" | $PYTHON -c 'import sys,json +try: + d=json.loads(sys.stdin.read()) + print(",".join(sorted({m["platform"]["architecture"] for m in d.get("manifests",[]) if m["platform"]["os"]=="linux"}))) +except Exception: pass' 2>/dev/null) + if echo "$archs" | grep -q amd64 && echo "$archs" | grep -q arm64; then + echo " archs: $archs" + note_pass "Phase 8: multi-arch manifest (amd64 + arm64) live" + elif [ -n "$archs" ]; then + note_fail "Phase 8: incomplete arch coverage ($archs)" + else + note_skip "Phase 8: docker manifest unreachable (offline?)" + fi + else + note_skip "Phase 8: docker CLI not available" + fi +fi + +# ------------------------------------------------------------------ +# PHASE 9: HOMECORE binary embedded in the Docker image +# ------------------------------------------------------------------ +if [ $RUN_HOMECORE -eq 1 ]; then + phase 9 "HOMECORE binary in Docker image (homecore-server --help)" + if command -v docker >/dev/null 2>&1; then + help_out="$(docker run --rm --entrypoint /app/homecore-server ruvnet/wifi-densepose:latest --help 2>&1)" || help_out="" + if echo "$help_out" | grep -q "0.0.0.0:8123"; then + note_pass "Phase 9: homecore-server present, binds :8123 by default" + elif [ -n "$help_out" ]; then + note_fail "Phase 9: homecore-server help output unexpected" + else + note_skip "Phase 9: docker pull or run unavailable" + fi + else + note_skip "Phase 9: docker CLI not available" + fi +fi # ------------------------------------------------------------------ # FINAL SUMMARY # ------------------------------------------------------------------ +echo "" echo -e "${BOLD}======================================================================${RESET}" +echo -e "${BOLD} SUMMARY (HEAD $git_head)${RESET}" +echo "" +for line in "${SUMMARY[@]}"; do + printf " %b\n" "$line" +done +echo "" -if [ $PIPELINE_EXIT -eq 0 ]; then - echo "" - echo -e " ${GREEN}${BOLD}RESULT: PASS${RESET}" - echo "" - echo " The production pipeline replayed the published reference signal" - echo " and produced a SHA-256 hash that MATCHES the published expected hash." - echo "" - echo " What this proves:" - echo " - The signal processing code is REAL (not mocked)" - echo " - The pipeline is DETERMINISTIC (same input -> same hash)" - echo " - The code path includes: noise filtering, Hamming windowing," - echo " amplitude normalization, FFT-based Doppler extraction," - echo " and power spectral density computation via scipy.fft" - echo " - No randomness was injected (the hash is exact)" - echo "" - echo " To falsify: change any signal processing code and re-run." - echo " The hash will break. That is the point." - echo "" - if [ $MOCK_FINDINGS -eq 0 ]; then - echo -e " Mock scan: ${GREEN}CLEAN${RESET} (no random generators in production code)" - else - echo -e " Mock scan: ${YELLOW}${MOCK_FINDINGS} finding(s)${RESET} (review recommended)" - fi - echo "" - echo -e "${BOLD}======================================================================${RESET}" - exit 0 -elif [ $PIPELINE_EXIT -eq 2 ]; then - echo "" - echo -e " ${YELLOW}${BOLD}RESULT: SKIP${RESET}" - echo "" - echo " No expected hash file to compare against." - echo " Run: python v1/data/proof/verify.py --generate-hash" - echo "" - echo -e "${BOLD}======================================================================${RESET}" - exit 2 +if [ $EXIT_CODE -eq 0 ]; then + echo -e " ${GREEN}${BOLD}OVERALL: PASS${RESET} — every phase that ran proved its layer of the stack." +elif [ $EXIT_CODE -eq 2 ]; then + echo -e " ${YELLOW}${BOLD}OVERALL: SKIPPED${RESET} — Phase 1 had no expected hash to compare (run with --generate-hash)." else - echo "" - echo -e " ${RED}${BOLD}RESULT: FAIL${RESET}" - echo "" - echo " The pipeline hash does NOT match the expected hash." - echo " Something changed in the signal processing code." - echo "" - echo -e "${BOLD}======================================================================${RESET}" - exit 1 + echo -e " ${RED}${BOLD}OVERALL: FAIL${RESET} — at least one phase did not match its published evidence." fi +echo "" +echo -e "${BOLD}======================================================================${RESET}" +exit $EXIT_CODE