#!/usr/bin/env bash set -uo pipefail # CASAN H5 — Ship the audit head to an external append-only WORM ledger (C5/V21). # Call after each audit seal. Locally this appends a hash-linked anchor to a # ledger on a separate path (default outside .specify/logs/audit); production # ships to S3 Object Lock / QLDB. On Linux the ledger is best-effort set # append-only immutable (chattr +a). # # Usage: audit-ship.sh [head-file] [ledger-file] # Env: CASAN_WORM_LEDGER (default .specify/logs/worm/anchor-ledger.jsonl) SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/casan-paths.sh" PROJECT_ROOT="$CASAN_APP_ROOT" HEAD_FILE="${1:-$CASAN_STATE_ROOT/logs/audit/audit-head.txt}" LEDGER="${2:-${CASAN_WORM_LEDGER:-$CASAN_STATE_ROOT/logs/worm/anchor-ledger.jsonl}}" mkdir -p "$(dirname "$LEDGER")" [[ -f "$HEAD_FILE" ]] || { echo "AUDIT_SHIP_NO_HEAD file=$HEAD_FILE" >&2; exit 1; } # Append-only immutability on Linux (best-effort; no-op on macOS/CI without cap). if command -v chattr >/dev/null 2>&1; then chattr -a "$LEDGER" 2>/dev/null || true; fi python "$SCRIPT_DIR/worm-ledger.py" ship "$HEAD_FILE" "$LEDGER"; RC=$? if command -v chattr >/dev/null 2>&1; then chattr +a "$LEDGER" 2>/dev/null || true; fi exit "$RC"