fix: Tor management system, bug fixes, federation name sync

Major changes:
- Full Tor hidden service management via systemd path unit pattern
  (tor-helper.sh + archipelago-tor-helper.path/service) — respects
  NoNewPrivileges=yes, no sudo needed from backend
- Container doctor: prefer system Tor over container, remove archy-tor
- Deploy script: fix torrc generation (read correct services.json path),
  web apps map port 80→local port, enable both tor and tor@default
- Federation: server rename pushes name to peers via background sync
- Server name: fix root-owned file, optimistic store update
- Mesh: local echo for sent messages, sendingArch loading state
- Web5: Message button → Mesh redirect, node name lookup in messages
- PeerFiles: show DID not onion in header
- Connected Nodes: flex-1 instead of fixed max-h
- Toast notifications route to Mesh
- Deploy script: fix single-quote syntax in SSH block

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-20 02:59:29 +00:00
parent f7872e2914
commit cffcc9f665
15 changed files with 904 additions and 250 deletions

View File

@@ -685,8 +685,9 @@ PYEOF
# Fix secrets directory ownership (must be readable by archipelago user, not root)
sudo chown -R archipelago:archipelago /var/lib/archipelago/secrets 2>/dev/null || true
sudo chmod 700 /var/lib/archipelago/secrets 2>/dev/null || true
# Fix any root-owned config files in data dir (dead man's switch, sessions, etc.)
sudo find /var/lib/archipelago -maxdepth 1 -name '*.json' -user root -exec chown archipelago:archipelago {} \; 2>/dev/null || true
# Fix any root-owned files in data dir - dead mans switch, sessions, server-name
sudo find /var/lib/archipelago -maxdepth 1 -name "*.json" -user root -exec chown archipelago:archipelago {} \; 2>/dev/null || true
sudo chown archipelago:archipelago /var/lib/archipelago/server-name 2>/dev/null || true
echo " Data directories OK"
# Rootless podman UID mapping: fix data dir ownership so container processes
@@ -716,6 +717,26 @@ PYEOF
scp $SSH_OPTS "$PROJECT_DIR/neode-ui/public/nostr-provider.js" "$TARGET_HOST:/tmp/nostr-provider.js" 2>/dev/null && \
ssh $SSH_OPTS "$TARGET_HOST" 'sudo cp /tmp/nostr-provider.js /opt/archipelago/web-ui/nostr-provider.js && echo " nostr-provider.js deployed"' 2>/dev/null || echo " (nostr-provider.js not found, skipping)"
# Deploy tor-helper: script + systemd path unit for privileged Tor management
progress "Deploying tor-helper"
scp $SSH_OPTS \
"$PROJECT_DIR/scripts/tor-helper.sh" \
"$PROJECT_DIR/image-recipe/configs/archipelago-tor-helper.path" \
"$PROJECT_DIR/image-recipe/configs/archipelago-tor-helper.service" \
"$TARGET_HOST:/tmp/" 2>/dev/null && \
ssh $SSH_OPTS "$TARGET_HOST" '
sudo mkdir -p /opt/archipelago/scripts
sudo cp /tmp/tor-helper.sh /opt/archipelago/scripts/tor-helper.sh
sudo chmod 755 /opt/archipelago/scripts/tor-helper.sh
sudo chown root:root /opt/archipelago/scripts/tor-helper.sh
sudo cp /tmp/archipelago-tor-helper.path /etc/systemd/system/
sudo cp /tmp/archipelago-tor-helper.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable archipelago-tor-helper.path
sudo systemctl start archipelago-tor-helper.path
echo " tor-helper deployed with systemd path unit"
' 2>/dev/null || echo " (tor-helper deploy skipped)"
# Sync nginx config (second pass — includes HTTPS snippets)
scp $SSH_OPTS "$PROJECT_DIR/image-recipe/configs/nginx-archipelago.conf" "$TARGET_HOST:/tmp/nginx-archipelago.conf" 2>/dev/null && \
ssh $SSH_OPTS "$TARGET_HOST" '
@@ -1201,32 +1222,54 @@ print("services.json created")
'
fi
# Generate torrc — use /var/lib/tor/ for hidden services (AppArmor-safe)
# Generate torrc from services.json — use /var/lib/tor/ for hidden services
sudo python3 -c '
import json
lines = ["SocksPort 9050", "ControlPort 0", ""]
try:
with open("/var/lib/archipelago/tor/services.json") as f:
cfg = json.load(f)
extra_ports = {"lnd": [8080]} # LND REST API over Tor
import json, os
# Protocol services get direct port mapping; web apps map port 80 to their local port
PROTOCOL_SERVICES = {"bitcoin", "bitcoin-knots", "electrs", "electrumx", "lnd"}
lines = ["# Auto-generated by Archipelago deploy", "SocksPort 9050", "# ControlPort disabled", ""]
# Try reading services config (check both paths for compatibility)
cfg = None
for path in ["/var/lib/archipelago/tor-config/services.json", "/var/lib/archipelago/tor/services.json"]:
try:
with open(path) as f:
cfg = json.load(f)
break
except Exception:
pass
if cfg:
for svc in cfg.get("services", []):
if svc.get("enabled", True):
n = svc["name"]
p = svc["local_port"]
lines.append("HiddenServiceDir /var/lib/tor/hidden_service_%s" % n)
lines.append("HiddenServicePort %d 127.0.0.1:%d" % (p, p))
for ep in extra_ports.get(n, []):
lines.append("HiddenServicePort %d 127.0.0.1:%d" % (ep, ep))
lines.append("")
except Exception:
for n, ports in [("archipelago",[80]),("bitcoin",[8333]),("electrumx",[50001]),("lnd",[9735,8080]),("btcpay",[23000]),("mempool",[4080]),("fedimint",[8175])]:
if not svc.get("enabled", True):
continue
n = svc["name"]
p = svc["local_port"]
lines.append("HiddenServiceDir /var/lib/tor/hidden_service_%s" % n)
for p in ports:
if n in PROTOCOL_SERVICES:
# Protocol: direct port mapping
lines.append("HiddenServicePort %d 127.0.0.1:%d" % (p, p))
if n == "lnd":
lines.append("HiddenServicePort 9735 127.0.0.1:9735")
lines.append("HiddenServicePort 10009 127.0.0.1:10009")
else:
# Web app: map port 80 on .onion to local app port (access via app.onion without port)
lines.append("HiddenServicePort 80 127.0.0.1:%d" % p)
lines.append("")
else:
# Fallback: default services
for n, mappings in [("archipelago",[(80,80)]),("bitcoin",[(8333,8333)]),("electrs",[(50001,50001)]),("lnd",[(8080,8080),(9735,9735),(10009,10009)]),("btcpay",[(80,23000)]),("mempool",[(80,4080)]),("fedimint",[(80,8175)])]:
lines.append("HiddenServiceDir /var/lib/tor/hidden_service_%s" % n)
for remote_p, local_p in mappings:
lines.append("HiddenServicePort %d 127.0.0.1:%d" % (remote_p, local_p))
lines.append("")
with open("/etc/tor/torrc", "w") as f:
f.write("\n".join(lines) + "\n")
print("torrc generated with %d services" % (len(lines) // 3))
enabled = sum(1 for s in (cfg or {}).get("services", []) if s.get("enabled", True))
print("torrc generated with %d services" % (enabled or 7))
'
# Remove any old Tor container (system Tor is preferred)
@@ -1238,6 +1281,8 @@ print("torrc generated with %d services" % (len(lines) // 3))
# Use system Tor (preferred — no AppArmor issues with default paths)
if command -v tor >/dev/null 2>&1; then
sudo systemctl enable tor 2>/dev/null
sudo systemctl enable tor@default 2>/dev/null
sudo systemctl restart tor 2>/dev/null
sudo systemctl restart tor@default 2>/dev/null
echo ' Using system Tor daemon'
else
@@ -1245,6 +1290,8 @@ print("torrc generated with %d services" % (len(lines) // 3))
sudo apt-get update -qq && sudo apt-get install -y -qq tor 2>/dev/null || true
if command -v tor >/dev/null 2>&1; then
sudo systemctl enable tor 2>/dev/null
sudo systemctl enable tor@default 2>/dev/null
sudo systemctl restart tor 2>/dev/null
sudo systemctl restart tor@default 2>/dev/null
echo ' System Tor installed and started'
else