#!/usr/bin/env bash # Bootstrap the DigiByte.X Wallet SUBPATH deployment — the web bundle # served at `https:///web/` alongside whatever else that host # already serves at `/`. Companion to Caddyfile.wallet-web-snippet. # # Difference from deploy-wallet.sh: # deploy-wallet.sh — wallet OWNS the whole site (Caddyfile.wallet at :443) # deploy-wallet-web — wallet is ONE section of an existing site # (Caddyfile.wallet-web-snippet imported into the # host's existing Caddyfile) # # Usage: # scp -r . user@vps:/opt/dgb-wallet # ssh user@vps # sudo /opt/dgb-wallet/ops/deploy-wallet-web.sh # (optional) --merge /etc/caddy/Caddyfile # (optional) --site-root /var/www/dgb-wallet # # Steps: # 1. install caddy + node20 (if missing) # 2. install monorepo deps, build web-ui (plain production build → # relative ./assets base + runtime fix in index.html) # 3. create dgb-wallet system user, sync bundle to /web/ # 4. install dgb-daemon.service, enable + start # 5. render snippet with SITE_ROOT into /etc/caddy/snippets/dgb-wallet.caddyfile # 6. (--merge) insert `import` line into the given Caddyfile between # sentinel markers; else print manual-merge instructions # 7. caddy validate, reload # 8. print status set -euo pipefail if [[ $EUID -ne 0 ]]; then echo "Run as root (sudo)." >&2 exit 1 fi REPO_DIR="${REPO_DIR:-/opt/dgb-wallet}" SITE_ROOT="/var/www/dgb-wallet" DAEMON_HOME="/var/lib/dgb-wallet" SNIPPET_DIR="/etc/caddy/snippets" SNIPPET_FILE="$SNIPPET_DIR/dgb-wallet.caddyfile" MERGE_TARGET="" OPS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" while [[ $# -gt 0 ]]; do case "$1" in --merge) MERGE_TARGET="${2:-}"; shift 2 ;; --site-root) SITE_ROOT="${2:-}"; shift 2 ;; -h|--help) sed -n '2,25p' "$0"; exit 0 ;; *) echo "Unknown flag: $1" >&2; exit 1 ;; esac done log() { echo -e "\n\033[1;36m>>> $*\033[0m"; } log "Step 1/7: install prerequisites (caddy, node 20)" apt-get update -qq apt-get install -y -qq curl gpg ca-certificates rsync debian-keyring debian-archive-keyring apt-transport-https if ! command -v caddy >/dev/null; then curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list >/dev/null apt-get update -qq apt-get install -y -qq caddy fi if ! command -v node >/dev/null || ! node -v | grep -qE '^v(20|22|24)\.'; then curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt-get install -y -qq nodejs fi log "Step 2/7: install deps and build web-ui bundle (relative ./ base)" cd "$REPO_DIR" npm install --silent # Plain production build. vite.config.ts defaults production to a # RELATIVE base ('./'), and index.html carries an inline script that # re-anchors a at …/web/ at runtime. That single # bundle therefore works both at https:///web/ and behind the # navigate.st BNS gateway (which injects its own ). Do NOT set # DGB_UI_BASE=/web/ here — an absolute base breaks the gateway path. if [[ -n "${DGB_UI_BASE:-}" ]]; then echo "ERROR: DGB_UI_BASE is set ('$DGB_UI_BASE'). Unset it — the subpath deploy relies on the relative './' base." >&2 exit 1 fi npm run build -w @dgb-wallet/web-ui # Sanity check — asset URLs are relative (./assets/*), not absolute. if ! grep -q 'src="\./assets/' "$REPO_DIR/packages/web-ui/dist/index.html"; then echo "ERROR: dist/index.html has no ./assets/ references — build did not produce a relative base" >&2 exit 1 fi if ! grep -q 'indexOf(./web/.)' "$REPO_DIR/packages/web-ui/dist/index.html"; then echo "ERROR: dist/index.html is missing the runtime fix script — /web/ subpath serving will break" >&2 exit 1 fi mkdir -p "$SITE_ROOT/web" rsync -a --delete "$REPO_DIR/packages/web-ui/dist/" "$SITE_ROOT/web/" log "Step 3/7: create dgb-wallet user" if ! id -u dgb-wallet >/dev/null 2>&1; then useradd --system --home "$DAEMON_HOME" --shell /usr/sbin/nologin dgb-wallet fi mkdir -p "$DAEMON_HOME" chown -R dgb-wallet:dgb-wallet "$DAEMON_HOME" "$REPO_DIR" log "Step 4/7: install dgb-daemon.service" install -m 0644 "$OPS_DIR/dgb-daemon.service" /etc/systemd/system/dgb-daemon.service systemctl daemon-reload systemctl enable --now dgb-daemon.service sleep 2 systemctl --no-pager --lines=5 status dgb-daemon.service || true log "Step 5/7: render Caddy snippet → $SNIPPET_FILE" mkdir -p "$SNIPPET_DIR" sed -e "s|@SITE_ROOT@|$SITE_ROOT|g" \ "$OPS_DIR/Caddyfile.wallet-web-snippet" > "$SNIPPET_FILE" log "Step 6/7: wire snippet into the host Caddyfile" IMPORT_LINE=" import $SNIPPET_FILE" START_MARK="# --- START DGB WALLET IMPORT ---" END_MARK="# --- END DGB WALLET IMPORT ---" if [[ -n "$MERGE_TARGET" ]]; then if [[ ! -f "$MERGE_TARGET" ]]; then echo "ERROR: --merge target does not exist: $MERGE_TARGET" >&2 exit 1 fi # Idempotent: if the sentinel block already exists, replace between # markers; otherwise insert before the LAST `}` in the file (assumed # to close the site block). if grep -qF "$START_MARK" "$MERGE_TARGET"; then awk -v s="$START_MARK" -v e="$END_MARK" -v line="$IMPORT_LINE" ' $0 ~ s { print; print line; skip=1; next } $0 ~ e { skip=0 } !skip { print } ' "$MERGE_TARGET" > "$MERGE_TARGET.tmp" mv "$MERGE_TARGET.tmp" "$MERGE_TARGET" echo " updated existing import block in $MERGE_TARGET" else # Insert marker block before the last `}` on its own line. LAST_BRACE_LINE=$(grep -n '^\s*}\s*$' "$MERGE_TARGET" | tail -n1 | cut -d: -f1) if [[ -z "$LAST_BRACE_LINE" ]]; then echo "ERROR: could not locate a site-block close brace in $MERGE_TARGET." >&2 echo " Add these three lines inside your :443 block by hand:" >&2 echo " $START_MARK" >&2 echo " $IMPORT_LINE" >&2 echo " $END_MARK" >&2 exit 1 fi awk -v n="$LAST_BRACE_LINE" -v s="$START_MARK" -v line="$IMPORT_LINE" -v e="$END_MARK" ' NR == n { print " " s; print line; print " " e } { print } ' "$MERGE_TARGET" > "$MERGE_TARGET.tmp" mv "$MERGE_TARGET.tmp" "$MERGE_TARGET" echo " inserted import block into $MERGE_TARGET before line $LAST_BRACE_LINE" fi caddy validate --config "$MERGE_TARGET" systemctl reload caddy || systemctl restart caddy else cat < to have it done automatically. EOF fi log "Step 7/7: DONE." VPS_IP="$(curl -s -4 https://ifconfig.me || echo '')" cat </web/ static bundle https:///p2p/health daemon health (reverse-proxied) https:///p2p/balance BIP158 scan https:///p2p/broadcast Dandelion++ broadcast To re-deploy after a code change, from the DEV BOX: ops/push.sh # web bundle + daemon sources, restart daemon ops/push.sh web # bundle only ops/push.sh daemon # daemon only (builds locally, ships over ssh, swaps atomically, verifies the served hash) Tail logs: journalctl -u dgb-daemon -u caddy -f EOF