Test self-contained unified hooks

This commit is contained in:
Otto 2026-03-07 17:17:25 +01:00
parent e2d0ff0352
commit a7c0f825be
11 changed files with 338 additions and 1 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.ots/.attestation-cache

View file

@ -15,3 +15,22 @@ c0685dabfb48360a3abc103b75357f94e9f054b2:pending:1772897691
810d26b7af9c5d306e77fec290d360c7ac876b2e:pending:1772897694
3b54e0cb8c611d3f3525ad2386368f60200891f1:pending:1772897698
ed2cd259e918344c5a21ecf884b3178b4256ea74:pending:1772897704
392ee723c3cf626d0e5281aa94771d7133bb345e:pending:1772897724
db6f29e01a33d8ed8f127ff169d9f91d55e8a229:pending:1772897725
4a6f5ed0c12315b0bc8a0fa5815ada1bd20e5963:pending:1772897726
46aded7b9582bbed673843e2cf8a3f8fa742ad91:pending:1772897727
c0685dabfb48360a3abc103b75357f94e9f054b2:pending:1772897728
810d26b7af9c5d306e77fec290d360c7ac876b2e:pending:1772897728
3b54e0cb8c611d3f3525ad2386368f60200891f1:pending:1772897729
ed2cd259e918344c5a21ecf884b3178b4256ea74:pending:1772897730
f4eb0ad6782d2bc003206521cc66ea370dcccd9f:pending:1772897732
392ee723c3cf626d0e5281aa94771d7133bb345e:pending:1772900099
db6f29e01a33d8ed8f127ff169d9f91d55e8a229:pending:1772900100
4a6f5ed0c12315b0bc8a0fa5815ada1bd20e5963:pending:1772900100
46aded7b9582bbed673843e2cf8a3f8fa742ad91:pending:1772900101
c0685dabfb48360a3abc103b75357f94e9f054b2:pending:1772900102
810d26b7af9c5d306e77fec290d360c7ac876b2e:pending:1772900103
3b54e0cb8c611d3f3525ad2386368f60200891f1:pending:1772900104
ed2cd259e918344c5a21ecf884b3178b4256ea74:pending:1772900105
f4eb0ad6782d2bc003206521cc66ea370dcccd9f:pending:1772900106
4bf5ab21764bc9a4507e25e0bc8de2989d4febad:pending:1772900109

Binary file not shown.

View file

@ -28,3 +28,22 @@ c0685dabfb48360a3abc103b75357f94e9f054b2:46aded7b9582bbed673843e2cf8a3f8fa742ad9
4a6f5ed0c12315b0bc8a0fa5815ada1bd20e5963:db6f29e01a33d8ed8f127ff169d9f91d55e8a229
db6f29e01a33d8ed8f127ff169d9f91d55e8a229:392ee723c3cf626d0e5281aa94771d7133bb345e
392ee723c3cf626d0e5281aa94771d7133bb345e:392ee723c3cf626d0e5281aa94771d7133bb345e^1
f4eb0ad6782d2bc003206521cc66ea370dcccd9f:ed2cd259e918344c5a21ecf884b3178b4256ea74
ed2cd259e918344c5a21ecf884b3178b4256ea74:3b54e0cb8c611d3f3525ad2386368f60200891f1
3b54e0cb8c611d3f3525ad2386368f60200891f1:810d26b7af9c5d306e77fec290d360c7ac876b2e
810d26b7af9c5d306e77fec290d360c7ac876b2e:c0685dabfb48360a3abc103b75357f94e9f054b2
c0685dabfb48360a3abc103b75357f94e9f054b2:46aded7b9582bbed673843e2cf8a3f8fa742ad91
46aded7b9582bbed673843e2cf8a3f8fa742ad91:4a6f5ed0c12315b0bc8a0fa5815ada1bd20e5963
4a6f5ed0c12315b0bc8a0fa5815ada1bd20e5963:db6f29e01a33d8ed8f127ff169d9f91d55e8a229
db6f29e01a33d8ed8f127ff169d9f91d55e8a229:392ee723c3cf626d0e5281aa94771d7133bb345e
392ee723c3cf626d0e5281aa94771d7133bb345e:392ee723c3cf626d0e5281aa94771d7133bb345e^1
4bf5ab21764bc9a4507e25e0bc8de2989d4febad:f4eb0ad6782d2bc003206521cc66ea370dcccd9f
f4eb0ad6782d2bc003206521cc66ea370dcccd9f:ed2cd259e918344c5a21ecf884b3178b4256ea74
ed2cd259e918344c5a21ecf884b3178b4256ea74:3b54e0cb8c611d3f3525ad2386368f60200891f1
3b54e0cb8c611d3f3525ad2386368f60200891f1:810d26b7af9c5d306e77fec290d360c7ac876b2e
810d26b7af9c5d306e77fec290d360c7ac876b2e:c0685dabfb48360a3abc103b75357f94e9f054b2
c0685dabfb48360a3abc103b75357f94e9f054b2:46aded7b9582bbed673843e2cf8a3f8fa742ad91
46aded7b9582bbed673843e2cf8a3f8fa742ad91:4a6f5ed0c12315b0bc8a0fa5815ada1bd20e5963
4a6f5ed0c12315b0bc8a0fa5815ada1bd20e5963:db6f29e01a33d8ed8f127ff169d9f91d55e8a229
db6f29e01a33d8ed8f127ff169d9f91d55e8a229:392ee723c3cf626d0e5281aa94771d7133bb345e
392ee723c3cf626d0e5281aa94771d7133bb345e:392ee723c3cf626d0e5281aa94771d7133bb345e^1

Binary file not shown.

View file

@ -1 +1 @@
ed2cd259e918344c5a21ecf884b3178b4256ea74
4bf5ab21764bc9a4507e25e0bc8de2989d4febad

Binary file not shown.

50
hooks/install.sh Executable file
View file

@ -0,0 +1,50 @@
#!/bin/bash
# Git OpenTimestamp Hooks Installer
# Copies self-contained hooks to git repository
set -e
REPO_PATH="${1:-.}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$REPO_PATH"
# Verify we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo "Error: not a git repository" >&2
exit 1
fi
HOOKS_DIR="$(git rev-parse --git-dir)/hooks"
echo "Installing OpenTimestamp hooks to: $HOOKS_DIR"
# Copy hooks
cp "$SCRIPT_DIR/post-commit" "$HOOKS_DIR/post-commit"
cp "$SCRIPT_DIR/pre-commit" "$HOOKS_DIR/pre-commit"
# Make executable
chmod +x "$HOOKS_DIR/post-commit" "$HOOKS_DIR/pre-commit"
echo "✓ Post-commit hook installed"
echo "✓ Pre-commit backfill hook installed"
# Setup .gitignore
GITIGNORE=".gitignore"
if [ ! -f "$GITIGNORE" ]; then
echo ".ots/.attestation-cache" > "$GITIGNORE"
echo "✓ Created .gitignore"
elif ! grep -q ".ots/.attestation-cache" "$GITIGNORE"; then
echo "" >> "$GITIGNORE"
echo ".ots/.attestation-cache" >> "$GITIGNORE"
echo "✓ Updated .gitignore"
fi
echo ""
echo "Next steps:"
echo "1. Make a test commit to verify hooks work"
echo "2. Commit the .ots/ directory: git add .ots/"
echo "3. Commit with message: git commit -m 'Add OpenTimestamp proofs'"
echo ""
echo "To uninstall:"
echo " rm $HOOKS_DIR/post-commit $HOOKS_DIR/pre-commit"

91
hooks/post-commit Executable file
View file

@ -0,0 +1,91 @@
#!/bin/bash
# Git OpenTimestamp Post-Commit Hook
# Self-contained - no external dependencies
# Generates cryptographic proof for each commit using OpenTimestamp
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
}
# 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); });
"
}
# 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}"
else
echo "[ots] Warning: Neither ots CLI nor @opentimestamps/ots found, skipping proof" >&2
exit 0
fi
# Save previous commit hash 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
INDIVIDUAL_PROOF="$OUTPUT_DIR/${COMMIT_HASH}.ots"
if [ -f "$OUTPUT_FILE" ]; then
cp "$OUTPUT_FILE" "$INDIVIDUAL_PROOF"
fi
echo "[ots] Proof generated successfully"

156
hooks/pre-commit Executable file
View file

@ -0,0 +1,156 @@
#!/bin/bash
# Git OpenTimestamp Pre-Commit Backfill Hook
# Self-contained - no external dependencies
# Upgrades all historical proofs before each new commit
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
if [ ! -f "$STATUS_CACHE" ]; then
echo "# Attestation status cache" > "$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)
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)
local age=$((now - timestamp))
# Cache valid for 1 hour (3600 seconds)
if [ "$age" -lt 3600 ]; then
echo "$status"
return 0
fi
fi
return 1
}
# Function to cache status
cache_status() {
local commit="$1"
local status="$2"
local now=$(date +%s)
echo "$commit:$status:$now" >> "$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"
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
else
rm -f "$temp_file"
return 1
fi
}
# 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
}
echo "[ots] Backfilling proofs..."
# Get all commit hashes (oldest to newest)
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"
# Skip verbose output for brevity
if [ $CURRENT -le 3 ] || [ $CURRENT -eq $TOTAL ]; then
echo "[ots] Processing commit $CURRENT/$TOTAL: ${COMMIT:0:8}"
elif [ $CURRENT -eq 4 ]; then
echo "[ots] ... processing remaining commits ..."
fi
if [ -f "$PROOF_FILE" ]; then
# Check cached status first
CACHED_STATUS=$(get_cached_status "$COMMIT" || echo "")
if [ "$CACHED_STATUS" = "attested" ]; then
continue
fi
# 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))
if [ "$CACHE_AGE" -lt 600 ]; then
continue
fi
# 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))
fi
fi
done
# Update latest proof symlink
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
# 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
done
echo "[ots] Backfill complete: $UPDATED proofs updated"

1
test9.txt Normal file
View file

@ -0,0 +1 @@
# Test self-contained hooks