fix(lnd-ui,bitcoin-ui): OTA-breaking lnd-ui spec, 404 channels link, iframe copy, node URI

All four found by verifying on archi-dev-box rather than assuming.

container-specs.sh: archy-lnd-ui was specified as a BRIDGE container with
SPEC_PORTS="18083:80", but docker/lnd-ui/nginx.conf listens on 18083
directly (it must, to proxy the backend on 127.0.0.1:5678 same-origin).
Recreating from that spec publishes host 18083 to container port 80, where
nothing listens. Reproduced on the node: the app came back with :18083
refusing connections, HTTP 000. This never fired before because the running
containers are created by first-boot-containers.sh, which is host-networked
and never reads this file; the spec is only consulted when self-update.sh
rebuilds a UI image, and that only happens when a file under docker/lnd-ui/
changes — which is exactly what the previous two commits did. So the next
OTA would have taken lnd-ui down on every node. Now SPEC_NETWORK="host"
with no port mapping, matching what actually runs. NET_BIND_SERVICE dropped
with it: 18083 is unprivileged.

lnd-ui channels link: pointed at /apps/lnd/channels, but that route is a
CHILD of the /dashboard record in neode-ui's router, so the real path is
/dashboard/apps/lnd/channels. nginx's SPA fallback returns 200 for the
wrong path, so it failed as vue-router's NotFound view rather than an HTTP
404 — both the Payment Channels card and the Manage Channels button.

Both apps, copy buttons: navigator.clipboard only exists in a secure
context, and nodes serve these apps over plain http; the main UI also
embeds them in an iframe, where the async Clipboard API is separately gated
by the clipboard-write permission policy. Every copy button silently did
nothing there. Added an execCommand('copy') fallback behind a copyText()
helper and routed all six call sites through it.

lnd-ui Node ID: showed the bare pubkey whenever getinfo.uris was empty,
which is the common case — LND only populates uris once it is advertising
an external address. The bare pubkey is not what a peer pastes to open a
channel. The full pubkey@host:9735 URI is now built from the Tor onion
where available, falling back to this node's address, with a hint saying
which and what its reachability is. The QR encodes the URI too.

Verified on archi-dev-box: both images rebuilt and containers recreated
from the specs; lnd-ui and bitcoin-ui both serve 200 with the new assets;
and the RPCs the new tabs depend on all answer on the live node —
getblockstats returns every field the charts read, getpeerinfo returns 11
peers carrying relaytxes and network values the classifier handles.

Note for whoever tests bitcoin-ui's Insights/Peers tabs: /bitcoin-rpc/ now
sits behind auth_request /_session_check (a05956c4 et al), so it answers
401 to an unauthenticated curl by design. A logged-in browser sends the
session cookie same-origin, which is how those tabs get their data.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-02 19:33:21 -04:00
co-authored by Claude Opus 5
parent 5c9d5dc424
commit aaa89789d2
3 changed files with 115 additions and 22 deletions
+12 -2
View File
@@ -578,11 +578,21 @@ load_spec_archy-lnd-ui() {
reset_spec
SPEC_NAME="archy-lnd-ui"
SPEC_IMAGE="localhost/lnd-ui:local"
SPEC_PORTS="18083:80"
# Host-networked, NOT bridge with 18083:80. docker/lnd-ui/nginx.conf listens
# on 18083 directly (it must, so it can proxy to the backend on
# 127.0.0.1:5678 without a cross-origin hop). This spec used to say
# SPEC_PORTS="18083:80", which published host 18083 to container port 80 —
# where nothing listens. Nobody noticed because the running containers were
# created by first-boot-containers.sh, which is host-networked and never
# consults this file; the spec is only read when self-update.sh rebuilds a
# UI image, and that only fires when a file under docker/lnd-ui/ changes.
# Verified on archi-dev-box: recreating from the old spec left :18083
# refusing connections.
SPEC_NETWORK="host"
SPEC_MEMORY="$(mem_limit archy-lnd-ui)"
SPEC_TIER="4"
SPEC_LOCAL_IMAGE="true"
SPEC_CAPS="CHOWN SETUID SETGID NET_BIND_SERVICE"
SPEC_CAPS="CHOWN SETUID SETGID"
SPEC_SECURITY="no-new-privileges:true"
}