Institutional stacks
4. Automated Fixity & Audit Engine
Silent data corruption (bit rot) is the single greatest threat to long-term digital retention. Implement automated systemd timers to verify SHA-256 checksum manifests against live disk states.
This audit engine exports native metrics to Prometheus (via Node Exporter textfile collector), logs events to systemd-journald/syslog for SIEM ingestion (Splunk, Elastic), and supports enterprise notification triggers (SMTP email-to-ticket or PagerDuty webhooks).
Enterprise Fixity Audit Script (/opt/scripts/fixity_audit.sh)
#!/usr/bin/env bash
set -euo pipefail
# --- CONFIGURATION ---
MANIFEST="/tank/archival-masters/manifest.sha256"
LOG_TAG="fixity-audit"
PROM_TEXTFILE_DIR="/var/lib/node_exporter/textfile_collector"
PROM_METRIC_FILE="${PROM_TEXTFILE_DIR}/archival_fixity.prom"
# Optional Alerting Endpoints (Set via environment or leave empty)
ALERT_EMAIL="${ALERT_EMAIL:-sysadmin@institution.edu}"
GENERIC_WEBHOOK_URL="${GENERIC_WEBHOOK_URL:-}" # PagerDuty, Opsgenie, Teams, ServiceNow
# Log directly to host systemd journald / syslog
log_info() { logger -t "$LOG_TAG" -p daemon.info "$1"; }
log_error() { logger -t "$LOG_TAG" -p daemon.err "$1"; }
log_info "Starting scheduled fixity audit..."
if [ ! -f "$MANIFEST" ]; then
log_error "CRITICAL: Manifest file missing at $MANIFEST"
exit 1
fi
START_TIME=$(date +%s)
FAILED_COUNT=0
# Run hash verification
if ! Output=$(sha256sum -c "$MANIFEST" 2>&1); then
# Calculate failed files count
FAILED_COUNT=$(echo "$Output" | grep -c "FAILED" || true)
fi
END_TIME=$(date +%s)
DURATION=$((END_TIME - START_TIME))
# --- EXPORT PROMETHEUS METRICS ---
if [ -d "$PROM_TEXTFILE_DIR" ]; then
cat <<EOF> "${PROM_METRIC_FILE}.tmp"
# HELP archival_fixity_failed_files Count of files failing SHA-256 fixity audit.
# TYPE archival_fixity_failed_files gauge
archival_fixity_failed_files ${FAILED_COUNT}
# HELP archival_fixity_last_run_timestamp Epoch timestamp of last fixity audit.
# TYPE archival_fixity_last_run_timestamp gauge
archival_fixity_last_run_timestamp ${END_TIME}
# HELP archival_fixity_duration_seconds Execution time of fixity audit.
# TYPE archival_fixity_duration_seconds gauge
archival_fixity_duration_seconds ${DURATION}
EOF
mv "${PROM_METRIC_FILE}.tmp" "$PROM_METRIC_FILE"
fi
# --- HANDLE AUDIT RESULT ---
if [ "$FAILED_COUNT" -eq 0 ]; then
log_info "SUCCESS: Fixity audit passed. All archival objects verified intact."
exit 0
else
ALERT_MSG="CRITICAL ALERT: Archival Fixity Audit FAILED! ${FAILED_COUNT} file(s) failed SHA-256 integrity checks."
log_error "$ALERT_MSG"
# 1. Dispatch POSIX Mail / Ticketing Alert (ServiceNow / Jira Ingest)
if command -v mailx &> /dev/null && [ -n "$ALERT_EMAIL" ]; then
echo -e "Fixity Audit Failure Report\n\n$Output" | mailx -s "[CRITICAL] Fixity Failure - Archival Vault" "$ALERT_EMAIL"
fi
# 2. Dispatch Enterprise Webhook (PagerDuty / Webex / Teams / Generic API)
if [ -n "$GENERIC_WEBHOOK_URL" ]; then
curl -s -X POST -H "Content-Type: application/json" \
-d "{\"summary\": \"$ALERT_MSG\", \"severity\": \"critical\", \"source\": \"$(hostname)\"}" \
"$GENERIC_WEBHOOK_URL" || true
fi
exit 1
fi
Make the script executable:
Systemd Automated Timer
Create /etc/systemd/system/fixity-audit.service:
[Unit]
Description=HistoryLabs Archival Fixity Audit Service
After=network.target
[Service]
Type=oneshot
ExecStart=/opt/scripts/fixity_audit.sh
StandardOutput=journal
StandardError=journal
Create /etc/systemd/system/fixity-audit.timer:
[Unit]
Description=Run Archival Fixity Audit Weekly
[Timer]
OnCalendar=Sun *-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
Enable the timer on your host: