Compare commits

..
Author SHA1 Message Date
archipelago ea4c072183 chore: release v1.7.123-alpha
Demo images / Build & push demo images (push) Successful in 3m58s
2026-08-05 09:35:47 -04:00
archipelagoandClaude Fable 5 cfd1b4c731 chore(trust): flip the signing checks to the new release root
v1.7.122-alpha was the last release signed with the old root — it is the
release that installed the new pin on every node. From v1.7.123 the new
root signs, and a node running .122+ rejects an old-key signature. The
ARCHY_RELEASE_ROOT_PUBKEY override is no longer needed either: the signer
built from this tree pins the same key we now sign with.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 09:31:28 -04:00
archipelagoandClaude Fable 5 27c1b151f8 docs(1.7.123): curate release notes and add the in-app What's New block
Demo images / Build & push demo images (push) Successful in 3m59s
Leads with the honest version: five screens were open and the previous
release's own audit reported them as fine, found by scanning from another
machine rather than asking the node. States plainly that what leaked was
the page, not credentials — the macaroon path was verified, not assumed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 09:08:07 -04:00
archipelagoandClaude Fable 5 9c736f20b6 fix(security): publish the loopback-pinned UI images and pin the new tags
Fresh installs pull *-ui images from the registry, so the source fix alone
left a newly flashed node serving the Bitcoin, LND, Electrs, FIPS and
Guardian screens with no login. All five rebuilt and pushed to
146.59.87.168:3000/lfg2025 as 1.7.123-alpha AND :latest — both tags,
because first-boot resolves the pinned tag from image-versions.sh while the
daemon's companion installer hardcodes :latest, and a stale :latest would
have quietly undone the fix on exactly the path that rebuilds companions.

Verified by pulling each image back from the registry anonymously and
reading /etc/nginx/conf.d/default.conf inside it — a private package would
make fresh nodes fall back to a stale local image without saying so.

Also fixes the FOURTH copy of bitcoin-ui's listen directive
(scripts/reconcile-containers.sh wrote 'listen 8334' into the rendered
nginx.conf on every reconcile, which would have re-opened the port after
the image and template were both corrected).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-05 09:07:05 -04:00
archipelagoandClaude Fable 5 abf0f56afc 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>
2026-08-05 08:46:09 -04:00
33 changed files with 325 additions and 104 deletions
+10
View File
@@ -1,5 +1,15 @@
# Changelog
## v1.7.123-alpha (2026-08-05)
- **Five more screens on your node were readable by anyone who could reach it, and the previous release's own check said they were fine.** The Bitcoin, Lightning, Electrum, FIPS mesh and Fedimint Guardian screens each answered on their port with no login. They were missed because they work differently from ordinary apps: they run directly on the node's network rather than behind its container plumbing, so there was no address to pin and their descriptions listed no port at all — and the node builds its list of what to protect from exactly those descriptions. It therefore neither protected them nor listed them as unprotected. A check that reports success while five screens are open is worse than no check, and this was found by scanning the node from another machine rather than asking the node about itself.
- **What was actually readable was the page, not your money.** Every request on those ports that could have returned a credential — the Lightning connection details, the wallet passthrough, container logs, and every node command — already required a login and still refused without one. The Lightning macaroon fix from v1.7.120 was verified directly rather than assumed. What leaked was the screen itself: layout and code, no wallet data, no keys.
- All five now serve only to the node itself, with the login gate in front of them, exactly like the twenty app screens closed in the previous release.
- **Every port on the node now has a stated policy — there are no undecided ones left.** Eleven ports previously had no instruction either way and stayed open by default. The BotFights arena, the router screen and the Pine voice screen now require the node password. The ones that genuinely cannot take a login page stay open with a written reason: Fedimint's guardian and gateway connections (federation members authenticate to the federation), NetBird's management and dashboard ports (your VPN devices carry their own credentials and cannot hold a browser session, and its dashboard needs its own certificate), Pine's secure listener, and the Lightning REST port, which wallets reach with a macaroon exactly as before.
- Fresh installs are covered too, not just existing nodes. The five screens are delivered as prebuilt images, so a newly flashed node would have come up open even after this fix. All five were rebuilt, published, and then pulled back and inspected to confirm the fix is really inside them.
- Two delivery faults fixed alongside, either of which would have silently undone the above: two of the five screens were reaching nodes through no update path at all, so edits to them never arrived; and a fourth copy of the Bitcoin screen's configuration was being rewritten on every health check, which would have re-opened that port after everything else was corrected.
- Known gaps, disclosed rather than buried: non-browser clients — phone apps for Vaultwarden, Home Assistant or Jellyfin, and git over the web — meet the login page and need an access token. Three voice-assistant ports remain open without authentication; the correct fix puts them on a private network with the assistant. The 5x real-node lifecycle gate was not run for this release.
## v1.7.122-alpha (2026-08-04)
- **Your apps now ask for your node password before they open — over your home network, Tailscale, the mesh and Tor alike.** Until now anyone who could reach your node could open Immich, Nextcloud, Vaultwarden, Jellyfin, Grafana and the rest simply by typing the address and port, with no login at all. Twenty app screens now sit behind the same login you use for the node, showing you which app you are opening, and honouring two-factor if you have it switched on. Logging in at an app address logs you into the dashboard too, so it is one password, not one per app. This completes the groundwork disclosed in v1.7.121.
+12 -1
View File
@@ -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
+2
View File
@@ -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
+12 -1
View File
@@ -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: []
+8
View File
@@ -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
+18
View File
@@ -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
+12 -1
View File
@@ -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: []
+5
View File
@@ -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
+12 -1
View File
@@ -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: []
+5
View File
@@ -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
+5
View File
@@ -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
+7
View File
@@ -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
+2
View File
@@ -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
+1 -1
View File
@@ -104,7 +104,7 @@ dependencies = [
[[package]]
name = "archipelago"
version = "1.7.122-alpha"
version = "1.7.123-alpha"
dependencies = [
"anyhow",
"archipelago-container",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "archipelago"
version = "1.7.122-alpha"
version = "1.7.123-alpha"
edition = "2021"
description = "Archipelago Bitcoin Node OS - Native backend"
authors = ["Archipelago Team"]
+1 -1
View File
@@ -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;
+8 -1
View File
@@ -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;
+8 -1
View File
@@ -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;
+8 -1
View File
@@ -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;
+8 -1
View File
@@ -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;
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "neode-ui",
"version": "1.7.122-alpha",
"version": "1.7.123-alpha",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "neode-ui",
"version": "1.7.122-alpha",
"version": "1.7.123-alpha",
"dependencies": {
"@scure/bip39": "^2.2.0",
"@types/dompurify": "^3.0.5",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "neode-ui",
"private": true,
"version": "1.7.122-alpha",
"version": "1.7.123-alpha",
"type": "module",
"scripts": {
"start": "./start-dev.sh",
@@ -362,6 +362,21 @@ init()
</button>
</div>
<div class="overflow-y-auto flex-1 min-h-0 space-y-6 pr-1">
<!-- v1.7.123-alpha -->
<div>
<div class="flex items-center gap-2 mb-3">
<span class="text-xs font-mono px-2 py-0.5 rounded bg-orange-500/20 text-orange-300">v1.7.123-alpha</span>
<span class="text-xs text-white/40">August 5, 2026</span>
</div>
<div class="space-y-3 text-sm text-white/80 pl-3 border-l border-white/10">
<p><strong>Five more screens on your node were readable by anyone who could reach it and the last release's own check said they were fine.</strong> The Bitcoin, Lightning, Electrum, FIPS mesh and Fedimint Guardian screens each answered on their port with no login. They were missed because they run directly on the node's network rather than behind its container plumbing, so there was no address to pin and their descriptions listed no port at all and the node builds its list of what to protect from exactly those descriptions. It neither protected them nor listed them as unprotected. Found by scanning the node from another machine instead of asking the node about itself.</p>
<p><strong>What was readable was the page, not your money.</strong> Every request on those ports that could have returned a credential the Lightning connection details, the wallet passthrough, container logs, and every node command already required a login and still refused without one. The Lightning macaroon fix from v1.7.120 was checked directly rather than assumed. What leaked was the screen itself: layout and code, no wallet data, no keys.</p>
<p>All five now serve only to the node itself, with the login gate in front, exactly like the twenty app screens closed in the last release.</p>
<p><strong>Every port on your node now has a stated policy none are left undecided.</strong> Eleven had no instruction either way and stayed open by default. The BotFights arena, the router screen and the Pine voice screen now ask for your node password. The ones that genuinely cannot take a login page stay open with a written reason: Fedimint's guardian and gateway connections, NetBird's management and dashboard ports (your VPN devices carry their own credentials and cannot hold a browser session), Pine's secure listener, and the Lightning REST port that wallets reach with a macaroon exactly as before.</p>
<p>Freshly flashed nodes are covered too, not only existing ones those five screens ship as prebuilt images, so a new node would otherwise have come up open even after this fix. All five were rebuilt, published, then pulled back and inspected to confirm the fix is really inside them.</p>
<p>Known gaps, disclosed rather than buried: non-browser clients phone apps for Vaultwarden, Home Assistant or Jellyfin, and git over the web meet the login page and need an access token. Three voice-assistant ports remain open without authentication; the correct fix puts them on a private network with the assistant. The 5x real-node lifecycle gate was not run for this release.</p>
</div>
</div>
<!-- v1.7.122-alpha -->
<div>
<div class="flex items-center gap-2 mb-3">
+21 -22
View File
@@ -1,34 +1,33 @@
{
"changelog": [
"**Your apps now ask for your node password before they open — over your home network, Tailscale, the mesh and Tor alike.** Until now anyone who could reach your node could open Immich, Nextcloud, Vaultwarden, Jellyfin, Grafana and the rest simply by typing the address and port, with no login at all. Twenty app screens now sit behind the same login you use for the node, showing you which app you are opening, and honouring two-factor if you have it switched on. Logging in at an app address logs you into the dashboard too, so it is one password, not one per app. This completes the groundwork disclosed in v1.7.121.",
"**The things that must stay open stayed open.** Zeus and other remote wallets still reach your Lightning node directly, Electrum wallets still connect, and Bitcoin still talks to its peers — those connections carry their own proof of identity and a login page would simply break them. Every one of these seventeen exceptions now has to state in writing why it is safe to leave open, so the list is something you can read rather than something you have to discover.",
"**A private address on your node was answering the mesh without a password.** One app's port was marked as being for this machine only, and the part of the node that carries mesh traffic did not know that — it forwarded requests from the whole mesh straight to it. Found while verifying the work above on a real node, not in testing. That path now refuses anything marked machine-only, and the app is reachable only from the node itself, as intended.",
"**Tor addresses no longer skip the login.** An app published as a .onion address was handed straight to the app, because a Tor visitor carries no session cookie to check. The login gate now takes those addresses first, closing the last of the four routes that went around it.",
"Nodes fix themselves after this update. Apps installed before this system used its current container setup kept their old wide-open address even after the signed list told them to move, and each would otherwise have needed hand-holding on every node. Your node now notices the difference and rebuilds those apps itself, keeping their data, within about half a minute of starting. Verified by putting a node back into the old state deliberately and watching it repair.",
"The node had been reading two different sets of instructions about its own apps — the signed list it downloads, and older copies on disk — which is how a port meant to stay private was briefly opened on a test node. Both now come from the signed list, and a port withdrawn from the login gate is released without needing a restart.",
"**The key that signs these updates has been replaced.** The previous signing key was exposed where it should not have been, so it is treated as compromised and this release installs its replacement. This update is the last one signed with the old key, by necessity — it is the one that teaches your node the new one.",
"Known gaps, disclosed rather than buried: eleven app ports still have no stated policy — BotFights, the Fedimint gateway, NetBird, the voice assistant's own screens and the router screen — and remain reachable without a login until each is decided deliberately; the node reports them rather than guessing, because guessing at an unstated setting caused both incidents behind this work. Three voice-assistant ports are still open without authentication; the correct fix puts them on a private network with the assistant. Non-browser clients — phone apps for Vaultwarden, Home Assistant or Jellyfin, and git over the web — will now meet the login page and need an access token. The 5x real-node lifecycle gate was not run for this release."
"**Five more screens on your node were readable by anyone who could reach it, and the previous release's own check said they were fine.** The Bitcoin, Lightning, Electrum, FIPS mesh and Fedimint Guardian screens each answered on their port with no login. They were missed because they work differently from ordinary apps: they run directly on the node's network rather than behind its container plumbing, so there was no address to pin and their descriptions listed no port at all — and the node builds its list of what to protect from exactly those descriptions. It therefore neither protected them nor listed them as unprotected. A check that reports success while five screens are open is worse than no check, and this was found by scanning the node from another machine rather than asking the node about itself.",
"**What was actually readable was the page, not your money.** Every request on those ports that could have returned a credential — the Lightning connection details, the wallet passthrough, container logs, and every node command — already required a login and still refused without one. The Lightning macaroon fix from v1.7.120 was verified directly rather than assumed. What leaked was the screen itself: layout and code, no wallet data, no keys.",
"All five now serve only to the node itself, with the login gate in front of them, exactly like the twenty app screens closed in the previous release.",
"**Every port on the node now has a stated policy — there are no undecided ones left.** Eleven ports previously had no instruction either way and stayed open by default. The BotFights arena, the router screen and the Pine voice screen now require the node password. The ones that genuinely cannot take a login page stay open with a written reason: Fedimint's guardian and gateway connections (federation members authenticate to the federation), NetBird's management and dashboard ports (your VPN devices carry their own credentials and cannot hold a browser session, and its dashboard needs its own certificate), Pine's secure listener, and the Lightning REST port, which wallets reach with a macaroon exactly as before.",
"Fresh installs are covered too, not just existing nodes. The five screens are delivered as prebuilt images, so a newly flashed node would have come up open even after this fix. All five were rebuilt, published, and then pulled back and inspected to confirm the fix is really inside them.",
"Two delivery faults fixed alongside, either of which would have silently undone the above: two of the five screens were reaching nodes through no update path at all, so edits to them never arrived; and a fourth copy of the Bitcoin screen's configuration was being rewritten on every health check, which would have re-opened that port after everything else was corrected.",
"Known gaps, disclosed rather than buried: non-browser clients — phone apps for Vaultwarden, Home Assistant or Jellyfin, and git over the web — meet the login page and need an access token. Three voice-assistant ports remain open without authentication; the correct fix puts them on a private network with the assistant. The 5x real-node lifecycle gate was not run for this release."
],
"components": [
{
"current_version": "1.7.122-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.122-alpha/archipelago",
"current_version": "1.7.123-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.123-alpha/archipelago",
"name": "archipelago",
"new_version": "1.7.122-alpha",
"sha256": "06aedbd235e962574b7abc5d6992c26b77cd943655e775cd93c84fdcc79ffab0",
"size_bytes": 54957496
"new_version": "1.7.123-alpha",
"sha256": "b7974a671f8f7987fab548d0a9cac1ad145e9a52f4099791c3d1879d065a9f47",
"size_bytes": 54957360
},
{
"current_version": "1.7.122-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.122-alpha/archipelago-frontend-1.7.122-alpha.tar.gz",
"name": "archipelago-frontend-1.7.122-alpha.tar.gz",
"new_version": "1.7.122-alpha",
"sha256": "865f5a0edb5eed1ced9dc4597b9112f24706d3538f8ffe84dea8104049d26af3",
"size_bytes": 210528707
"current_version": "1.7.123-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.123-alpha/archipelago-frontend-1.7.123-alpha.tar.gz",
"name": "archipelago-frontend-1.7.123-alpha.tar.gz",
"new_version": "1.7.123-alpha",
"sha256": "075370c761f07373e553db07eb5e019d96ceb591fa9c1067e1adc6701fa30752",
"size_bytes": 210533854
}
],
"release_date": "2026-08-05",
"signature": "aca66567bf5954aefd450167f881289ee4715fd912fe61a50726741cadf1a93d39e832efc3266388839279ad41001c9802fdfaf766c8cfa9399509916ed4a80f",
"signed_by": "did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur",
"version": "1.7.122-alpha"
"signature": "f64eaf9731277865c0d3f757f63dc2f4921fb7d90deb1615ca2b2b0f4c173282524b39a2d0b8f027c7532e918c349a86b14ed1b7992a96b0c6a80cd46b5d500d",
"signed_by": "did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT",
"version": "1.7.123-alpha"
}
+68 -7
View File
@@ -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,7 @@
}
},
"schema": 1,
"signature": "cc83d0be50ce6144e2b5693a7175d7743d4a19141f4ef9a46a3c88d2dadd848acda9c25063e7a8b5643cecb2ccde279762a00a9715a5d26c99a95b492bc82a05",
"signed_by": "did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur",
"updated": "2026-08-04"
"signature": "cc19214673b37c2d7d41edf996d5002abe41cf933ee9f33fb7182b05c6c40aa1a555ad596228c4a83dc4a11166f3345e7478399f9693f5f2da3818020980eb03",
"signed_by": "did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT",
"updated": "2026-08-05"
}
+21 -22
View File
@@ -1,34 +1,33 @@
{
"changelog": [
"**Your apps now ask for your node password before they open — over your home network, Tailscale, the mesh and Tor alike.** Until now anyone who could reach your node could open Immich, Nextcloud, Vaultwarden, Jellyfin, Grafana and the rest simply by typing the address and port, with no login at all. Twenty app screens now sit behind the same login you use for the node, showing you which app you are opening, and honouring two-factor if you have it switched on. Logging in at an app address logs you into the dashboard too, so it is one password, not one per app. This completes the groundwork disclosed in v1.7.121.",
"**The things that must stay open stayed open.** Zeus and other remote wallets still reach your Lightning node directly, Electrum wallets still connect, and Bitcoin still talks to its peers — those connections carry their own proof of identity and a login page would simply break them. Every one of these seventeen exceptions now has to state in writing why it is safe to leave open, so the list is something you can read rather than something you have to discover.",
"**A private address on your node was answering the mesh without a password.** One app's port was marked as being for this machine only, and the part of the node that carries mesh traffic did not know that — it forwarded requests from the whole mesh straight to it. Found while verifying the work above on a real node, not in testing. That path now refuses anything marked machine-only, and the app is reachable only from the node itself, as intended.",
"**Tor addresses no longer skip the login.** An app published as a .onion address was handed straight to the app, because a Tor visitor carries no session cookie to check. The login gate now takes those addresses first, closing the last of the four routes that went around it.",
"Nodes fix themselves after this update. Apps installed before this system used its current container setup kept their old wide-open address even after the signed list told them to move, and each would otherwise have needed hand-holding on every node. Your node now notices the difference and rebuilds those apps itself, keeping their data, within about half a minute of starting. Verified by putting a node back into the old state deliberately and watching it repair.",
"The node had been reading two different sets of instructions about its own apps — the signed list it downloads, and older copies on disk — which is how a port meant to stay private was briefly opened on a test node. Both now come from the signed list, and a port withdrawn from the login gate is released without needing a restart.",
"**The key that signs these updates has been replaced.** The previous signing key was exposed where it should not have been, so it is treated as compromised and this release installs its replacement. This update is the last one signed with the old key, by necessity — it is the one that teaches your node the new one.",
"Known gaps, disclosed rather than buried: eleven app ports still have no stated policy — BotFights, the Fedimint gateway, NetBird, the voice assistant's own screens and the router screen — and remain reachable without a login until each is decided deliberately; the node reports them rather than guessing, because guessing at an unstated setting caused both incidents behind this work. Three voice-assistant ports are still open without authentication; the correct fix puts them on a private network with the assistant. Non-browser clients — phone apps for Vaultwarden, Home Assistant or Jellyfin, and git over the web — will now meet the login page and need an access token. The 5x real-node lifecycle gate was not run for this release."
"**Five more screens on your node were readable by anyone who could reach it, and the previous release's own check said they were fine.** The Bitcoin, Lightning, Electrum, FIPS mesh and Fedimint Guardian screens each answered on their port with no login. They were missed because they work differently from ordinary apps: they run directly on the node's network rather than behind its container plumbing, so there was no address to pin and their descriptions listed no port at all — and the node builds its list of what to protect from exactly those descriptions. It therefore neither protected them nor listed them as unprotected. A check that reports success while five screens are open is worse than no check, and this was found by scanning the node from another machine rather than asking the node about itself.",
"**What was actually readable was the page, not your money.** Every request on those ports that could have returned a credential — the Lightning connection details, the wallet passthrough, container logs, and every node command — already required a login and still refused without one. The Lightning macaroon fix from v1.7.120 was verified directly rather than assumed. What leaked was the screen itself: layout and code, no wallet data, no keys.",
"All five now serve only to the node itself, with the login gate in front of them, exactly like the twenty app screens closed in the previous release.",
"**Every port on the node now has a stated policy — there are no undecided ones left.** Eleven ports previously had no instruction either way and stayed open by default. The BotFights arena, the router screen and the Pine voice screen now require the node password. The ones that genuinely cannot take a login page stay open with a written reason: Fedimint's guardian and gateway connections (federation members authenticate to the federation), NetBird's management and dashboard ports (your VPN devices carry their own credentials and cannot hold a browser session, and its dashboard needs its own certificate), Pine's secure listener, and the Lightning REST port, which wallets reach with a macaroon exactly as before.",
"Fresh installs are covered too, not just existing nodes. The five screens are delivered as prebuilt images, so a newly flashed node would have come up open even after this fix. All five were rebuilt, published, and then pulled back and inspected to confirm the fix is really inside them.",
"Two delivery faults fixed alongside, either of which would have silently undone the above: two of the five screens were reaching nodes through no update path at all, so edits to them never arrived; and a fourth copy of the Bitcoin screen's configuration was being rewritten on every health check, which would have re-opened that port after everything else was corrected.",
"Known gaps, disclosed rather than buried: non-browser clients — phone apps for Vaultwarden, Home Assistant or Jellyfin, and git over the web — meet the login page and need an access token. Three voice-assistant ports remain open without authentication; the correct fix puts them on a private network with the assistant. The 5x real-node lifecycle gate was not run for this release."
],
"components": [
{
"current_version": "1.7.122-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.122-alpha/archipelago",
"current_version": "1.7.123-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.123-alpha/archipelago",
"name": "archipelago",
"new_version": "1.7.122-alpha",
"sha256": "06aedbd235e962574b7abc5d6992c26b77cd943655e775cd93c84fdcc79ffab0",
"size_bytes": 54957496
"new_version": "1.7.123-alpha",
"sha256": "b7974a671f8f7987fab548d0a9cac1ad145e9a52f4099791c3d1879d065a9f47",
"size_bytes": 54957360
},
{
"current_version": "1.7.122-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.122-alpha/archipelago-frontend-1.7.122-alpha.tar.gz",
"name": "archipelago-frontend-1.7.122-alpha.tar.gz",
"new_version": "1.7.122-alpha",
"sha256": "865f5a0edb5eed1ced9dc4597b9112f24706d3538f8ffe84dea8104049d26af3",
"size_bytes": 210528707
"current_version": "1.7.123-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.123-alpha/archipelago-frontend-1.7.123-alpha.tar.gz",
"name": "archipelago-frontend-1.7.123-alpha.tar.gz",
"new_version": "1.7.123-alpha",
"sha256": "075370c761f07373e553db07eb5e019d96ceb591fa9c1067e1adc6701fa30752",
"size_bytes": 210533854
}
],
"release_date": "2026-08-05",
"signature": "aca66567bf5954aefd450167f881289ee4715fd912fe61a50726741cadf1a93d39e832efc3266388839279ad41001c9802fdfaf766c8cfa9399509916ed4a80f",
"signed_by": "did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur",
"version": "1.7.122-alpha"
"signature": "f64eaf9731277865c0d3f757f63dc2f4921fb7d90deb1615ca2b2b0f4c173282524b39a2d0b8f027c7532e918c349a86b14ed1b7992a96b0c6a80cd46b5d500d",
"signed_by": "did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT",
"version": "1.7.123-alpha"
}
+5 -13
View File
@@ -240,19 +240,11 @@ install -m 0644 "$FRONTEND_ARCHIVE" "$VERSION_DIR/archipelago-frontend-${VERSION
# warning and falls through — and the commit then happened anyway. A release
# commit carrying a manifest no node will accept has no valid use, so refuse
# to create one rather than leave a tag that has to be re-cut.
# ⚠ ROTATION IN FLIGHT (v1.7.122-alpha) — this is the OLD root, deliberately.
#
# The trust anchor in the binary already pins the NEW root
# (z6Mkfu5LT…DLWT), because this release is what installs that pin. But the
# manifest THIS release ships must be signed with the OLD root
# (z6Mkkid…q7ur): every node is still running the previous binary, which
# pins the old key and would reject anything else. Signing this one with the
# new key ends OTA fleet-wide and needs hands-on recovery per node.
#
# ➜ NEXT RELEASE (v1.7.123+): change this to the new DID, and the same line
# in publish-release-assets.sh. By then every node runs a binary pinning
# the new root, and an old-key signature is the one that gets rejected.
EXPECTED_DID="did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur"
# Release root ROTATED 2026-08-05. v1.7.122-alpha was the last release signed
# with the old root (z6Mkkid…q7ur) — it is the release that installed this
# pin on every node. From v1.7.123 onward the new root signs, and nodes
# running .122+ reject anything signed with the old key.
EXPECTED_DID="did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT"
if ! grep -q '"signature":' "$PROJECT_ROOT/releases/manifest.json" \
|| ! grep -q "\"signed_by\": \"$EXPECTED_DID\"" "$PROJECT_ROOT/releases/manifest.json"; then
echo "" >&2
+4 -4
View File
@@ -84,7 +84,7 @@ STRFRY_IMAGE="$ARCHY_REGISTRY/strfry:1.0.4"
NOSTR_VPN_IMAGE="$ARCHY_REGISTRY/nostr-vpn:v0.3.7"
NOSTR_VPN_UI_IMAGE="$ARCHY_REGISTRY/nostr-vpn-ui:latest"
FIPS_IMAGE="$ARCHY_REGISTRY/fips:v0.1.0"
FIPS_UI_IMAGE="$ARCHY_REGISTRY/fips-ui:latest"
FIPS_UI_IMAGE="$ARCHY_REGISTRY/fips-ui:1.7.123-alpha"
# AI / Routing
ROUTSTR_IMAGE="$ARCHY_REGISTRY/routstr:v0.4.3"
@@ -117,9 +117,9 @@ PENPOT_EXPORTER_IMAGE="$ARCHY_REGISTRY/penpot-exporter:2.4"
PENPOT_FRONTEND_IMAGE="$ARCHY_REGISTRY/penpot-frontend:2.4"
# Custom UI containers (built from docker/ dirs, pushed to registry)
BITCOIN_UI_IMAGE="$ARCHY_REGISTRY/bitcoin-ui:1.7.119-alpha"
LND_UI_IMAGE="$ARCHY_REGISTRY/lnd-ui:1.7.119-alpha"
ELECTRS_UI_IMAGE="$ARCHY_REGISTRY/electrs-ui:latest"
BITCOIN_UI_IMAGE="$ARCHY_REGISTRY/bitcoin-ui:1.7.123-alpha"
LND_UI_IMAGE="$ARCHY_REGISTRY/lnd-ui:1.7.123-alpha"
ELECTRS_UI_IMAGE="$ARCHY_REGISTRY/electrs-ui:1.7.123-alpha"
# Base images
NGINX_ALPINE_IMAGE="$ARCHY_REGISTRY/nginx:1.27.4-alpine"
+3 -5
View File
@@ -29,11 +29,9 @@ fail() { echo "Error: $*" >&2; exit 1; }
# with the pinned release-root anchor refuse to auto-apply unsigned manifests,
# and enforcement will tighten to hard-reject — an unsigned publish would
# strand them. Grep proves presence; ceremony verify proves the crypto.
# ⚠ ROTATION IN FLIGHT (v1.7.122-alpha) — OLD root on purpose; see the same
# block in create-release.sh. Nodes still run the previous binary and pin the
# old key, so the manifest this release publishes must carry an old-key
# signature. Flip both to z6Mkfu5LT…DLWT for v1.7.123+.
EXPECTED_DID="did:key:z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur"
# Release root ROTATED 2026-08-05; see create-release.sh. New root from
# v1.7.123 onward.
EXPECTED_DID="did:key:z6Mkfu5LT8d4DjETtrkATvHh9Dvcbnr7zBCUwfau8Sw7DLWT"
grep -q '"signature":' "$PROJECT_ROOT/releases/manifest.json" \
&& grep -q "\"signed_by\": \"$EXPECTED_DID\"" "$PROJECT_ROOT/releases/manifest.json" \
|| fail "releases/manifest.json is not signed by the release root — run: bash scripts/sign-manifest.sh"
+6 -1
View File
@@ -809,7 +809,12 @@ ensure_bitcoin_ui_nginx_conf() {
tmp="${CONF_PATH}.tmp.$$"
sudo tee "$tmp" >/dev/null << EOF
server {
listen 8334;
# Loopback ONLY — this is the fourth copy of this declaration (the others
# are the Rust template in container/bitcoin_ui_nginx.conf.template, the
# image, and the manifest). Host networking means this nginx binds the
# HOST's address, so \`listen 8334;\` served the Bitcoin screen on every
# interface with no login. The app gate owns the external addresses now.
listen 127.0.0.1:8334;
server_name _;
root /usr/share/nginx/html;
index index.html;
+23 -3
View File
@@ -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
+3 -12
View File
@@ -12,18 +12,9 @@
# re-signing (e.g. a manifest edited after creation) or signing on a box where
# the release run was non-interactive.
#
# ⚠ ROTATION IN FLIGHT (v1.7.122-alpha). This release must be signed with the
# OLD release root, because every node still runs a binary pinning it — but
# the signer built from THIS tree already pins the NEW root, so its own
# verification would reject a correct old-key signature. Pin the old anchor
# for the duration of the ceremony so signing and verification agree:
#
# ARCHY_RELEASE_ROOT_PUBKEY=5d15cbee8a108f7dd288c02d29a1d9d71f198acc99186aad8008b4f28d469951 \
# bash scripts/sign-manifest.sh
#
# That hex is the OLD root's PUBLIC key (verified to derive to
# did:key:z6Mkkid…q7ur); it is not secret and pins verification only.
# From v1.7.123 the override is unnecessary — drop it and this block.
# The release root was rotated 2026-08-05. From v1.7.123 this signs with the
# NEW mnemonic and the signer's own anchor already pins that key, so no
# ARCHY_RELEASE_ROOT_PUBKEY override is needed (it was, for .122 only).
set -euo pipefail
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"