fix: first-boot container creation, remote input relay, ISO packages

Critical first-boot fixes (root cause: ALL 25 containers failed on install):
- Fix image-versions.sh sourcing: multi-path fallback for /opt/archipelago/scripts/
- Fix --add-host host-gateway: resolve actual gateway IP (podman 4.3 compat)
- Fix disk size detection: check /var/lib/archipelago not / (was forcing prune on 428GB disk)
- Fix Bitcoin health check: expand $RPC vars at creation, not inside container
- Add --network-alias to all containers (aardvark-dns reliability)
- Add --network-alias to backend RPC install handler

ISO build:
- Add apache2-utils for htpasswd (Fedimint gateway password hashing)

Remote input:
- Add broadcast relay channel for companion app → browser input forwarding
- Add /ws/remote-relay WebSocket endpoint
- Android: NES controller improvements, server connect flow updates

Container images:
- Fix lnd-ui Dockerfile: listen on 8080, run as root user (rootless compat)
- Fix bitcoin-ui, electrs-ui Dockerfiles: root user for rootless podman

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-04-02 10:34:58 +01:00
co-authored by Claude Opus 4.6
parent 051d3b1375
commit 07808a95c4
13 changed files with 238 additions and 71 deletions
+15 -1
View File
@@ -13,6 +13,7 @@ use crate::state::StateManager;
use anyhow::Result;
use hyper::{Method, Request, Response, StatusCode};
use std::sync::Arc;
use tokio::sync::broadcast;
use tracing::debug;
/// Build an HTTP response without unwrap. Falls back to a plain 500 if builder fails.
@@ -32,6 +33,8 @@ pub struct ApiHandler {
state_manager: Arc<StateManager>,
metrics_store: Arc<MetricsStore>,
session_store: SessionStore,
/// Broadcast channel for relaying companion app input to remote browsers.
input_relay_tx: broadcast::Sender<String>,
}
impl ApiHandler {
@@ -50,6 +53,7 @@ impl ApiHandler {
)
.await?,
);
let (input_relay_tx, _) = broadcast::channel(64);
Ok(Self {
config,
@@ -57,6 +61,7 @@ impl ApiHandler {
state_manager,
metrics_store,
session_store,
input_relay_tx,
})
}
@@ -147,7 +152,16 @@ impl ApiHandler {
tracing::warn!("401 WebSocket /ws/remote-input — session invalid or missing");
return Ok(Self::unauthorized());
}
return Self::handle_remote_input(req).await;
return Self::handle_remote_input(req, self.input_relay_tx.clone()).await;
}
// Remote relay WebSocket — browser receives companion input events
if method == Method::GET && path == "/ws/remote-relay" {
if !self.is_authenticated(req.headers()).await {
tracing::warn!("401 WebSocket /ws/remote-relay — session invalid or missing");
return Ok(Self::unauthorized());
}
return Self::handle_remote_relay(req, self.input_relay_tx.subscribe()).await;
}
// Convert body to bytes for non-WS routes