fix(security): close the five host-networked app UIs the audit could not see
Scanning archi-dev-box from OUTSIDE found five ports serving their screens with no login — lnd-ui 18083, bitcoin-ui 8334, fips-ui 8336, electrs-ui 50002 and the Fedimint Guardian 8175 — none of which appeared in the gate's unprotected list. They are host-networked, so Podman publishes nothing to pin and their manifests declared 'ports: []'; the gate builds its map from declared ports, so it neither protected them nor reported them. An audit that reports success while five screens are open is worse than no audit. Their nginx now listens on 127.0.0.1 instead of 0.0.0.0, and each port is declared 'auth: gated' so the daemon owns the outside. 'bind:' on a host-networked app is a statement of where the container listens, not a publish instruction — quadlet already skips PublishPort in host mode. Guardian 8175 is declared on the fedimint app because its companion has no manifest, and the gate keys on port, not container. Credential paths were NOT exposed and are verified so: /lnd-connect-info, the /proxy/lnd/ passthrough, container logs and every RPC method through these screens all return 401 unauthenticated. What leaked was the page shell. Also fixes the delivery gap that would have made this unshippable: only bitcoin-ui, lnd-ui and electrs-ui were ever rsynced to /opt/archipelago/docker, so edits to fips-ui and fedimint-ui reached nodes through no path at all. All five now sync; the two whose rebuilds the daemon owns are synced without being handed to container-specs. Every remaining undeclared port is now declared with a stated reason — gated: botfights 9100, router 8084, pine 10380; exempt with rationale: fedimint consensus 8173/8174, gateway 8176/9737, netbird 8086/8087 (TLS + own auth, and enrolled devices cannot hold a session), pine TLS 10381, lightning-stack REST 8091 (macaroon, mirrors lnd). Zero undeclared ports remain across all 56 manifests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
634640944c
commit
abf0f56afc
@@ -31,7 +31,18 @@ app:
|
||||
# proxies to 127.0.0.1:8332 which is where the bitcoin backend binds
|
||||
# its RPC. `ports:` is intentionally empty because host networking
|
||||
# bypasses port mapping.
|
||||
ports: []
|
||||
# Declared so the APP GATE can see this port. Host networking means Podman
|
||||
# publishes nothing (quadlet skips PublishPort in host mode), so `bind:` here
|
||||
# is a statement of where the container's own nginx listens — 127.0.0.1 —
|
||||
# not a publish instruction. Without this declaration the gate had no idea
|
||||
# the port existed: it was neither protected nor listed as unprotected, and
|
||||
# served the Bitcoin screen unauthenticated on every interface.
|
||||
ports:
|
||||
- host: 8334
|
||||
container: 8334
|
||||
protocol: tcp
|
||||
bind: 127.0.0.1
|
||||
auth: gated
|
||||
|
||||
volumes:
|
||||
# Bind-mount the rendered nginx.conf read-only. The prod orchestrator
|
||||
|
||||
@@ -62,6 +62,8 @@ app:
|
||||
- host: 9100
|
||||
container: 9100
|
||||
protocol: tcp # Web UI + API
|
||||
bind: 127.0.0.1
|
||||
auth: gated
|
||||
|
||||
volumes:
|
||||
# A bare relative source (was "botfights-data", no leading slash) is
|
||||
|
||||
@@ -23,7 +23,18 @@ app:
|
||||
network_policy: host
|
||||
|
||||
# Host networking: nginx listens on 50002 directly on the host IP.
|
||||
ports: []
|
||||
# Declared so the APP GATE can see this port. Host networking means Podman
|
||||
# publishes nothing (quadlet skips PublishPort in host mode), so `bind:` here
|
||||
# is a statement of where the container's own nginx listens — 127.0.0.1 —
|
||||
# not a publish instruction. Without this declaration the gate had no idea
|
||||
# the port existed: it was neither protected nor listed as unprotected, and
|
||||
# served the Electrs screen unauthenticated on every interface.
|
||||
ports:
|
||||
- host: 50002
|
||||
container: 50002
|
||||
protocol: tcp
|
||||
bind: 127.0.0.1
|
||||
auth: gated
|
||||
|
||||
volumes: []
|
||||
|
||||
|
||||
@@ -60,9 +60,17 @@ app:
|
||||
- host: 8176
|
||||
container: 8176
|
||||
protocol: tcp
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
Fedimint gateway API, protected by its own bcrypt password (--bcrypt-password-hash)
|
||||
and reached by federation peers and clients that cannot hold a browser session.
|
||||
- host: 9737
|
||||
container: 9737
|
||||
protocol: tcp
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
LDK Lightning p2p for the gateway. The BOLT-8 noise handshake authenticates and
|
||||
encrypts the connection itself.
|
||||
|
||||
volumes:
|
||||
- type: bind
|
||||
|
||||
@@ -50,11 +50,29 @@ app:
|
||||
- host: 8173
|
||||
container: 8173
|
||||
protocol: tcp
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
Fedimint guardian consensus. Other guardians speak the federation's own
|
||||
authenticated protocol here; a login page would break consensus.
|
||||
- host: 8174
|
||||
container: 8174
|
||||
protocol: tcp
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
Fedimint guardian API for federation clients, which authenticate to the
|
||||
federation itself and cannot hold a browser session.
|
||||
# Public launch port 8175 is owned by archy-fedimint-ui, which serves a
|
||||
# wait page while Bitcoin syncs and proxies here after fedimintd starts.
|
||||
# Declared HERE because that companion has no manifest of its own, and the
|
||||
# gate keys on the port rather than the container: without this entry it
|
||||
# served the Guardian UI unauthenticated on every interface and never
|
||||
# appeared in the audit. Its nginx is pinned to 127.0.0.1
|
||||
# (docker/fedimint-ui/nginx.conf) so the gate can own the outside.
|
||||
- host: 8175
|
||||
container: 8175
|
||||
protocol: tcp
|
||||
bind: 127.0.0.1
|
||||
auth: gated
|
||||
- host: 8177
|
||||
container: 8175
|
||||
protocol: tcp
|
||||
|
||||
@@ -27,7 +27,18 @@ app:
|
||||
# Host networking: nginx listens on 8336 directly on the host IP and
|
||||
# proxies to 127.0.0.1:5678 (the archipelago RPC). `ports:` is
|
||||
# intentionally empty because host networking bypasses port mapping.
|
||||
ports: []
|
||||
# Declared so the APP GATE can see this port. Host networking means Podman
|
||||
# publishes nothing (quadlet skips PublishPort in host mode), so `bind:` here
|
||||
# is a statement of where the container's own nginx listens — 127.0.0.1 —
|
||||
# not a publish instruction. Without this declaration the gate had no idea
|
||||
# the port existed: it was neither protected nor listed as unprotected, and
|
||||
# served the FIPS mesh screen unauthenticated on every interface.
|
||||
ports:
|
||||
- host: 8336
|
||||
container: 8336
|
||||
protocol: tcp
|
||||
bind: 127.0.0.1
|
||||
auth: gated
|
||||
|
||||
volumes: []
|
||||
|
||||
|
||||
@@ -41,9 +41,14 @@ app:
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
LND gRPC, authenticated by macaroon over TLS. Remote wallets depend on reaching this directly.
|
||||
# Mirrors lnd's 18080 exemption — same LND REST API, same macaroon auth.
|
||||
- host: 8091
|
||||
container: 8080
|
||||
protocol: tcp # REST/Web UI
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
LND REST, authenticated by macaroon over TLS. A browser login page would break
|
||||
Zeus and every non-browser wallet client, exactly as for lnd's 18080.
|
||||
|
||||
volumes:
|
||||
- type: bind
|
||||
|
||||
@@ -35,7 +35,18 @@ app:
|
||||
# port to a container port where nothing listens. scripts/container-specs.sh
|
||||
# carried the identical mistake and was fixed alongside this; recreating from
|
||||
# it on archi-dev-box left :18083 refusing connections.
|
||||
ports: []
|
||||
# Declared so the APP GATE can see this port. Host networking means Podman
|
||||
# publishes nothing (quadlet skips PublishPort in host mode), so `bind:` here
|
||||
# is a statement of where the container's own nginx listens — 127.0.0.1 —
|
||||
# not a publish instruction. Without this declaration the gate had no idea
|
||||
# the port existed: it was neither protected nor listed as unprotected, and
|
||||
# served the LND screen unauthenticated on every interface.
|
||||
ports:
|
||||
- host: 18083
|
||||
container: 18083
|
||||
protocol: tcp
|
||||
bind: 127.0.0.1
|
||||
auth: gated
|
||||
|
||||
volumes: []
|
||||
|
||||
|
||||
@@ -48,6 +48,11 @@ app:
|
||||
- host: 8086
|
||||
container: 80
|
||||
protocol: tcp # management API + embedded OIDC issuer (/oauth2)
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
NetBird management API and its OIDC issuer. Enrolled devices authenticate
|
||||
themselves with setup keys and JWTs, and they cannot hold a browser session —
|
||||
a login page here would disconnect every VPN client on the network.
|
||||
- host: 3478
|
||||
container: 3478
|
||||
protocol: udp # STUN — must be UDP; tcp here breaks relay discovery
|
||||
|
||||
@@ -44,6 +44,11 @@ app:
|
||||
- host: 8087
|
||||
container: 443
|
||||
protocol: tcp
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
NetBird dashboard over TLS, with its own login. The gate speaks plain HTTP,
|
||||
so fronting this port would break the secure context the dashboard requires
|
||||
(issue #15) and the certificate clients pin.
|
||||
|
||||
volumes:
|
||||
- type: bind
|
||||
|
||||
@@ -53,9 +53,16 @@ app:
|
||||
- host: 10380
|
||||
container: 80
|
||||
protocol: tcp
|
||||
bind: 127.0.0.1
|
||||
auth: gated
|
||||
- host: 10381
|
||||
container: 443
|
||||
protocol: tcp
|
||||
auth: none
|
||||
auth_rationale: >-
|
||||
Pine's TLS listener. The gate speaks plain HTTP, so fronting this port would
|
||||
break the secure context navigator.bluetooth needs for WiFi provisioning.
|
||||
The plain-HTTP entry point (10380) is gated, and it is what the UI opens.
|
||||
|
||||
volumes:
|
||||
- type: bind
|
||||
|
||||
@@ -30,6 +30,8 @@ app:
|
||||
- host: 8084
|
||||
container: 8080
|
||||
protocol: tcp # Web UI
|
||||
bind: 127.0.0.1
|
||||
auth: gated
|
||||
- host: 5353
|
||||
container: 5353
|
||||
protocol: udp # mDNS/Bonjour
|
||||
|
||||
@@ -293,6 +293,6 @@ mod tests {
|
||||
// Lock in the core shape so a bad template edit doesn't ship.
|
||||
assert!(TEMPLATE.contains("proxy_pass http://127.0.0.1:8332/"));
|
||||
assert!(TEMPLATE.contains("location /bitcoin-rpc/"));
|
||||
assert!(TEMPLATE.contains("listen 8334"));
|
||||
assert!(TEMPLATE.contains("listen 127.0.0.1:8334"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
server {
|
||||
listen 8334;
|
||||
# Loopback ONLY. This container is host-networked, so this nginx binds the
|
||||
# HOST's address directly — `listen 8334;` meant every interface, and the
|
||||
# app gate could never stand in front of it (there is no podman publish to
|
||||
# pin, and the manifest declared no port, so the gate neither protected it
|
||||
# nor reported it — it served this page to anyone who asked, on LAN,
|
||||
# Tailscale and the mesh alike). Binding loopback lets the daemon claim the
|
||||
# external addresses and authenticate them; see appgate::listener.
|
||||
listen 127.0.0.1:8334;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
server {
|
||||
listen 50002;
|
||||
# Loopback ONLY. This container is host-networked, so this nginx binds the
|
||||
# HOST's address directly — `listen 50002;` meant every interface, and the
|
||||
# app gate could never stand in front of it (there is no podman publish to
|
||||
# pin, and the manifest declared no port, so the gate neither protected it
|
||||
# nor reported it — it served this page to anyone who asked, on LAN,
|
||||
# Tailscale and the mesh alike). Binding loopback lets the daemon claim the
|
||||
# external addresses and authenticate them; see appgate::listener.
|
||||
listen 127.0.0.1:50002;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
server {
|
||||
listen 8175;
|
||||
# Loopback ONLY. This container is host-networked, so this nginx binds the
|
||||
# HOST's address directly — `listen 8175;` meant every interface, and the
|
||||
# app gate could never stand in front of it (there is no podman publish to
|
||||
# pin, and the manifest declared no port, so the gate neither protected it
|
||||
# nor reported it — it served this page to anyone who asked, on LAN,
|
||||
# Tailscale and the mesh alike). Binding loopback lets the daemon claim the
|
||||
# external addresses and authenticate them; see appgate::listener.
|
||||
listen 127.0.0.1:8175;
|
||||
server_name _;
|
||||
|
||||
proxy_intercept_errors on;
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
server {
|
||||
listen 8336;
|
||||
# Loopback ONLY. This container is host-networked, so this nginx binds the
|
||||
# HOST's address directly — `listen 8336;` meant every interface, and the
|
||||
# app gate could never stand in front of it (there is no podman publish to
|
||||
# pin, and the manifest declared no port, so the gate neither protected it
|
||||
# nor reported it — it served this page to anyone who asked, on LAN,
|
||||
# Tailscale and the mesh alike). Binding loopback lets the daemon claim the
|
||||
# external addresses and authenticate them; see appgate::listener.
|
||||
listen 127.0.0.1:8336;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
server {
|
||||
# Host-networked: listen on the app's own port directly (NOT 80, which the
|
||||
# host's main nginx already owns). The app is reached at http(s)://<node>:18083.
|
||||
listen 18083;
|
||||
# Loopback ONLY. This container is host-networked, so this nginx binds the
|
||||
# HOST's address directly — `listen 18083;` meant every interface, and the
|
||||
# app gate could never stand in front of it (there is no podman publish to
|
||||
# pin, and the manifest declared no port, so the gate neither protected it
|
||||
# nor reported it — it served this page to anyone who asked, on LAN,
|
||||
# Tailscale and the mesh alike). Binding loopback lets the daemon claim the
|
||||
# external addresses and authenticate them; see appgate::listener.
|
||||
listen 127.0.0.1:18083;
|
||||
server_name _;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
@@ -711,7 +711,15 @@
|
||||
},
|
||||
"id": "bitcoin-ui",
|
||||
"name": "Bitcoin UI",
|
||||
"ports": [],
|
||||
"ports": [
|
||||
{
|
||||
"auth": "gated",
|
||||
"bind": "127.0.0.1",
|
||||
"container": 8334,
|
||||
"host": 8334,
|
||||
"protocol": "tcp"
|
||||
}
|
||||
],
|
||||
"resources": {
|
||||
"memory_limit": "128Mi"
|
||||
},
|
||||
@@ -805,6 +813,8 @@
|
||||
"name": "BotFights",
|
||||
"ports": [
|
||||
{
|
||||
"auth": "gated",
|
||||
"bind": "127.0.0.1",
|
||||
"container": 9100,
|
||||
"host": 9100,
|
||||
"protocol": "tcp"
|
||||
@@ -1159,7 +1169,15 @@
|
||||
},
|
||||
"id": "electrs-ui",
|
||||
"name": "Electrs UI",
|
||||
"ports": [],
|
||||
"ports": [
|
||||
{
|
||||
"auth": "gated",
|
||||
"bind": "127.0.0.1",
|
||||
"container": 50002,
|
||||
"host": 50002,
|
||||
"protocol": "tcp"
|
||||
}
|
||||
],
|
||||
"resources": {
|
||||
"memory_limit": "64Mi"
|
||||
},
|
||||
@@ -1355,15 +1373,26 @@
|
||||
"name": "Fedimint Guardian",
|
||||
"ports": [
|
||||
{
|
||||
"auth": "none",
|
||||
"auth_rationale": "Fedimint guardian consensus. Other guardians speak the federation's own authenticated protocol here; a login page would break consensus.",
|
||||
"container": 8173,
|
||||
"host": 8173,
|
||||
"protocol": "tcp"
|
||||
},
|
||||
{
|
||||
"auth": "none",
|
||||
"auth_rationale": "Fedimint guardian API for federation clients, which authenticate to the federation itself and cannot hold a browser session.",
|
||||
"container": 8174,
|
||||
"host": 8174,
|
||||
"protocol": "tcp"
|
||||
},
|
||||
{
|
||||
"auth": "gated",
|
||||
"bind": "127.0.0.1",
|
||||
"container": 8175,
|
||||
"host": 8175,
|
||||
"protocol": "tcp"
|
||||
},
|
||||
{
|
||||
"auth": "local",
|
||||
"bind": "127.0.0.1",
|
||||
@@ -1548,11 +1577,15 @@
|
||||
"name": "Fedimint Gateway",
|
||||
"ports": [
|
||||
{
|
||||
"auth": "none",
|
||||
"auth_rationale": "Fedimint gateway API, protected by its own bcrypt password (--bcrypt-password-hash) and reached by federation peers and clients that cannot hold a browser session.",
|
||||
"container": 8176,
|
||||
"host": 8176,
|
||||
"protocol": "tcp"
|
||||
},
|
||||
{
|
||||
"auth": "none",
|
||||
"auth_rationale": "LDK Lightning p2p for the gateway. The BOLT-8 noise handshake authenticates and encrypts the connection itself.",
|
||||
"container": 9737,
|
||||
"host": 9737,
|
||||
"protocol": "tcp"
|
||||
@@ -1700,7 +1733,15 @@
|
||||
},
|
||||
"id": "fips-ui",
|
||||
"name": "FIPS Mesh",
|
||||
"ports": [],
|
||||
"ports": [
|
||||
{
|
||||
"auth": "gated",
|
||||
"bind": "127.0.0.1",
|
||||
"container": 8336,
|
||||
"host": 8336,
|
||||
"protocol": "tcp"
|
||||
}
|
||||
],
|
||||
"resources": {
|
||||
"memory_limit": "128Mi"
|
||||
},
|
||||
@@ -2909,6 +2950,8 @@
|
||||
"protocol": "tcp"
|
||||
},
|
||||
{
|
||||
"auth": "none",
|
||||
"auth_rationale": "LND REST, authenticated by macaroon over TLS. A browser login page would break Zeus and every non-browser wallet client, exactly as for lnd's 18080.",
|
||||
"container": 8080,
|
||||
"host": 8091,
|
||||
"protocol": "tcp"
|
||||
@@ -3078,7 +3121,15 @@
|
||||
},
|
||||
"id": "lnd-ui",
|
||||
"name": "LND UI",
|
||||
"ports": [],
|
||||
"ports": [
|
||||
{
|
||||
"auth": "gated",
|
||||
"bind": "127.0.0.1",
|
||||
"container": 18083,
|
||||
"host": 18083,
|
||||
"protocol": "tcp"
|
||||
}
|
||||
],
|
||||
"resources": {
|
||||
"memory_limit": "64Mi"
|
||||
},
|
||||
@@ -3413,6 +3464,8 @@
|
||||
"name": "NetBird",
|
||||
"ports": [
|
||||
{
|
||||
"auth": "none",
|
||||
"auth_rationale": "NetBird dashboard over TLS, with its own login. The gate speaks plain HTTP, so fronting this port would break the secure context the dashboard requires (issue #15) and the certificate clients pin.",
|
||||
"container": 443,
|
||||
"host": 8087,
|
||||
"protocol": "tcp"
|
||||
@@ -3617,6 +3670,8 @@
|
||||
"name": "NetBird Server",
|
||||
"ports": [
|
||||
{
|
||||
"auth": "none",
|
||||
"auth_rationale": "NetBird management API and its OIDC issuer. Enrolled devices authenticate themselves with setup keys and JWTs, and they cannot hold a browser session — a login page here would disconnect every VPN client on the network.",
|
||||
"container": 80,
|
||||
"host": 8086,
|
||||
"protocol": "tcp"
|
||||
@@ -4012,11 +4067,15 @@
|
||||
"name": "Pine",
|
||||
"ports": [
|
||||
{
|
||||
"auth": "gated",
|
||||
"bind": "127.0.0.1",
|
||||
"container": 80,
|
||||
"host": 10380,
|
||||
"protocol": "tcp"
|
||||
},
|
||||
{
|
||||
"auth": "none",
|
||||
"auth_rationale": "Pine's TLS listener. The gate speaks plain HTTP, so fronting this port would break the secure context navigator.bluetooth needs for WiFi provisioning. The plain-HTTP entry point (10380) is gated, and it is what the UI opens.",
|
||||
"container": 443,
|
||||
"host": 10381,
|
||||
"protocol": "tcp"
|
||||
@@ -4458,6 +4517,8 @@
|
||||
},
|
||||
"ports": [
|
||||
{
|
||||
"auth": "gated",
|
||||
"bind": "127.0.0.1",
|
||||
"container": 8080,
|
||||
"host": 8084,
|
||||
"protocol": "tcp"
|
||||
@@ -4837,7 +4898,5 @@
|
||||
}
|
||||
},
|
||||
"schema": 1,
|
||||
"signature": "cc83d0be50ce6144e2b5693a7175d7743d4a19141f4ef9a46a3c88d2dadd848acda9c25063e7a8b5643cecb2ccde279762a00a9715a5d26c99a95b492bc82a05",
|
||||
"signed_by": "did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur",
|
||||
"updated": "2026-08-04"
|
||||
"updated": "2026-08-05"
|
||||
}
|
||||
|
||||
+23
-3
@@ -321,7 +321,17 @@ fi
|
||||
UI_DOCKER_DEST="/opt/archipelago/docker"
|
||||
sudo mkdir -p "$UI_DOCKER_DEST"
|
||||
UI_REBUILD_LIST=""
|
||||
for ui in bitcoin-ui lnd-ui electrs-ui; do
|
||||
# fips-ui and fedimint-ui are synced but NOT added to UI_REBUILD_LIST below:
|
||||
# container-specs.sh has no spec for either (and their container names break
|
||||
# the archy-<ui> assumption — the FIPS one is plain `fips-ui`). Their rebuilds
|
||||
# come from elsewhere — the daemon's companion installer for fedimint-ui, the
|
||||
# orchestrator's build context for fips-ui — but BOTH read
|
||||
# /opt/archipelago/docker/<ui>, and nothing was ever updating that directory.
|
||||
# So source edits to those two trees reached nodes through no path at all:
|
||||
# their nginx kept listening on 0.0.0.0 and served the Guardian and FIPS
|
||||
# screens unauthenticated on every interface (found by scanning archi-dev-box
|
||||
# from outside, 2026-08-05 — the in-node audit could not see them).
|
||||
for ui in bitcoin-ui lnd-ui electrs-ui fips-ui fedimint-ui; do
|
||||
src="$REPO_DIR/docker/$ui"
|
||||
dst="$UI_DOCKER_DEST/$ui"
|
||||
[ -d "$src" ] || continue
|
||||
@@ -335,9 +345,19 @@ for ui in bitcoin-ui lnd-ui electrs-ui; do
|
||||
dst_hash=$( (cd "$dst" && find . -type f | LC_ALL=C sort | xargs sha256sum 2>/dev/null) | sha256sum | cut -d' ' -f1)
|
||||
fi
|
||||
if [ "$src_hash" != "$dst_hash" ]; then
|
||||
log "UI source changed for $ui; syncing and marking for rebuild"
|
||||
log "UI source changed for $ui; syncing"
|
||||
sudo rsync -a --delete "$src/" "$dst/"
|
||||
UI_REBUILD_LIST="$UI_REBUILD_LIST $ui"
|
||||
case "$ui" in
|
||||
# Rebuilt below from container-specs.sh.
|
||||
bitcoin-ui|lnd-ui|electrs-ui)
|
||||
UI_REBUILD_LIST="$UI_REBUILD_LIST $ui" ;;
|
||||
# Synced only — rebuilt by the daemon (companion installer /
|
||||
# orchestrator build context), which watches this directory.
|
||||
# Adding them to the rebuild list would fail: no spec exists and
|
||||
# the container names are not archy-<ui>.
|
||||
*)
|
||||
log " $ui synced; rebuild is owned by the daemon" ;;
|
||||
esac
|
||||
else
|
||||
ok "UI source unchanged for $ui"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user