fix: RC hardware-test feedback — mesh serial access, federation.list, masked install errors

- image: create archipelago user with dialout so the backend can open
  /dev/ttyUSB*/ttyACM* LoRa radios; doctor Fix 14 heals existing nodes
  (verified live on the .65 RC install — device detected after the fix)
- Web5Federation.vue called nonexistent federation.list (Unknown method);
  now uses federation.list-nodes + federation.list-pending-requests with
  a last-seen-based online count
- sanitizer: let app-install dependency errors through — 'LND requires a
  running Bitcoin node' was masked as 'Check server logs', so install
  failures on fresh nodes looked random

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Dorian 2026-07-13 18:08:25 +01:00
parent b800288550
commit ca89cb4196
4 changed files with 36 additions and 6 deletions

View File

@ -64,6 +64,13 @@ pub(super) fn sanitize_error_message(msg: &str) -> String {
"Failed to open channel", "Failed to open channel",
"Failed to close channel", "Failed to close channel",
"Failed to connect to peer", "Failed to connect to peer",
// App-install dependency errors (package/dependencies.rs) — masking
// these left users retrying installs blind ("LND install failed" on a
// fresh node was really "Bitcoin Knots isn't running yet")
"LND requires",
"ElectrumX requires",
"BTCPay Server requires",
"Mempool requires",
"Container", "Container",
"Image", "Image",
"Bitcoin address", "Bitcoin address",

View File

@ -398,7 +398,7 @@ RUN apt-get update && apt-get -y full-upgrade && apt-get install -y --no-install
RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen RUN echo "en_US.UTF-8 UTF-8" > /etc/locale.gen && locale-gen
# Create archipelago user with password "archipelago" # Create archipelago user with password "archipelago"
RUN useradd -m -s /bin/bash -G sudo archipelago && \ RUN useradd -m -s /bin/bash -G sudo,dialout archipelago && \
echo "archipelago:archipelago" | chpasswd && \ echo "archipelago:archipelago" | chpasswd && \
echo "root:archipelago" | chpasswd && \ echo "root:archipelago" | chpasswd && \
echo "archipelago ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/archipelago echo "archipelago ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/archipelago

View File

@ -101,13 +101,18 @@ async function loadFederationSummary() {
federationError.value = '' federationError.value = ''
try { try {
const res = await rpcClient.call<{ const res = await rpcClient.call<{
nodes: Array<{ status: string }> nodes: Array<{ last_seen?: string }>
pending_requests: Array<unknown> }>({ method: 'federation.list-nodes', timeout: 5000 })
}>({ method: 'federation.list', timeout: 5000 })
const nodes = res.nodes || [] const nodes = res.nodes || []
nodeCount.value = nodes.length nodeCount.value = nodes.length
onlineCount.value = nodes.filter(n => n.status === 'online' || n.status === 'connected').length // list-nodes has no live status field count a node seen in the
pendingCount.value = (res.pending_requests || []).length // last 10 minutes as online
const cutoff = Date.now() - 10 * 60 * 1000
onlineCount.value = nodes.filter(n => n.last_seen && new Date(n.last_seen).getTime() > cutoff).length
try {
const pend = await rpcClient.call<{ requests: Array<unknown> }>({ method: 'federation.list-pending-requests', timeout: 5000 })
pendingCount.value = (pend.requests || []).length
} catch { pendingCount.value = 0 }
hasLoadedFederation.value = true hasLoadedFederation.value = true
} catch (e: unknown) { } catch (e: unknown) {
federationError.value = e instanceof Error ? e.message : 'Federation unavailable' federationError.value = e instanceof Error ? e.message : 'Federation unavailable'

View File

@ -614,6 +614,23 @@ fix_missing_catatonit() {
return 1 return 1
} }
# ── Fix 14: archipelago user missing dialout (mesh radios) ───
# The image used to create the archipelago user with only `sudo`, so the
# backend couldn't open /dev/ttyUSB*/ttyACM* serial LoRa radios — Mesh
# never detected a plugged-in device (observed on the 1.7.99 RC install
# 2026-07-13). Group change takes effect on the next service restart; we
# restart archipelago only if a serial device is actually present.
fix_archipelago_dialout() {
id -nG archipelago 2>/dev/null | grep -qw dialout && return 1
usermod -aG dialout archipelago 2>/dev/null || return 1
log "Added archipelago to dialout (serial mesh radios were unreadable)"
if ls /dev/ttyUSB* /dev/ttyACM* >/dev/null 2>&1; then
systemctl try-restart archipelago 2>/dev/null || true
log "Restarted archipelago to pick up dialout (radio present)"
fi
return 0
}
# ── Main ───────────────────────────────────────────────────── # ── Main ─────────────────────────────────────────────────────
# If remote host provided, run via SSH # If remote host provided, run via SSH
@ -646,6 +663,7 @@ run_fix "rootless-ports" fix_missing_rootless_ports
run_fix "npm-public-hosts" fix_npm_public_hosts run_fix "npm-public-hosts" fix_npm_public_hosts
run_fix "btcpay-route-hints" fix_btcpay_route_hints run_fix "btcpay-route-hints" fix_btcpay_route_hints
run_fix "catatonit" fix_missing_catatonit run_fix "catatonit" fix_missing_catatonit
run_fix "dialout" fix_archipelago_dialout
echo "" echo ""
if [ $FIXES_APPLIED -gt 0 ]; then if [ $FIXES_APPLIED -gt 0 ]; then