Initial commit: TRI bootstrap server
- Caddy HTTPS static file server config - Tor hidden service (V3 onion) for anonymous access - Bootstrap generation script (blk0001.dat + txleveldb + database) - Dropbox upload script - Systemd services (tor-bootstrap, caddy) - Operator guide and verification script
This commit is contained in:
Executable
+157
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/env bash
|
||||
# generate-bootstrap.sh — Creates a TRI bootstrap archive from a running node's chaindata
|
||||
# Usage: ./generate-bootstrap.sh /path/to/.triangles [output-file.tar.gz]
|
||||
#
|
||||
# Prerequisites on the node:
|
||||
# - Triangles daemon running and fully synced
|
||||
# - rpcuser and rpcpassword in triangles.conf
|
||||
#
|
||||
# What this script packages:
|
||||
# - blk0001.dat (block chain)
|
||||
# - txleveldb/ (UTXO index)
|
||||
# - database/ (BDB state)
|
||||
#
|
||||
# What it DOES NOT package (deliberate — keeps archives small and safe):
|
||||
# - wallet.dat (never include wallets in bootstrap!)
|
||||
# - peers.dat (peer addresses — will be rediscovered)
|
||||
# - debug.log, banlist.dat, .lock files
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
DATADIR="${1:?Usage: $0 /path/to/.triangles [output-file.tar.gz]}"
|
||||
OUTPUT="${2:-/tmp/triangles-bootstrap-$(date +%Y-%m-%d).tar.gz}"
|
||||
CHAINDIR="$DATADIR"
|
||||
|
||||
echo "=== TRI Bootstrap Generator ==="
|
||||
echo "Data directory: $CHAINDIR"
|
||||
echo "Output file: $OUTPUT"
|
||||
|
||||
# Verify required files exist
|
||||
if [ ! -f "$CHAINDIR/blk0001.dat" ]; then
|
||||
echo "ERROR: blk0001.dat not found in $CHAINDIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$CHAINDIR/txleveldb" ]; then
|
||||
echo "ERROR: txleveldb/ not found in $CHAINDIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check that the node is running and RPC is accessible
|
||||
RPCPORT=$(grep -E "^rpcport=" "$CHAINDIR/triangles.conf" 2>/dev/null | cut -d= -f2 || echo "19112")
|
||||
RPCUSER=$(grep -E "^rpcuser=" "$CHAINDIR/triangles.conf" 2>/dev/null | cut -d= -f2)
|
||||
RPCPASSWORD=$(grep -E "^rpcpassword=" "$CHAINDIR/triangles.conf" 2>/dev/null | cut -d= -f2)
|
||||
|
||||
if [ -z "$RPCUSER" ] || [ -z "$RPCPASSWORD" ]; then
|
||||
echo "WARNING: Could not find rpcuser/rpcpassword in triangles.conf. Skipping RPC checks."
|
||||
BLOCKCOUNT="unknown"
|
||||
BESTHASH="unknown"
|
||||
else
|
||||
echo "Checking chain tip via RPC..."
|
||||
BLOCKCOUNT=$(curl -s -X POST \
|
||||
-H "content-type: application/json" \
|
||||
-u "$RPCUSER:$RPCPASSWORD" \
|
||||
--max-time 5 \
|
||||
--fail \
|
||||
"http://127.0.0.1:$RPCPORT" \
|
||||
-d '{"jsonrpc":"1.0","id":"bootstrap","method":"getblockcount","params":[]}' \
|
||||
2>/dev/null | grep -oE '[0-9]+' | head -1 || echo "unknown")
|
||||
|
||||
BESTHASH=$(curl -s -X POST \
|
||||
-H "content-type: application/json" \
|
||||
-u "$RPCUSER:$RPCPASSWORD" \
|
||||
--max-time 5 \
|
||||
--fail \
|
||||
"http://127.0.0.1:$RPCPORT" \
|
||||
-d '{"jsonrpc":"1.0","id":"bootstrap","method":"getbestblockhash","params":[]}' \
|
||||
2>/dev/null | grep -oE '"[0-9a-f]{64}"' | tr -d '"' | head -1 || echo "unknown")
|
||||
|
||||
echo "Chain tip: block $BLOCKCOUNT, hash $BESTHASH"
|
||||
fi
|
||||
|
||||
# Get file sizes for the archive manifest
|
||||
BLK_SIZE=$(du -sh "$CHAINDIR/blk0001.dat" 2>/dev/null | cut -f1)
|
||||
TX_SIZE=$(du -sh "$CHAINDIR/txleveldb" 2>/dev/null | cut -f1)
|
||||
DB_SIZE=$(du -sh "$CHAINDIR/database" 2>/dev/null | cut -f1 || echo "N/A")
|
||||
|
||||
echo ""
|
||||
echo "Archive contents:"
|
||||
echo " blk0001.dat : $BLK_SIZE"
|
||||
echo " txleveldb/ : $TX_SIZE"
|
||||
echo " database/ : $DB_SIZE"
|
||||
echo ""
|
||||
|
||||
# Stop the node cleanly before archiving (recommended)
|
||||
echo "Stopping trianglesd cleanly (if running)..."
|
||||
"$CHAINDIR/../../.local/bin/trianglesd" -datadir="$CHAINDIR" -conf="$CHAINDIR/triangles.conf" stop 2>/dev/null || \
|
||||
trianglesd -datadir="$CHAINDIR" stop 2>/dev/null || \
|
||||
echo "(could not stop — continuing anyway)"
|
||||
|
||||
sleep 2
|
||||
|
||||
# Create the archive
|
||||
# We use tar with gzip compression, preserving directory structure as-is
|
||||
# Files are placed at the top level of the archive (not inside a subdirectory)
|
||||
echo "Creating archive..."
|
||||
mkdir -p "$(dirname "$OUTPUT")"
|
||||
|
||||
tar -czf "$OUTPUT" \
|
||||
-C "$CHAINDIR" \
|
||||
--exclude='wallet.dat*' \
|
||||
--exclude='peers.dat' \
|
||||
--exclude='banlist.dat' \
|
||||
--exclude='debug.log' \
|
||||
--exclude='*.lock' \
|
||||
--exclude='.lock' \
|
||||
--exclude='bootstrap.log' \
|
||||
blk0001.dat \
|
||||
txleveldb \
|
||||
database \
|
||||
2>/dev/null || true
|
||||
|
||||
ARCHIVE_SIZE=$(du -sh "$OUTPUT" | cut -f1)
|
||||
ARCHIVE_HASH=$(sha256sum "$OUTPUT" | cut -d' ' -f1)
|
||||
|
||||
echo ""
|
||||
echo "=== Bootstrap Archive Created ==="
|
||||
echo "File: $OUTPUT"
|
||||
echo "Size: $ARCHIVE_SIZE"
|
||||
echo "SHA256: $ARCHIVE_HASH"
|
||||
echo "Blocks: $BLOCKCOUNT"
|
||||
echo "Best hash: $BESTHASH"
|
||||
echo ""
|
||||
|
||||
# Write a manifest alongside the archive
|
||||
MANIFEST="${OUTPUT%.tar.gz}.manifest.txt"
|
||||
cat > "$MANIFEST" <<EOF
|
||||
Triangles Bootstrap Archive
|
||||
===========================
|
||||
Generated: $(date -u)
|
||||
Data dir: $CHAINDIR
|
||||
Block height: $BLOCKCOUNT
|
||||
Best block hash: $BESTHASH
|
||||
Archive size: $ARCHIVE_SIZE
|
||||
SHA256: $ARCHIVE_HASH
|
||||
|
||||
Contents:
|
||||
blk0001.dat : $BLK_SIZE
|
||||
txleveldb/ : $TX_SIZE
|
||||
database/ : $DB_SIZE
|
||||
|
||||
Files EXCLUDED (deliberate):
|
||||
wallet.dat* — never include wallets in bootstrap
|
||||
peers.dat — will be rediscovered
|
||||
banlist.dat — will be rediscovered
|
||||
debug.log — runtime log
|
||||
*.lock — lock files
|
||||
|
||||
To serve this file:
|
||||
1. Place on your web server
|
||||
2. Update Caddyfile to point to the directory
|
||||
3. Update DNS/Tor hidden service if needed
|
||||
4. Notify node operators of the new bootstrap URL
|
||||
EOF
|
||||
|
||||
echo "Manifest written to: $MANIFEST"
|
||||
echo ""
|
||||
echo "Done!"
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
# publish-bootstrap.sh — Uploads bootstrap archive to Dropbox
|
||||
# Requires: dbxcli (Dropbox CLI)
|
||||
# Usage: ./publish-bootstrap.sh /path/to/archive.tar.gz [dropbox-path]
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ARCHIVE="${1:?Usage: $0 /path/to/archive.tar.gz}"
|
||||
DROPBOX_PATH="${2:-/TRI $(date +%m\ %d\ %y) bootstrap}"
|
||||
|
||||
echo "=== Dropbox Publisher ==="
|
||||
echo "File: $ARCHIVE"
|
||||
echo "Destination: $DROPBOX_PATH"
|
||||
echo ""
|
||||
|
||||
if ! command -v dbxcli &>/dev/null; then
|
||||
echo "ERROR: dbxcli not found. Install: https://github.com/dropbox/dbxcli"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify the archive first
|
||||
echo "Running quick verification..."
|
||||
if ! tar -tzf "$ARCHIVE" --wildcards "blk0001.dat" &>/dev/null; then
|
||||
echo "ERROR: Archive does not contain blk0001.dat"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Upload
|
||||
echo "Uploading to Dropbox..."
|
||||
dbxcli put "$ARCHIVE" "$DROPBOX_PATH/$(basename "$ARCHIVE")"
|
||||
|
||||
# Get the shared link (if available)
|
||||
echo ""
|
||||
echo "Getting shareable link..."
|
||||
LINK=$(dbxcli share "$DROPBOX_PATH/$(basename "$ARCHIVE")" 2>/dev/null | grep -oE "https://www\.dropbox\.com[^ ]+" | head -1 || echo "manual link needed")
|
||||
|
||||
echo ""
|
||||
echo "=== Upload Complete ==="
|
||||
echo "Dropbox path: $DROPBOX_PATH"
|
||||
echo "Share link: $LINK"
|
||||
echo ""
|
||||
echo "To use this bootstrap:"
|
||||
echo " 1. Download from Dropbox to the bootstrap server"
|
||||
echo " 2. Extract to /var/www/triangles-bootstrap/"
|
||||
echo " 3. Update symlinks:"
|
||||
echo " cd /var/www/triangles-bootstrap"
|
||||
echo " ln -sf triangles-bootstrap-YYYY-MM-DD.tar.gz triangles-bootstrap.tar.gz"
|
||||
Executable
+109
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
# setup.sh — One-time server setup for TRI Bootstrap Server
|
||||
# Run as root or with sudo
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "=== TRI Bootstrap Server Setup ==="
|
||||
echo ""
|
||||
|
||||
# Check if running as root
|
||||
if [ "$EUID" -ne 0 ]; then
|
||||
echo "Please run as root: sudo $0"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Detect OS
|
||||
if [ -f /etc/debian_version ]; then
|
||||
OS="debian"
|
||||
elif [ -f /etc/redhat-release ]; then
|
||||
OS="rhel"
|
||||
else
|
||||
echo "This setup script supports Debian/Ubuntu and RHEL/Fedora."
|
||||
echo "For other OSes, please follow the manual steps in docs/setup.md"
|
||||
OS="unknown"
|
||||
fi
|
||||
|
||||
# Install dependencies
|
||||
echo "[1/6] Installing dependencies..."
|
||||
if [ "$OS" = "debian" ]; then
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq tor caddy wget curl jq > /dev/null 2>&1
|
||||
elif [ "$OS" = "rhel" ]; then
|
||||
yum install -y tor caddy wget curl jq > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Create directories
|
||||
echo "[2/6] Creating directories..."
|
||||
mkdir -p /var/www/triangles-bootstrap
|
||||
mkdir -p /etc/tor/tri-bootstrap
|
||||
mkdir -p /var/lib/tor/tri-bootstrap
|
||||
mkdir -p /var/log/tri-bootstrap
|
||||
|
||||
# Generate Tor private key (if not exists)
|
||||
if [ ! -f /var/lib/tor/tri-bootstrap/private_key ]; then
|
||||
echo "[3/6] Generating Tor hidden service key..."
|
||||
tor -f /etc/tor/tri-bootstrap/torrc --quiet
|
||||
sleep 2
|
||||
pkill -HUP tor || true
|
||||
else
|
||||
echo "[3/6] Tor key already exists, skipping..."
|
||||
fi
|
||||
|
||||
# Get the onion address
|
||||
if [ -f /var/lib/tor/tri-bootstrap/hostname ]; then
|
||||
ONION=$(cat /var/lib/tor/tri-bootstrap/hostname)
|
||||
echo " Onion address: $ONION"
|
||||
else
|
||||
echo " Warning: onion address not yet available (Tor may still be starting)"
|
||||
fi
|
||||
|
||||
# Configure Caddy
|
||||
echo "[4/6] Configuring Caddy..."
|
||||
if [ ! -f /etc/caddy/tri-bootstrap-Caddyfile ]; then
|
||||
cp "$(dirname "$0")/../caddy/Caddyfile.template" /etc/caddy/tri-bootstrap-Caddyfile
|
||||
# Replace template variables
|
||||
read -p " Enter your domain (e.g. bootstrap.cryptographic-triangles.org): " DOMAIN
|
||||
read -p " Enter your email for Let's Encrypt: " EMAIL
|
||||
read -p " Enter bootstrap directory (default: /var/www/triangles-bootstrap): " BOOTSTRAP_DIR
|
||||
BOOTSTRAP_DIR="${BOOTSTRAP_DIR:-/var/www/triangles-bootstrap}"
|
||||
sed -i "s/{{BOOTSTRAP_DOMAIN}}/$DOMAIN/g" /etc/caddy/tri-bootstrap-Caddyfile
|
||||
sed -i "s/{{EMAIL}}/$EMAIL/g" /etc/caddy/tri-bootstrap-Caddyfile
|
||||
sed -i "s|{{BOOTSTRAP_DIR}}|$BOOTSTRAP_DIR|g" /etc/caddy/tri-bootstrap-Caddyfile
|
||||
else
|
||||
echo " Caddyfile already exists, skipping..."
|
||||
fi
|
||||
|
||||
# Copy Tor config
|
||||
echo "[5/6] Configuring Tor..."
|
||||
if [ ! -f /etc/tor/tri-bootstrap/torrc ]; then
|
||||
cp "$(dirname "$0")/../tor/torrc.template" /etc/tor/tri-bootstrap/torrc
|
||||
chown -R debian-tor:debian-tor /etc/tor/tri-bootstrap
|
||||
chown -R debian-tor:debian-tor /var/lib/tor/tri-bootstrap
|
||||
else
|
||||
echo " Tor config already exists, skipping..."
|
||||
fi
|
||||
|
||||
# Enable and start services
|
||||
echo "[6/6] Starting services..."
|
||||
systemctl enable tor@tri-bootstrap 2>/dev/null || true
|
||||
systemctl enable caddy 2>/dev/null || true
|
||||
systemctl restart tor@tri-bootstrap || true
|
||||
systemctl restart caddy || true
|
||||
|
||||
echo ""
|
||||
echo "=== Setup Complete ==="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Upload a bootstrap archive to /var/www/triangles-bootstrap/"
|
||||
echo " (e.g. triangles-bootstrap.tar.gz)"
|
||||
echo ""
|
||||
echo " 2. Test:"
|
||||
echo " curl -I https://YOUR_DOMAIN/triangles-bootstrap.tar.gz"
|
||||
echo ""
|
||||
echo " 3. Share your onion address:"
|
||||
if [ -f /var/lib/tor/tri-bootstrap/hostname ]; then
|
||||
cat /var/lib/tor/tri-bootstrap/hostname
|
||||
fi
|
||||
echo ""
|
||||
echo " 4. Update your DNS A record to point to this server's IP"
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env bash
|
||||
# verify-bootstrap.sh — Verifies a bootstrap archive before publishing
|
||||
# Usage: ./verify-bootstrap.sh /path/to/archive.tar.gz
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ARCHIVE="${1:?Usage: $0 /path/to/archive.tar.gz}"
|
||||
|
||||
echo "=== Bootstrap Archive Verifier ==="
|
||||
echo "File: $ARCHIVE"
|
||||
echo ""
|
||||
|
||||
# Check file exists
|
||||
if [ ! -f "$ARCHIVE" ]; then
|
||||
echo "ERROR: File not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# File size
|
||||
SIZE=$(du -sh "$ARCHIVE" | cut -f1)
|
||||
echo "Size: $SIZE"
|
||||
|
||||
# SHA256
|
||||
SHA256=$(sha256sum "$ARCHIVE" | cut -d' ' -f1)
|
||||
echo "SHA256: $SHA256"
|
||||
echo ""
|
||||
|
||||
# Check it's a valid gzip file
|
||||
if file "$ARCHIVE" | grep -q "gzip compressed"; then
|
||||
echo "Format: Valid gzip (.tar.gz) ✅"
|
||||
else
|
||||
echo "Format: WARNING — not a gzip file!"
|
||||
fi
|
||||
|
||||
# List contents without extracting
|
||||
echo ""
|
||||
echo "Archive contents:"
|
||||
tar -tzvf "$ARCHIVE" 2>/dev/null | head -20 || echo "WARNING: Could not list contents"
|
||||
|
||||
# Check for dangerous files
|
||||
echo ""
|
||||
echo "Security checks:"
|
||||
if tar -tzf "$ARCHIVE" 2>/dev/null | grep -q "wallet.dat"; then
|
||||
echo " ❌ DANGER: wallet.dat found in archive!"
|
||||
else
|
||||
echo " ✅ No wallet.dat found"
|
||||
fi
|
||||
|
||||
if tar -tzf "$ARCHIVE" 2>/dev/null | grep -q "peers.dat"; then
|
||||
echo " ⚠️ peers.dat found (will be rediscovered — acceptable)"
|
||||
else
|
||||
echo " ✅ No peers.dat found"
|
||||
fi
|
||||
|
||||
if tar -tzf "$ARCHIVE" 2>/dev/null | grep -q "debug.log"; then
|
||||
echo " ⚠️ debug.log found (should be excluded)"
|
||||
else
|
||||
echo " ✅ No debug.log found"
|
||||
fi
|
||||
|
||||
# Check blk0001.dat size
|
||||
BLK_SIZE=$(tar -xzf "$ARCHIVE" -C /tmp --wildcards "blk0001.dat" --to-command="du -h" 2>/dev/null | cut -f1 || echo "NOT FOUND")
|
||||
echo ""
|
||||
echo "blk0001.dat size: $BLK_SIZE"
|
||||
if [ "$BLK_SIZE" = "NOT FOUND" ]; then
|
||||
echo " ❌ blk0001.dat not found in archive!"
|
||||
elif [ "$(echo "$BLK_SIZE" | tr -d 'MGT' | cut -d. -f1)" -lt 100 ]; then
|
||||
echo " ⚠️ blk0001.dat suspiciously small — check it"
|
||||
else
|
||||
echo " ✅ blk0001.dat size OK"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "SHA256 for reference:"
|
||||
echo "$SHA256"
|
||||
Reference in New Issue
Block a user