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:
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
# }
|
||||||
@@ -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.
|
||||||
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"
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Tor hidden service for TRI Bootstrap Server
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
# Run Tor as the debian-tor user (standard on Debian/Ubuntu)
|
||||||
|
User=debian-tor
|
||||||
|
Group=debian-tor
|
||||||
|
ExecStart=/usr/bin/tor -f /etc/tor/tri-bootstrap/torrc
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
# Hardening
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ReadOnlyPaths=/etc/tor/tri-bootstrap
|
||||||
|
WritePaths=/var/lib/tor/tri-bootstrap
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Caddy web server for TRI Bootstrap
|
||||||
|
After=network-online.target tor@tri-bootstrap.service
|
||||||
|
Wants=network-online.target tor@tri-bootstrap.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
Group=root
|
||||||
|
ExecReload=/usr/bin/caddy reload --config /etc/caddy/tri-bootstrap-Caddyfile
|
||||||
|
ExecStart=/usr/bin/caddy run --config /etc/caddy/tri-bootstrap-Caddyfile --adapter caddyfile
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
# Caddy needs to bind to ports 80 and 443
|
||||||
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||||
|
|
||||||
|
# Hardening
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
ProtectSystem=full
|
||||||
|
ProtectHome=yes
|
||||||
|
ReadOnlyPaths=/etc/caddy/tri-bootstrap-Caddyfile
|
||||||
|
ReadWritePaths=/var/log/tri-bootstrap
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Tor configuration for TRI Bootstrap Server Hidden Service
|
||||||
|
# Place at: /etc/tor/tri-bootstrap/torrc
|
||||||
|
# Run as: sudo systemctl enable --now tor@tri-bootstrap
|
||||||
|
|
||||||
|
# Hidden Service Directory
|
||||||
|
HiddenServiceDir /var/lib/tor/tri-bootstrap/
|
||||||
|
|
||||||
|
# Serve HTTP (port 80) via Tor
|
||||||
|
# The Caddy server (or any HTTP server) listens on 127.0.0.1:80
|
||||||
|
HiddenServicePort 80 127.0.0.1:80
|
||||||
|
|
||||||
|
# V3 onion service (current standard)
|
||||||
|
HiddenServiceVersion 3
|
||||||
|
|
||||||
|
# Don't attach to a specific port — use the default
|
||||||
|
# OR force a specific port:
|
||||||
|
HiddenServicePort 80
|
||||||
|
|
||||||
|
# Security settings
|
||||||
|
ExcludeSingleHopRelays 1
|
||||||
Reference in New Issue
Block a user