fix: ISO boot, container installs, VPN, nginx, companion input

- LUKS auto-unlock: initramfs hook + systemd service + nofail fstab
- Rootfs packages: add passt, aardvark-dns, netavark, nftables for Podman 5.x
- nginx: resolver + variable proxy_pass for external domains (DNS at boot)
- Boot: loglevel=0 suppresses kernel warnings, serial console for QEMU
- Container installs: write configs before chown, sudo chown for LUKS volumes
- Container installs: build UI sidecars locally (not from registry) for auth injection
- Bitcoin UI: inject RPC auth from secrets file, --no-cache rebuild
- Secrets: chown to archipelago user in first-boot (backend needs read access)
- Podman: image_copy_tmp_dir for read-only /var/tmp in user namespace
- NostrVPN: enable service in auto-install, always include public relays
- NostrVPN: read tunnel IP from nvpn status (not just config file)
- VPN invite: v2 base64 no-pad format matching phone app
- Companion input: relay always active, kiosk skips relay listener (prevents double input)
- dev-start.sh: production build includes AIUI deployment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-04-10 03:10:49 -04:00
co-authored by Claude Opus 4.6
parent 88bdba19db
commit 067a3ed106
10 changed files with 328 additions and 89 deletions
+24 -14
View File
@@ -269,19 +269,33 @@ impl RpcHandler {
let network_id = vpn::read_nvpn_config_list_entry("networks", "network_id").await
.unwrap_or_else(|| "nostr-vpn".to_string());
// Read relays from config
// Read relays from config — filter out localhost relays (unreachable from phone)
let relays = vpn::read_nvpn_config_list("nostr", "relays").await;
let relay_csv = if relays.is_empty() {
"wss://relay.damus.io,wss://relay.primal.net".to_string()
let reachable: Vec<String> = relays.iter()
.filter(|r| !r.contains("127.0.0.1") && !r.contains("localhost"))
.cloned()
.collect();
let invite_relays = if reachable.is_empty() {
vec!["wss://relay.damus.io".to_string(), "wss://relay.primal.net".to_string()]
} else {
relays.join(",")
reachable
};
// Build invite URL: nvpn://invite/<network_id>?npub=<npub>&relays=<csv>
let invite_url = format!(
"nvpn://invite/{}?npub={}&relays={}",
network_id, npub, relay_csv
);
// Build invite as base64-encoded JSON (nvpn v2 format, no padding)
use base64::Engine;
let invite_payload = serde_json::json!({
"v": 2,
"networkName": network_id,
"networkId": network_id,
"inviterNpub": npub,
"inviterNodeName": "archipelago",
"admins": [npub],
"participants": [npub],
"relays": invite_relays,
});
let invite_b64 = base64::engine::general_purpose::STANDARD_NO_PAD
.encode(invite_payload.to_string().as_bytes());
let invite_url = format!("nvpn://invite/{}", invite_b64);
// Generate QR code
let qr = qrcode::QrCode::new(invite_url.as_bytes())
@@ -295,11 +309,7 @@ impl RpcHandler {
"qr_svg": svg,
"npub": npub,
"network_id": network_id,
"relays": if relays.is_empty() {
vec!["wss://relay.damus.io".to_string(), "wss://relay.primal.net".to_string()]
} else {
relays
},
"relays": invite_relays,
}))
}