commit 50bd396c2b98872c898690347a0115349e45fcaf Author: Krystie Date: Tue Apr 21 01:53:34 2026 -0700 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 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..6a735bc --- /dev/null +++ b/.env.example @@ -0,0 +1,14 @@ +# Bootstrap Server Configuration +# Copy to .env and fill in your values + +# Domain for the bootstrap server (clearnet) +BOOTSTRAP_DOMAIN=bootstrap.cryptographic-triangles.org + +# Directory where bootstrap archives are stored (full path) +BOOTSTRAP_DIR=/var/www/triangles-bootstrap + +# Tor hidden service port (internal, should match torrc) +TOR_HS_PORT=80 + +# Contact email for Let's Encrypt +EMAIL=admin@example.com diff --git a/README.md b/README.md new file mode 100644 index 0000000..db108e3 --- /dev/null +++ b/README.md @@ -0,0 +1,151 @@ +# TRI Bootstrap Server + +Self-hosted bootstrap server for the Triangles (TRI) cryptocurrency network. + +Serves blockchain bootstrap archives over both **clearnet (HTTPS)** and **Tor onion**, enabling new nodes to sync quickly without downloading 2M+ blocks from peers. + +--- + +## What is a Bootstrap Server? + +When a new Triangles node starts with no chain data, it needs a trusted source of truth. Rather than syncing from random peers (which might be on a minority/forked chain), the node downloads a signed, known-good blockchain snapshot from a trusted bootstrap URL. + +This server provides: +- **HTTPS endpoint** — for nodes on clearnet +- **Tor onion endpoint** — for nodes that prefer or require Tor anonymity + +--- + +## Architecture + +``` + ┌─────────────────────────────────────┐ + │ Bootstrap Server │ + │ (your server, e.g. DNS2) │ + │ │ + New TRI Node ─────►│ Caddy (HTTPS :443) ───► static │ + (clearnet) │ │ │ + │ Tor HS (:80) ──────► same static │ + │ │ │ + └─────────│───────────────────────────┘ + │ + Triangles Bootstrap Archive + (blk0001.dat + txleveldb + database/) +``` + +--- + +## Quick Start + +### 1. Clone / Download This Repo + +```bash +git clone https://github.com/SamiAhmed7777/tri-bootstrap-server.git +cd tri-bootstrap-server +``` + +### 2. Configure Environment + +```bash +cp .env.example .env +# Edit .env with your settings +``` + +Key variables: +- `BOOTSTRAP_DIR` — where bootstrap archives are stored +- `TOR_HS_PORT` — internal port for Tor hidden service (default: 80) +- `CUDDLE_DOMAIN` — your domain (e.g. `bootstrap.cryptographic-triangles.org`) + +### 3. Run + +```bash +# Using the setup script +sudo ./scripts/setup.sh + +# Or manually: +sudo cp tor/torrc /etc/tor/tri-bootstrap/torrc +sudo systemctl enable --now tor@tri-bootstrap +sudo cp systemd/tri-bootstrap-caddy.service /etc/systemd/system/ +sudo systemctl enable --now tri-bootstrap-caddy +``` + +### 4. Generate Your First Bootstrap + +```bash +# On a trusted, synced TRI node: +./scripts/generate-bootstrap.sh /path/to/.triangles + +# Upload the archive to your server: +scp triangles-bootstrap.tar.gz user@your-server:/opt/triangles-bootstrap/ +``` + +--- + +## Scripts + +| Script | Purpose | +|--------|---------| +| `generate-bootstrap.sh` | Creates a `triangles-bootstrap.tar.gz` from a running node's chain data | +| `publish-bootstrap.sh` | Uploads to Dropbox (optional bridge) | +| `verify-bootstrap.sh` | Verifies archive integrity and chain hash | + +--- + +## Tor Onion Setup + +The server automatically creates a Tor hidden service. The `.onion` address is stored in: + +``` +/var/lib/tor/tri-bootstrap/hostname +``` + +Share this address with nodes that need Tor-only connectivity. + +--- + +## For Node Operators + +In your TRI node's `triangles.conf`: + +```ini +# Use this bootstrap server +bootstrap=https://bootstrap.your-domain.com/triangles-bootstrap.tar.gz + +# Or via Tor (replace with your server's onion address) +bootstrap=http://your-onion-address.onion/triangles-bootstrap.tar.gz +``` + +--- + +## Repo Structure + +``` +tri-bootstrap-server/ +├── README.md +├── LICENSE +├── .env.example +├── caddy/ +│ └── Caddyfile.template +├── tor/ +│ └── torrc.template +├── scripts/ +│ ├── setup.sh +│ ├── generate-bootstrap.sh +│ ├── publish-bootstrap.sh +│ └── verify-bootstrap.sh +├── systemd/ +│ ├── tor-bootstrap.service +│ └── tri-bootstrap-caddy.service +└── docs/ + ├── bootstrap-format.md + └── operator-guide.md +``` + +--- + +## Security Notes + +- The bootstrap archive contains the blockchain up to a point in time — it is NOT a wallet and contains no keys +- Always generate bootstrap from a **trusted, verified canonical node** +- Verify the `bestblockhash` of your bootstrap against known-good references before publishing +- Tor hidden service provides anonymity for nodes that need it, but the clearnet HTTPS route is faster for most users diff --git a/caddy/Caddyfile.template b/caddy/Caddyfile.template new file mode 100644 index 0000000..36cd95a --- /dev/null +++ b/caddy/Caddyfile.template @@ -0,0 +1,66 @@ +# Caddyfile for TRI Bootstrap Server +# Serves static bootstrap archives over HTTPS (and optionally Tor) + +############################################################################### +# IMPORTANT: Set these values before using this template: +# +# 1. Replace {{BOOTSTRAP_DOMAIN}} with your domain +# (e.g. bootstrap.cryptographic-triangles.org) +# +# 2. Replace {{EMAIL}} with your email for Let's Encrypt +# (e.g. admin@yourdomain.com) +# +# 3. Replace {{BOOTSTRAP_DIR}} with your actual bootstrap archive directory +# (e.g. /var/www/triangles-bootstrap) +# +# 4. If serving Tor onion, uncomment the @onion block below +############################################################################### + +# Global settings +{ + email {{EMAIL}} + admin off + auto_https off +} + +# Serve the bootstrap archive +{{BOOTSTRAP_DOMAIN}}:443 { + root * {{BOOTSTRAP_DIR}} + + # Enable gzip/brotli compression for large files + encode gzip zstd + + # Cache-control for bootstrap files — long TTL since archives don't change often + @bootstrap { + path /triangles-bootstrap*.tar.gz /tri-bootstrap*.tar.gz + } + header @bootstrap { + Cache-Control "public, max-age=86400, stale-while-revalidate=3600" + Content-Type "application/octet-stream" + } + + # Don't cache HTML or index pages (none should exist here, but safety net) + @html { + path *.html *.htm + } + respond @html 404 + + # Serve all files as downloads + file_server { + download true + } + + # Optional: Log requests + log { + output file /var/log/tri-bootstrap/access.log + } +} + +# Tor hidden service block (uncomment if using Tor) +# @onion onion { +# v3en +# port 80 +# } +# {{BOOTSTRAP_DOMAIN}}:80 { +# respond @onion "Tor is not configured. Please use HTTPS." 400 +# } diff --git a/docs/operator-guide.md b/docs/operator-guide.md new file mode 100644 index 0000000..94af72a --- /dev/null +++ b/docs/operator-guide.md @@ -0,0 +1,129 @@ +# Operator Guide — Running a TRI Bootstrap Server + +This guide covers everything a node operator needs to know to run a TRI bootstrap server in production. + +## Overview + +The bootstrap server serves blockchain snapshot files to new TRI nodes that need to sync quickly without downloading from the peer-to-peer network. It is **not** a full node itself — it is a simple static file server. + +## When to Update the Bootstrap + +Update the bootstrap when: +- A significant chain fork has been resolved +- A new TRI release includes consensus changes +- The existing bootstrap is causing nodes to land on the wrong chain + +## Step-by-Step: Updating the Bootstrap + +### 1. Generate the Bootstrap on a Trusted Node + +On a node that is fully synced and on the canonical chain: + +```bash +# Get current block info first +./triangles-cli getblockcount +./triangles-cli getbestblockhash + +# Generate the bootstrap (from the TRI Bootstrap Server repo) +./scripts/generate-bootstrap.sh ~/.triangles + +# Review the output: +# Block height: XXXXXX +# Best block hash: XXXXXXXX... +# SHA256: XXXXXXXX... +``` + +**Compare the best block hash** against: +- Another trusted synced node: `./triangles-cli -rpcconnect=OTHER_IP getbestblockhash` +- The community Discord/Telegram if available +- Block explorers + +Only proceed if the hash matches a known-good reference. + +### 2. Transfer to the Server + +```bash +# Direct upload (if you have SSH access) +scp triangles-bootstrap-YYYY-MM-DD.tar.gz user@your-server:/var/www/triangles-bootstrap/ + +# Or via Dropbox bridge: +./scripts/publish-bootstrap.sh triangles-bootstrap-YYYY-MM-DD.tar.gz +``` + +### 3. Install on the Server + +```bash +# On the bootstrap server: +cd /var/www/triangles-bootstrap + +# Create a DATED archive (for record-keeping) +cp triangles-bootstrap-YYYY-MM-DD.tar.gz triangles-bootstrap-YYYY-MM-DD.tar.gz.backup + +# Update the symlink (so the canonical URL always serves latest) +ln -sf triangles-bootstrap-YYYY-MM-DD.tar.gz triangles-bootstrap.tar.gz + +# Verify +sha256sum triangles-bootstrap.tar.gz +# Should match what you generated locally +``` + +### 4. Announce + +Notify node operators of the new bootstrap URL and block height/hash so they can update their nodes. + +## Monitoring + +### Check the Server is Responding + +```bash +# HTTPS +curl -I https://bootstrap.your-domain.com/triangles-bootstrap.tar.gz + +# Should return: HTTP/2 200 +# Content-Length: XXXXXXXX +# Content-Type: application/octet-stream +``` + +### Check the Tor Onion + +```bash +# Get your onion address +cat /var/lib/tor/tri-bootstrap/hostname + +# Test it (from a machine with Tor installed) +torify curl -I http://your-onion-address.onion/triangles-bootstrap.tar.gz +``` + +### Log Analysis + +Check access logs: +```bash +tail -f /var/log/tri-bootstrap/access.log +``` + +## Troubleshooting + +### Curl returns 404 +- The archive file doesn't exist in the expected directory +- Check the Caddyfile `root` directive + +### TLS certificate errors +- Caddy might need to reload after DNS change +- Run: `systemctl reload caddy` + +### Tor onion not responding +- Check Tor is running: `systemctl status tor@tri-bootstrap` +- Check logs: `journalctl -u tor@tri-bootstrap` +- Verify hostname: `cat /var/lib/tor/tri-bootstrap/hostname` + +### Bootstrap still serving old chain +- A node downloaded the old archive before you updated +- Node operators need to delete their local chain state and re-download + +## Security Considerations + +1. **Wallet safety**: Never include wallet files in the bootstrap archive. The generate script explicitly excludes `wallet.dat*`. +2. **Only serve from trusted nodes**: Generate bootstrap from a known-good synced node. +3. **Verify hashes**: Always cross-check the bestblockhash against other trusted references. +4. **No authentication**: The bootstrap server serves public files with no auth. This is intentional — any node should be able to download. +5. **Tor anonymity**: Using the Tor hidden service provides anonymity for nodes that need it, but is not required for clearnet users. diff --git a/scripts/generate-bootstrap.sh b/scripts/generate-bootstrap.sh new file mode 100755 index 0000000..263bc63 --- /dev/null +++ b/scripts/generate-bootstrap.sh @@ -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" <