diff --git a/AGENTS.md b/AGENTS.md index b7b3f43..b907b02 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,8 +31,8 @@ git commit -m "Add OpenTimestamp proofs for commit history" ## Manual Installation ```bash -cp hooks/post-commit-ots .git/hooks/post-commit -cp hooks/pre-commit-ots .git/hooks/pre-commit +cp hooks/post-commit .git/hooks/post-commit +cp hooks/pre-commit .git/hooks/pre-commit chmod +x .git/hooks/post-commit .git/hooks/pre-commit ``` @@ -45,8 +45,8 @@ chmod +x .git/hooks/post-commit .git/hooks/pre-commit | File | Purpose | Version? | |------|---------|----------| -| `hooks/post-commit-ots` | Post-commit hook | Yes | -| `hooks/pre-commit-ots` | Backfill hook | Yes | +| `hooks/post-commit` | Post-commit hook | Yes | +| `hooks/pre-commit` | Backfill hook | Yes | | `hooks/install.sh` | Installer (checks ots) | Yes | | `.ots/*.ots` | Individual proofs | Yes | | `.ots/proof.ots` | Latest proof reference | Yes | diff --git a/README.md b/README.md index 849a658..f54ea22 100644 --- a/README.md +++ b/README.md @@ -34,8 +34,8 @@ That's it! Every commit will now be timestamped and anchored to Bitcoin. ```bash # Copy hooks -cp hooks/post-commit-ots .git/hooks/post-commit -cp hooks/pre-commit-ots .git/hooks/pre-commit +cp hooks/post-commit .git/hooks/post-commit +cp hooks/pre-commit .git/hooks/pre-commit chmod +x .git/hooks/post-commit .git/hooks/pre-commit # Setup .gitignore diff --git a/SKILL.md b/SKILL.md index 7df4c7d..5dd2d33 100644 --- a/SKILL.md +++ b/SKILL.md @@ -73,8 +73,8 @@ repo/ If you prefer manual setup: ```bash -cp hooks/post-commit-ots .git/hooks/post-commit -cp hooks/pre-commit-ots .git/hooks/pre-commit +cp hooks/post-commit .git/hooks/post-commit +cp hooks/pre-commit .git/hooks/pre-commit chmod +x .git/hooks/post-commit .git/hooks/pre-commit ``` diff --git a/hooks/install.sh b/hooks/install.sh index d2f2b3c..9477e0c 100755 --- a/hooks/install.sh +++ b/hooks/install.sh @@ -40,9 +40,9 @@ HOOKS_DIR="$(git rev-parse --git-dir)/hooks" echo "Installing hooks to: $HOOKS_DIR" -# Copy ots hooks -cp "$SCRIPT_DIR/post-commit-ots" "$HOOKS_DIR/post-commit" -cp "$SCRIPT_DIR/pre-commit-ots" "$HOOKS_DIR/pre-commit" +# Copy hooks +cp "$SCRIPT_DIR/post-commit" "$HOOKS_DIR/post-commit" +cp "$SCRIPT_DIR/pre-commit" "$HOOKS_DIR/pre-commit" chmod +x "$HOOKS_DIR/post-commit" "$HOOKS_DIR/pre-commit" diff --git a/hooks/post-commit b/hooks/post-commit index 044dfe3..776cc89 100755 --- a/hooks/post-commit +++ b/hooks/post-commit @@ -1,91 +1,41 @@ #!/bin/bash -# Git OpenTimestamp Post-Commit Hook -# Self-contained - no external dependencies -# Generates cryptographic proof for each commit using OpenTimestamp +# Git OpenTimestamp Post-Commit Hook (ots CLI version) +# Requires: opentimestamps-client (pipx install opentimestamps-client) set -e -# Configuration OUTPUT_DIR=".ots" OUTPUT_FILE="$OUTPUT_DIR/proof.ots" -# Ensure output directory exists mkdir -p "$OUTPUT_DIR" -# Get the current commit hash COMMIT_HASH=$(git rev-parse HEAD) -# Function to generate proof using ots CLI -generate_with_ots_cli() { - local hash="$1" - local output="$2" - - # Create a temporary file with the hash (convert hex to binary) - local temp_file=$(mktemp) - local temp_ots="${temp_file}.ots" - - # Use python3 for hex to binary conversion (more portable than xxd) - python3 -c "import sys; sys.stdout.buffer.write(bytes.fromhex('$hash'))" > "$temp_file" - - # Generate timestamp - ots creates .ots file alongside the original - ots stamp "$temp_file" 2>/dev/null - - # Move the generated .ots file to the desired location - if [ -f "$temp_ots" ]; then - mv "$temp_ots" "$output" - rm -f "$temp_file" - return 0 - else - rm -f "$temp_file" - return 1 - fi -} +# Generate proof using ots CLI +temp_file=$(mktemp) +temp_ots="${temp_file}.ots" -# Function to generate proof using nodejs fallback -generate_with_node() { - local hash="$1" - local output="$2" - - node -e " -const ots = require('@opentimestamps/ots'); -const fs = require('fs'); -const hash = '$hash'; -const output = '$output'; -const hashBuffer = Buffer.from(hash, 'hex'); -ots.Timestamp.hash(hashBuffer).then(timestamp => { - const proof = { hash: hash, timestamp: new Date().toISOString(), status: 'pending' }; - fs.writeFileSync(output, JSON.stringify(proof, null, 2)); - console.log('Generated local proof (nodejs fallback)'); -}).catch(err => { console.error('Error:', err); process.exit(1); }); -" -} +python3 -c "import sys; sys.stdout.buffer.write(bytes.fromhex('$COMMIT_HASH'))" > "$temp_file" +ots stamp "$temp_file" 2>/dev/null -# Check for available tools and generate proof -if command -v ots &> /dev/null; then - if generate_with_ots_cli "$COMMIT_HASH" "$OUTPUT_FILE"; then - echo "[ots] Generated proof with ots CLI: ${COMMIT_HASH:0:8}" - else - echo "[ots] Warning: ots CLI failed" >&2 - exit 0 - fi -elif command -v node &> /dev/null && node -e "require('@opentimestamps/ots')" &> /dev/null 2>&1; then - generate_with_node "$COMMIT_HASH" "$OUTPUT_FILE" - echo "[ots] Generated proof with nodejs fallback: ${COMMIT_HASH:0:8}" +if [ -f "$temp_ots" ]; then + mv "$temp_ots" "$OUTPUT_FILE" + rm -f "$temp_file" + echo "[ots] Generated proof: ${COMMIT_HASH:0:8}" else - echo "[ots] Warning: Neither ots CLI nor @opentimestamps/ots found, skipping proof" >&2 + rm -f "$temp_file" + echo "[ots] Warning: Failed to generate proof" >&2 exit 0 fi -# Save previous commit hash for chaining +# Save previous commit for chaining PREV_COMMIT=$(git rev-parse HEAD^1 2>/dev/null || echo "") if [ -n "$PREV_COMMIT" ]; then echo "$PREV_COMMIT" > "$OUTPUT_DIR/prev-commit.txt" fi -# Create individual proof file for this commit +# Create individual proof file INDIVIDUAL_PROOF="$OUTPUT_DIR/${COMMIT_HASH}.ots" -if [ -f "$OUTPUT_FILE" ]; then - cp "$OUTPUT_FILE" "$INDIVIDUAL_PROOF" -fi +cp "$OUTPUT_FILE" "$INDIVIDUAL_PROOF" echo "[ots] Proof generated successfully" diff --git a/hooks/post-commit-ots b/hooks/post-commit-ots deleted file mode 100755 index 776cc89..0000000 --- a/hooks/post-commit-ots +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -# Git OpenTimestamp Post-Commit Hook (ots CLI version) -# Requires: opentimestamps-client (pipx install opentimestamps-client) - -set -e - -OUTPUT_DIR=".ots" -OUTPUT_FILE="$OUTPUT_DIR/proof.ots" - -mkdir -p "$OUTPUT_DIR" - -COMMIT_HASH=$(git rev-parse HEAD) - -# Generate proof using ots CLI -temp_file=$(mktemp) -temp_ots="${temp_file}.ots" - -python3 -c "import sys; sys.stdout.buffer.write(bytes.fromhex('$COMMIT_HASH'))" > "$temp_file" -ots stamp "$temp_file" 2>/dev/null - -if [ -f "$temp_ots" ]; then - mv "$temp_ots" "$OUTPUT_FILE" - rm -f "$temp_file" - echo "[ots] Generated proof: ${COMMIT_HASH:0:8}" -else - rm -f "$temp_file" - echo "[ots] Warning: Failed to generate proof" >&2 - exit 0 -fi - -# Save previous commit for chaining -PREV_COMMIT=$(git rev-parse HEAD^1 2>/dev/null || echo "") -if [ -n "$PREV_COMMIT" ]; then - echo "$PREV_COMMIT" > "$OUTPUT_DIR/prev-commit.txt" -fi - -# Create individual proof file -INDIVIDUAL_PROOF="$OUTPUT_DIR/${COMMIT_HASH}.ots" -cp "$OUTPUT_FILE" "$INDIVIDUAL_PROOF" - -echo "[ots] Proof generated successfully" diff --git a/hooks/pre-commit b/hooks/pre-commit index 9337782..65d354d 100755 --- a/hooks/pre-commit +++ b/hooks/pre-commit @@ -1,24 +1,19 @@ #!/bin/bash -# Git OpenTimestamp Pre-Commit Backfill Hook -# Self-contained - no external dependencies -# Upgrades all historical proofs before each new commit +# Git OpenTimestamp Pre-Commit Backfill Hook (ots CLI version) +# Requires: opentimestamps-client (pipx install opentimestamps-client) set -e -# Configuration OUTPUT_DIR=".ots" STATUS_CACHE="$OUTPUT_DIR/.attestation-cache" -# Ensure output directory exists mkdir -p "$OUTPUT_DIR" -# Initialize cache file if it doesn't exist +# Initialize cache if [ ! -f "$STATUS_CACHE" ]; then - echo "# Attestation status cache" > "$STATUS_CACHE" - echo "# Format: commit-hash:status:timestamp" >> "$STATUS_CACHE" + echo "# Format: commit-hash:status:timestamp" > "$STATUS_CACHE" fi -# Function to get cached status get_cached_status() { local commit="$1" local cache_line=$(grep "^$commit:" "$STATUS_CACHE" 2>/dev/null | tail -1) @@ -26,9 +21,7 @@ get_cached_status() { local status=$(echo "$cache_line" | cut -d: -f2) local timestamp=$(echo "$cache_line" | cut -d: -f3) local now=$(date +%s) - local age=$((now - timestamp)) - # Cache valid for 1 hour (3600 seconds) - if [ "$age" -lt 3600 ]; then + if [ "$((now - timestamp))" -lt 3600 ]; then echo "$status" return 0 fi @@ -36,19 +29,15 @@ get_cached_status() { return 1 } -# Function to cache status cache_status() { local commit="$1" local status="$2" - local now=$(date +%s) - echo "$commit:$status:$now" >> "$STATUS_CACHE" + echo "$commit:$status:$(date +%s)" >> "$STATUS_CACHE" } -# Function to generate proof using ots CLI generate_proof() { local hash="$1" local output="$2" - local temp_file=$(mktemp) local temp_ots="${temp_file}.ots" @@ -59,20 +48,17 @@ generate_proof() { mv "$temp_ots" "$output" rm -f "$temp_file" return 0 - else - rm -f "$temp_file" - return 1 fi + rm -f "$temp_file" + return 1 } -# Function to check if proof is attested is_attested() { local proof_file="$1" local pending_count=$(ots info "$proof_file" 2>&1 | grep -c "PendingAttestation" || echo "0") [ "$pending_count" -eq 0 ] } -# Function to upgrade proof upgrade_proof() { local proof_file="$1" ots upgrade "$proof_file" 2>/dev/null @@ -80,7 +66,6 @@ upgrade_proof() { echo "[ots] Backfilling proofs..." -# Get all commit hashes (oldest to newest) COMMITS=$(git rev-list --reverse HEAD) TOTAL=$(echo "$COMMITS" | wc -l) CURRENT=0 @@ -90,47 +75,35 @@ for COMMIT in $COMMITS; do CURRENT=$((CURRENT + 1)) PROOF_FILE="$OUTPUT_DIR/${COMMIT}.ots" - # Skip verbose output for brevity if [ $CURRENT -le 3 ] || [ $CURRENT -eq $TOTAL ]; then - echo "[ots] Processing commit $CURRENT/$TOTAL: ${COMMIT:0:8}" + echo "[ots] Processing $CURRENT/$TOTAL: ${COMMIT:0:8}" elif [ $CURRENT -eq 4 ]; then - echo "[ots] ... processing remaining commits ..." + echo "[ots] ... processing remaining ..." fi if [ -f "$PROOF_FILE" ]; then - # Check cached status first CACHED_STATUS=$(get_cached_status "$COMMIT" || echo "") - if [ "$CACHED_STATUS" = "attested" ]; then - continue - fi + [ "$CACHED_STATUS" = "attested" ] && continue - # Check if already attested if is_attested "$PROOF_FILE"; then cache_status "$COMMIT" "attested" continue fi - # Cache as pending cache_status "$COMMIT" "pending" - # Skip upgrade if cache is fresh (< 10 min old) CACHE_LINE=$(grep "^$COMMIT:" "$STATUS_CACHE" | tail -1) CACHE_TIME=$(echo "$CACHE_LINE" | cut -d: -f3) - NOW=$(date +%s) - CACHE_AGE=$((NOW - CACHE_TIME)) + CACHE_AGE=$(($(date +%s) - CACHE_TIME)) - if [ "$CACHE_AGE" -lt 600 ]; then - continue - fi + [ "$CACHE_AGE" -lt 600 ] && continue - # Try to upgrade if upgrade_proof "$PROOF_FILE"; then cache_status "$COMMIT" "attested" UPDATED=$((UPDATED + 1)) fi else - # Generate new proof if generate_proof "$COMMIT" "$PROOF_FILE"; then cache_status "$COMMIT" "pending" UPDATED=$((UPDATED + 1)) @@ -138,19 +111,15 @@ for COMMIT in $COMMITS; do fi done -# Update latest proof symlink +# Update latest proof LATEST_COMMIT=$(git rev-parse HEAD) -if [ -f "$OUTPUT_DIR/${LATEST_COMMIT}.ots" ]; then - cp "$OUTPUT_DIR/${LATEST_COMMIT}.ots" "$OUTPUT_DIR/proof.ots" -fi +[ -f "$OUTPUT_DIR/${LATEST_COMMIT}.ots" ] && cp "$OUTPUT_DIR/${LATEST_COMMIT}.ots" "$OUTPUT_DIR/proof.ots" # Save commit chain rm -f "$OUTPUT_DIR/commit-chain.txt" git rev-list HEAD | while read COMMIT; do PREV=$(git rev-parse ${COMMIT}^1 2>/dev/null || echo "") - if [ -n "$PREV" ]; then - echo "$COMMIT:$PREV" >> "$OUTPUT_DIR/commit-chain.txt" - fi + [ -n "$PREV" ] && echo "$COMMIT:$PREV" >> "$OUTPUT_DIR/commit-chain.txt" done -echo "[ots] Backfill complete: $UPDATED proofs updated" +echo "[ots] Backfill complete: $UPDATED updated" diff --git a/hooks/pre-commit-ots b/hooks/pre-commit-ots deleted file mode 100755 index 65d354d..0000000 --- a/hooks/pre-commit-ots +++ /dev/null @@ -1,125 +0,0 @@ -#!/bin/bash -# Git OpenTimestamp Pre-Commit Backfill Hook (ots CLI version) -# Requires: opentimestamps-client (pipx install opentimestamps-client) - -set -e - -OUTPUT_DIR=".ots" -STATUS_CACHE="$OUTPUT_DIR/.attestation-cache" - -mkdir -p "$OUTPUT_DIR" - -# Initialize cache -if [ ! -f "$STATUS_CACHE" ]; then - echo "# Format: commit-hash:status:timestamp" > "$STATUS_CACHE" -fi - -get_cached_status() { - local commit="$1" - local cache_line=$(grep "^$commit:" "$STATUS_CACHE" 2>/dev/null | tail -1) - if [ -n "$cache_line" ]; then - local status=$(echo "$cache_line" | cut -d: -f2) - local timestamp=$(echo "$cache_line" | cut -d: -f3) - local now=$(date +%s) - if [ "$((now - timestamp))" -lt 3600 ]; then - echo "$status" - return 0 - fi - fi - return 1 -} - -cache_status() { - local commit="$1" - local status="$2" - echo "$commit:$status:$(date +%s)" >> "$STATUS_CACHE" -} - -generate_proof() { - local hash="$1" - local output="$2" - local temp_file=$(mktemp) - local temp_ots="${temp_file}.ots" - - python3 -c "import sys; sys.stdout.buffer.write(bytes.fromhex('$hash'))" > "$temp_file" - ots stamp "$temp_file" 2>/dev/null - - if [ -f "$temp_ots" ]; then - mv "$temp_ots" "$output" - rm -f "$temp_file" - return 0 - fi - rm -f "$temp_file" - return 1 -} - -is_attested() { - local proof_file="$1" - local pending_count=$(ots info "$proof_file" 2>&1 | grep -c "PendingAttestation" || echo "0") - [ "$pending_count" -eq 0 ] -} - -upgrade_proof() { - local proof_file="$1" - ots upgrade "$proof_file" 2>/dev/null -} - -echo "[ots] Backfilling proofs..." - -COMMITS=$(git rev-list --reverse HEAD) -TOTAL=$(echo "$COMMITS" | wc -l) -CURRENT=0 -UPDATED=0 - -for COMMIT in $COMMITS; do - CURRENT=$((CURRENT + 1)) - PROOF_FILE="$OUTPUT_DIR/${COMMIT}.ots" - - if [ $CURRENT -le 3 ] || [ $CURRENT -eq $TOTAL ]; then - echo "[ots] Processing $CURRENT/$TOTAL: ${COMMIT:0:8}" - elif [ $CURRENT -eq 4 ]; then - echo "[ots] ... processing remaining ..." - fi - - if [ -f "$PROOF_FILE" ]; then - CACHED_STATUS=$(get_cached_status "$COMMIT" || echo "") - - [ "$CACHED_STATUS" = "attested" ] && continue - - if is_attested "$PROOF_FILE"; then - cache_status "$COMMIT" "attested" - continue - fi - - cache_status "$COMMIT" "pending" - - CACHE_LINE=$(grep "^$COMMIT:" "$STATUS_CACHE" | tail -1) - CACHE_TIME=$(echo "$CACHE_LINE" | cut -d: -f3) - CACHE_AGE=$(($(date +%s) - CACHE_TIME)) - - [ "$CACHE_AGE" -lt 600 ] && continue - - if upgrade_proof "$PROOF_FILE"; then - cache_status "$COMMIT" "attested" - UPDATED=$((UPDATED + 1)) - fi - else - if generate_proof "$COMMIT" "$PROOF_FILE"; then - cache_status "$COMMIT" "pending" - UPDATED=$((UPDATED + 1)) - fi - fi -done - -# Update latest proof -LATEST_COMMIT=$(git rev-parse HEAD) -[ -f "$OUTPUT_DIR/${LATEST_COMMIT}.ots" ] && cp "$OUTPUT_DIR/${LATEST_COMMIT}.ots" "$OUTPUT_DIR/proof.ots" - -# Save commit chain -rm -f "$OUTPUT_DIR/commit-chain.txt" -git rev-list HEAD | while read COMMIT; do - PREV=$(git rev-parse ${COMMIT}^1 2>/dev/null || echo "") - [ -n "$PREV" ] && echo "$COMMIT:$PREV" >> "$OUTPUT_DIR/commit-chain.txt" -done - -echo "[ots] Backfill complete: $UPDATED updated"