Files
vm-cc/scripts/vmcc
2025-12-27 01:15:00 +00:00

136 lines
3.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
source "$(dirname "$0")/lib/common.sh"
ROOT="$(vmcc_root)"
CMD="${1:-}"
RUN_ID="${VMCC_RUN_ID:-$(run_id)}"
DAY="$(date -u "+%Y-%m-%d")"
EVID_DIR="$ROOT/30-evidence/$DAY/$RUN_ID"
RULE_DIR="$ROOT/50-reports/$DAY/$RUN_ID/rules"
REP_DIR="$ROOT/50-reports/$DAY/$RUN_ID"
mkdir -p "$EVID_DIR" "$RULE_DIR" "$REP_DIR"
hash_file() {
local file="$1"
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "$file" | awk '{print $1}'
else
shasum -a 256 "$file" | awk '{print $1}'
fi
}
write_manifest() {
require_cmd jq
local manifest="$EVID_DIR/manifest.json"
local ts
ts="$(iso_utc_now)"
if sort -z --version >/dev/null 2>&1; then
find "$EVID_DIR" -type f ! -name "manifest.json" -print0 \
| LC_ALL=C sort -z \
| while IFS= read -r -d '' file; do
local rel
rel="${file#$ROOT/}"
local sha
sha="$(hash_file "$file")"
jq -n --arg path "$rel" --arg sha "$sha" '{path:$path, sha256:$sha}'
done \
| jq -s --arg ts "$ts" --arg run "$RUN_ID" '{
version: "1.0.0",
collected_at: $ts,
run_id: $run,
files: .
}' > "$manifest"
else
find "$EVID_DIR" -type f ! -name "manifest.json" \
| LC_ALL=C sort \
| while IFS= read -r file; do
local rel
rel="${file#$ROOT/}"
local sha
sha="$(hash_file "$file")"
jq -n --arg path "$rel" --arg sha "$sha" '{path:$path, sha256:$sha}'
done \
| jq -s --arg ts "$ts" --arg run "$RUN_ID" '{
version: "1.0.0",
collected_at: $ts,
run_id: $run,
files: .
}' > "$manifest"
fi
}
run_collect() {
echo "[vmcc] run_id=$RUN_ID"
echo "[vmcc] collecting evidence -> $EVID_DIR"
"$ROOT/20-collectors/collect_ledger_verify.sh" "$EVID_DIR"
"$ROOT/20-collectors/collect_constitution_hash.sh" "$EVID_DIR"
"$ROOT/20-collectors/collect_backup_restore_drill.sh" "$EVID_DIR"
write_manifest
}
run_evaluate() {
echo "[vmcc] evaluating rules -> $RULE_DIR"
"$ROOT/40-rules/ledger_hash_chain_intact.sh" "$EVID_DIR" > "$RULE_DIR/ledger.hash_chain_intact.json"
"$ROOT/40-rules/governance_constitution_pinned.sh" "$EVID_DIR" > "$RULE_DIR/governance.constitution_pinned.json"
"$ROOT/40-rules/backup_restore_drill_recent.sh" "$EVID_DIR" > "$RULE_DIR/backup.restore_drill_recent.json"
}
run_report() {
require_cmd jq
echo "[vmcc] assembling report -> $REP_DIR/report.json"
TS="$(iso_utc_now)"
PASSED_COUNT="$(jq -s '[.[] | select(.passed==true)] | length' "$RULE_DIR"/*.json)"
FAILED_COUNT="$(jq -s '[.[] | select(.passed==false)] | length' "$RULE_DIR"/*.json)"
jq -n \
--arg version "1.0.0" \
--arg timestamp "$TS" \
--arg run_id "$RUN_ID" \
--arg day "$DAY" \
--slurpfile rules <(cat "$RULE_DIR"/*.json) \
--argjson passed "$PASSED_COUNT" \
--argjson failed "$FAILED_COUNT" \
'{
version: $version,
timestamp: $timestamp,
period: "run",
run_id: $run_id,
day: $day,
summary: {
rules_passed: $passed,
rules_failed: $failed,
status: (if $failed == 0 then "COMPLIANT" else "NONCOMPLIANT" end)
},
rules: $rules
}' > "$REP_DIR/report.json"
}
case "$CMD" in
collect)
run_collect
;;
evaluate)
run_evaluate
;;
report)
run_report
;;
all)
run_collect
run_evaluate
run_report
if jq -e -s 'map(select(.passed == false)) | length > 0' "$RULE_DIR"/*.json >/dev/null; then
exit 3
fi
;;
*)
echo "Usage: $0 {collect|evaluate|report|all}" >&2
exit 1
;;
esac