Harden onion boot flow and deepen site surfaces
Add persistent onion key backup and restore, improve startup resilience, and flesh out the major site verticals with richer navigation, search coverage, and operator documentation. Made-with: Cursor
This commit is contained in:
248
start.sh
Executable file
248
start.sh
Executable file
@@ -0,0 +1,248 @@
|
||||
#!/usr/bin/env bash
|
||||
# CyberLux — single entry: regenerate onion config, deps, build, Tor/nginx,
|
||||
# self-heal onion key directories from backup, wait for hostname readiness,
|
||||
# print all .onion URLs, back them up, then start Next.
|
||||
#
|
||||
# ./start.sh
|
||||
# git pull && ./start.sh
|
||||
#
|
||||
# Env (optional):
|
||||
# CYBERLUX_SKIP_GIT_PULL=1
|
||||
# CYBERLUX_SKIP_TOR=1 — local only → http://127.0.0.1:3000
|
||||
# CYBERLUX_DEPLOY_SEED=n
|
||||
# CYBERLUX_RANDOM_SEED=1
|
||||
# CYBERLUX_SKIP_ONION_BACKUP=1
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
REPO="$(cd "$(dirname "$0")" && pwd)"
|
||||
cd "${REPO}"
|
||||
|
||||
if ! command -v node >/dev/null 2>&1; then
|
||||
echo "[!] node not found (required for scripts/generate-onion-config.cjs)."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] Regenerating Tor/nginx/app maps from scripts/onion-nodes.json"
|
||||
node "${REPO}/scripts/generate-onion-config.cjs"
|
||||
|
||||
TOR_DIRS_FILE="${REPO}/scripts/generated/tor-dirs.txt"
|
||||
LABELS_FILE="${REPO}/scripts/generated/onion-labels.tsv"
|
||||
RANGE_FILE="${REPO}/scripts/generated/onion-port-range.txt"
|
||||
RESTORE_SCRIPT="${REPO}/scripts/restore-onion-keys.sh"
|
||||
BACKUP_SCRIPT="${REPO}/scripts/backup-onion-keys.sh"
|
||||
|
||||
if [[ ! -f "${TOR_DIRS_FILE}" ]]; then
|
||||
echo "[!] Missing ${TOR_DIRS_FILE} — generator failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
declare -A CYBERLUX_ROLE=()
|
||||
if [[ -f "${LABELS_FILE}" ]]; then
|
||||
while IFS=$'\t' read -r dir desc || [[ -n "${dir}" ]]; do
|
||||
[[ -z "${dir}" ]] && continue
|
||||
CYBERLUX_ROLE["${dir}"]="${desc}"
|
||||
done < "${LABELS_FILE}"
|
||||
fi
|
||||
|
||||
read -r ONION_PORT_MIN ONION_PORT_MAX < "${RANGE_FILE}" || true
|
||||
ONION_PORT_MIN="${ONION_PORT_MIN:-8080}"
|
||||
ONION_PORT_MAX="${ONION_PORT_MAX:-8122}"
|
||||
|
||||
mapfile -t CYBERLUX_TOR_DIRS < "${TOR_DIRS_FILE}"
|
||||
|
||||
count_ready_hostnames() {
|
||||
local ready=0
|
||||
local dir
|
||||
for dir in "${CYBERLUX_TOR_DIRS[@]}"; do
|
||||
[[ -z "${dir}" ]] && continue
|
||||
if sudo test -s "/var/lib/tor/${dir}/hostname" 2>/dev/null; then
|
||||
((ready++)) || true
|
||||
fi
|
||||
done
|
||||
echo "${ready}"
|
||||
}
|
||||
|
||||
wait_for_all_hostnames() {
|
||||
local max_wait="${1:-90}"
|
||||
local waited=0
|
||||
local total=0
|
||||
local ready=0
|
||||
total="${#CYBERLUX_TOR_DIRS[@]}"
|
||||
|
||||
while (( waited < max_wait )); do
|
||||
ready="$(count_ready_hostnames)"
|
||||
if (( ready >= total )); then
|
||||
return 0
|
||||
fi
|
||||
sleep 1
|
||||
((waited++)) || true
|
||||
done
|
||||
|
||||
ready="$(count_ready_hostnames)"
|
||||
echo "[!] Timeout waiting for all onion hostnames (${ready}/${total} ready)." >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
print_onion_banner() {
|
||||
echo ""
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo " CYBERLUX — copy these .onion URLs (Tor Browser, http:// only)"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
local any=0
|
||||
local ready=0
|
||||
for dir in "${CYBERLUX_TOR_DIRS[@]}"; do
|
||||
[[ -z "${dir}" ]] && continue
|
||||
local f="/var/lib/tor/${dir}/hostname"
|
||||
local onion=""
|
||||
if onion=$(sudo cat "${f}" 2>/dev/null | tr -d '\n'); then
|
||||
any=1
|
||||
((ready++)) || true
|
||||
printf " %-26s %s\n" "${dir}" "http://${onion}"
|
||||
printf " %-26s %s\n" "" "${CYBERLUX_ROLE[$dir]:-}"
|
||||
echo ""
|
||||
else
|
||||
printf " %-26s %s\n" "${dir}" "(hostname not readable — sudo cat ${f})"
|
||||
echo ""
|
||||
fi
|
||||
done
|
||||
if [[ "${any}" -eq 0 ]]; then
|
||||
echo " [!] Could not read hostnames. Check: sudo systemctl status tor"
|
||||
echo ""
|
||||
fi
|
||||
echo " Hostnames ready: ${ready}/${#CYBERLUX_TOR_DIRS[@]}"
|
||||
echo ""
|
||||
echo " Loopback (do not bind publicly):"
|
||||
echo " Next.js http://127.0.0.1:3000"
|
||||
echo " nginx 127.0.0.1:${ONION_PORT_MIN}–${ONION_PORT_MAX} → Next (one port per .onion)"
|
||||
echo ""
|
||||
echo " Service status:"
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
if systemctl is-active --quiet tor@default.service 2>/dev/null || systemctl is-active --quiet tor 2>/dev/null; then
|
||||
echo " tor: active"
|
||||
else
|
||||
echo " tor: inactive or unknown"
|
||||
fi
|
||||
if systemctl is-active --quiet nginx 2>/dev/null; then
|
||||
echo " nginx: active"
|
||||
else
|
||||
echo " nginx: inactive or unknown"
|
||||
fi
|
||||
else
|
||||
echo " (systemctl not found)"
|
||||
fi
|
||||
echo ""
|
||||
echo " Onion key backup:"
|
||||
echo " /var/backups/cyberlux-onion-keys/current"
|
||||
echo ""
|
||||
echo " Firewall (optional): sudo bash ${REPO}/scripts/classroom-ufw.sh"
|
||||
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# --- Deploy seed (optional) ---
|
||||
if [[ -n "${CYBERLUX_DEPLOY_SEED:-}" ]]; then
|
||||
export NEXT_PUBLIC_DEPLOY_SEED="${CYBERLUX_DEPLOY_SEED}"
|
||||
echo "[*] NEXT_PUBLIC_DEPLOY_SEED=${NEXT_PUBLIC_DEPLOY_SEED} (from env)"
|
||||
elif [[ "${CYBERLUX_RANDOM_SEED:-}" == "1" ]]; then
|
||||
export NEXT_PUBLIC_DEPLOY_SEED=$((RANDOM % 10000))
|
||||
echo "[*] NEXT_PUBLIC_DEPLOY_SEED=${NEXT_PUBLIC_DEPLOY_SEED} (random)"
|
||||
fi
|
||||
|
||||
if [[ -d .git ]] && [[ "${CYBERLUX_SKIP_GIT_PULL:-}" != "1" ]]; then
|
||||
echo "[*] git pull --ff-only"
|
||||
if ! git pull --ff-only; then
|
||||
echo "[!] git pull failed (offline, dirty tree, or no upstream). Continuing — use CYBERLUX_SKIP_GIT_PULL=1 to skip this step."
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! command -v npm >/dev/null 2>&1; then
|
||||
echo "[!] npm not found. Install Node.js LTS."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "[*] npm install"
|
||||
npm install
|
||||
|
||||
# Root-owned .next (e.g. after `sudo npm run build`) breaks `next build` — fix with sudo once.
|
||||
if [[ -d "${REPO}/.next" ]] && [[ ! -w "${REPO}/.next" ]]; then
|
||||
echo "[*] .next/ is not writable (often root-owned). Repairing ownership (sudo may ask for password)…"
|
||||
if command -v sudo >/dev/null 2>&1; then
|
||||
sudo chown -R "$(id -un):$(id -gn)" "${REPO}/.next" || {
|
||||
echo "[!] Could not chown .next. Run manually:"
|
||||
echo " sudo bash ${REPO}/scripts/fix-next-perms.sh"
|
||||
exit 1
|
||||
}
|
||||
else
|
||||
echo "[!] No sudo — run as root once: chown -R \$(whoami) ${REPO}/.next"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[*] npm run build"
|
||||
if ! npm run build; then
|
||||
echo ""
|
||||
echo "[!] Build failed. Common fix after a root-owned build:"
|
||||
echo " sudo bash ${REPO}/scripts/fix-next-perms.sh"
|
||||
echo " ./start.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if command -v ss >/dev/null 2>&1; then
|
||||
if ss -ltn 2>/dev/null | grep -qE '127\.0\.0\.1:3000\b'; then
|
||||
echo "[!] Something is already listening on 127.0.0.1:3000 — stop it first."
|
||||
exit 1
|
||||
fi
|
||||
elif command -v lsof >/dev/null 2>&1; then
|
||||
if lsof -iTCP:3000 -sTCP:LISTEN >/dev/null 2>&1; then
|
||||
echo "[!] Port 3000 is in use."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "${CYBERLUX_SKIP_TOR:-}" == "1" ]]; then
|
||||
echo "[*] CYBERLUX_SKIP_TOR=1 — skipping Tor/nginx. Open http://127.0.0.1:3000"
|
||||
echo ""
|
||||
else
|
||||
if ! command -v sudo >/dev/null 2>&1; then
|
||||
echo "[!] sudo required for Tor/nginx install."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bin_exists() {
|
||||
command -v "$1" >/dev/null 2>&1 || [[ -x "/usr/bin/$1" ]] || [[ -x "/usr/sbin/$1" ]] || [[ -x "/sbin/$1" ]]
|
||||
}
|
||||
|
||||
for bin in tor nginx; do
|
||||
if ! bin_exists "$bin"; then
|
||||
echo "[!] Missing '${bin}'. On Debian/Ubuntu: sudo apt install tor nginx"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ! -f "${REPO}/scripts/install-tor-onion.sh" ]]; then
|
||||
echo "[!] Missing install script."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -f "${RESTORE_SCRIPT}" ]]; then
|
||||
echo "[*] Restoring backed-up onion keys (missing dirs only)…"
|
||||
sudo bash "${RESTORE_SCRIPT}"
|
||||
fi
|
||||
|
||||
echo "[*] Installing Tor + nginx (sudo)…"
|
||||
sudo env CYBERLUX_INSTALL_QUIET=1 bash "${REPO}/scripts/install-tor-onion.sh"
|
||||
|
||||
echo "[*] Waiting for Tor hostname files…"
|
||||
wait_for_all_hostnames 90 || true
|
||||
|
||||
if [[ "${CYBERLUX_SKIP_ONION_BACKUP:-}" != "1" ]] && [[ -f "${BACKUP_SCRIPT}" ]]; then
|
||||
echo "[*] Refreshing onion key backup set…"
|
||||
sudo bash "${BACKUP_SCRIPT}" || echo "[!] Onion key backup refresh failed."
|
||||
fi
|
||||
|
||||
print_onion_banner
|
||||
fi
|
||||
|
||||
echo "[*] Starting Next.js on 127.0.0.1:3000 (Ctrl+C to stop)"
|
||||
exec npm run start:onion
|
||||
Reference in New Issue
Block a user