feat: deploy-to-target supports .253 + mesh/federation/VPN updates

- Add deploy_secondary() function for deploying to multiple LAN nodes
- --both now deploys to .198 and .253 (previously .198 only)
- Fleet deploy updated for 3 LAN nodes
- Mesh DM fixes: protocol frame format, DM-via-channel routing
- Federation pending requests, discover modal
- VPN status UI improvements
- Image versions and container specs updates

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-04-18 11:07:08 -04:00
co-authored by Claude Opus 4.6
parent e210376e05
commit 9dd802998c
38 changed files with 3773 additions and 697 deletions
+26 -112
View File
@@ -109,13 +109,13 @@ if [ -f "$UNBUNDLED_MARKER" ]; then
log "Creating FileBrowser (noauth)..."
mkdir -p /var/lib/archipelago/filebrowser /var/lib/archipelago/filebrowser-data
mkdir -p /var/lib/archipelago/filebrowser/{Documents,Photos,Music,Videos,Downloads}
chown -R 1000:1000 /var/lib/archipelago/filebrowser
chown -R 1000:1000 /var/lib/archipelago/filebrowser-data
chown -R 100000:100000 /var/lib/archipelago/filebrowser
chown -R 100000:100000 /var/lib/archipelago/filebrowser-data
# Write config with database on persistent volume
cat > /var/lib/archipelago/filebrowser-data/.filebrowser.json <<'FBEOF'
{"port":80,"baseURL":"","address":"0.0.0.0","database":"/data/filebrowser.db","root":"/srv","log":"stdout"}
FBEOF
chown 1000:1000 /var/lib/archipelago/filebrowser-data/.filebrowser.json
chown 100000:100000 /var/lib/archipelago/filebrowser-data/.filebrowser.json
pull_with_fallback "${FILEBROWSER_IMAGE}"
$DOCKER run -d --name filebrowser --restart unless-stopped \
--network archy-net \
@@ -141,25 +141,25 @@ FBEOF
chown -R 1000:1000 /var/lib/archipelago/secrets
fi
# Generate WireGuard keys for VPN
if [ ! -f /var/lib/archipelago/wireguard/wg0.conf ]; then
# Generate WireGuard keys for standalone VPN (archipelago-wg service)
WG_DIR="/var/lib/archipelago/wireguard"
if [ ! -f "$WG_DIR/private.key" ]; then
log "Generating WireGuard keys..."
mkdir -p /var/lib/archipelago/wireguard /etc/wireguard
PRIVKEY=$(wg genkey)
PUBKEY=$(echo "$PRIVKEY" | wg pubkey)
cat > /var/lib/archipelago/wireguard/wg0.conf <<WGEOF
[Interface]
PrivateKey = $PRIVKEY
Address = 10.0.0.1/24
ListenPort = 51820
WGEOF
cp /var/lib/archipelago/wireguard/wg0.conf /etc/wireguard/wg0.conf
chmod 600 /etc/wireguard/wg0.conf /var/lib/archipelago/wireguard/wg0.conf
chown -R 1000:1000 /var/lib/archipelago/wireguard
systemctl enable wg-quick@wg0 2>/dev/null || true
wg-quick up wg0 2>>"$LOG" || true
log " WireGuard configured: pubkey=$PUBKEY"
mkdir -p "$WG_DIR"
wg genkey > "$WG_DIR/private.key" 2>/dev/null
chmod 600 "$WG_DIR/private.key"
wg pubkey < "$WG_DIR/private.key" > "$WG_DIR/public.key"
chown -R 1000:1000 "$WG_DIR"
log " WireGuard keypair generated: pubkey=$(cat "$WG_DIR/public.key")"
fi
# Start standalone WireGuard service (wg0:51820 on 10.44.0.1/16)
modprobe wireguard 2>/dev/null || true
systemctl enable --now archipelago-wg 2>/dev/null || true
systemctl enable --now archipelago-wg-address 2>/dev/null || true
if command -v ufw >/dev/null 2>&1 && ufw status | grep -q "Status: active"; then
ufw allow 51820/udp >/dev/null 2>&1 || true
fi
log " Standalone WireGuard started (wg0:51820)"
log "Unbundled first-boot complete"
exit 0
@@ -242,98 +242,10 @@ else
log "nostr-rs-relay binary not found — skipping relay setup"
fi
# ── NostrVPN: configure native system service with node identity ──────
# The nvpn binary may have GLIBC mismatch (built for newer glibc than target OS).
# Write config.toml directly as fallback — the Rust backend reads it for vpn.invite/status.
NOSTR_SECRET=$(cat /var/lib/archipelago/identity/nostr_secret 2>/dev/null)
NOSTR_PUBKEY=$(cat /var/lib/archipelago/identity/nostr_pubkey 2>/dev/null)
if [ -n "$NOSTR_SECRET" ]; then
NVPN_CONFIG_DIR="/home/archipelago/.config/nvpn"
DAEMON_CONFIG_DIR="/var/lib/archipelago/nostr-vpn/.config/nvpn"
mkdir -p "$NVPN_CONFIG_DIR" "$DAEMON_CONFIG_DIR"
# Try nvpn CLI first (may fail with GLIBC mismatch)
NVPN_CLI_OK=false
if command -v nvpn >/dev/null 2>&1; then
if [ ! -f "$NVPN_CONFIG_DIR/config.toml" ]; then
if su -l archipelago -c "nvpn init" 2>/dev/null; then
NVPN_CLI_OK=true
su -l archipelago -c "nvpn set --config '$NVPN_CONFIG_DIR/config.toml'" 2>/dev/null || true
else
log "NostrVPN: nvpn init failed (likely GLIBC mismatch) — using direct config"
fi
else
NVPN_CLI_OK=true
fi
fi
# Get server's public IP for WireGuard endpoint
HOST_IP=$(cat /var/lib/archipelago/host-ip.env 2>/dev/null | grep ARCHIPELAGO_HOST_IP | cut -d= -f2)
[ -z "$HOST_IP" ] && HOST_IP=$(curl -s --connect-timeout 5 https://api.ipify.org 2>/dev/null || hostname -I | awk '{print $1}')
if $NVPN_CLI_OK && [ -f "$NVPN_CONFIG_DIR/config.toml" ]; then
# nvpn CLI works — use it to configure
su -l archipelago -c "nvpn set --endpoint '${HOST_IP}:51821'" 2>/dev/null || true
# Direct relay (public IP) — only if not behind NAT
if [ -n "$HOST_IP" ] && ! echo "$HOST_IP" | grep -qE '^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)'; then
su -l archipelago -c "nvpn relay add 'ws://${HOST_IP}:7777'" 2>/dev/null || true
fi
RELAY_ONION=$(cat /var/lib/archipelago/tor-hostnames/relay 2>/dev/null)
if [ -n "$RELAY_ONION" ]; then
su -l archipelago -c "nvpn relay add 'ws://${RELAY_ONION}:7777'" 2>/dev/null || true
fi
fi
# Fallback: write config.toml directly if it doesn't exist yet.
# Uses hex keys — the Rust backend converts hex to npub1/nsec1 at read time.
if [ ! -f "$DAEMON_CONFIG_DIR/config.toml" ] && [ ! -f "$NVPN_CONFIG_DIR/config.toml" ]; then
# Build relay list
RELAYS=""
RELAY_ONION=$(cat /var/lib/archipelago/tor-hostnames/relay 2>/dev/null)
if [ -n "$RELAY_ONION" ]; then
RELAYS="\"ws://${RELAY_ONION}:7777\""
fi
if [ -n "$HOST_IP" ] && ! echo "$HOST_IP" | grep -qE '^(10\.|192\.168\.|172\.(1[6-9]|2[0-9]|3[01])\.)'; then
[ -n "$RELAYS" ] && RELAYS="$RELAYS, "
RELAYS="${RELAYS}\"ws://${HOST_IP}:7777\""
fi
[ -z "$RELAYS" ] && RELAYS='"wss://relay.damus.io", "wss://relay.primal.net"'
cat > "$DAEMON_CONFIG_DIR/config.toml" <<NVPNCONF
[nostr]
public_key = "${NOSTR_PUBKEY}"
secret_key = "${NOSTR_SECRET}"
relays = [${RELAYS}]
[[networks]]
network_id = "archipelago"
participants = []
NVPNCONF
chmod 600 "$DAEMON_CONFIG_DIR/config.toml"
log "NostrVPN: wrote config.toml directly (hex keys, backend converts)"
fi
# Sync user config to daemon dir if nvpn CLI created it
if [ -f "$NVPN_CONFIG_DIR/config.toml" ] && [ ! -f "$DAEMON_CONFIG_DIR/config.toml" ]; then
cp "$NVPN_CONFIG_DIR/config.toml" "$DAEMON_CONFIG_DIR/config.toml"
fi
chown -R archipelago:archipelago /var/lib/archipelago/nostr-vpn
# Ensure env file exists for the service
mkdir -p /var/lib/archipelago/nostr-vpn
cat > /var/lib/archipelago/nostr-vpn/env <<NVPNENV
NOSTR_SECRET=${NOSTR_SECRET}
NOSTR_PUBKEY=${NOSTR_PUBKEY}
NVPNENV
chmod 600 /var/lib/archipelago/nostr-vpn/env
# Start NostrVPN mesh service (standalone WG already started above)
systemctl reset-failed nostr-vpn 2>/dev/null || true
systemctl enable --now nostr-vpn 2>/dev/null || true
log "NostrVPN configured with node identity and started"
else
log "NostrVPN: no Nostr identity yet — will configure after onboarding"
fi
# ── NostrVPN: DISABLED — using standalone WireGuard only ──────────────
# NostrVPN (nvpn) is disabled for now. Standalone WireGuard (archipelago-wg)
# handles VPN with QR-based peer provisioning via the web UI.
log "NostrVPN disabled — standalone WireGuard only (wg0:51820)"
# Wait for a container to be healthy (accepting connections)
wait_for_container() {
@@ -497,6 +409,8 @@ grep -q "^archipelago:" /etc/subuid 2>/dev/null || {
echo "archipelago:100000:65536" >> /etc/subgid
log " subuid/subgid configured"
}
# Apply podman migrations after subuid/subgid changes (per official tutorial)
$DOCKER system migrate 2>/dev/null || true
# Ensure /etc/hosts is readable (rootless podman needs it)
chmod 644 /etc/hosts 2>/dev/null