- Replace aardvark-dns container names with host.containers.internal
for all cross-app connections (LND→Bitcoin, ElectrumX→Bitcoin,
Mempool→ElectrumX, Fedimint→Bitcoin, NBXplorer→Bitcoin P2P+RPC)
- Add BTCPay multi-container stack installer (postgres + nbxplorer +
btcpay-server) with proper secrets, data dir ownership, NOAUTH
- Add Mempool multi-container stack installer (mariadb + mempool-api +
mempool-frontend) with host.containers.internal for RPC
- Immediately remove apps from state on uninstall (no 3-min ghost delay)
- Include archy-bitcoin-ui in bitcoin uninstall container list
- Fix LND UI port 8081 (was 8080, conflicting with LND gRPC)
- Fix ElectrumX UI: proxy /electrs-status to backend, cache-busting
headers, graceful fallback when backend returns HTML
- Add Tor hidden services for ElectrumX and LND in torrc template
- Remove unused detect_bitcoin_container_name() (replaced by
host.containers.internal)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Standalone WireGuard (wg0:51820):
- New archipelago-wg.service creates wg0 independent of NostrVPN
- Keypair generated on first-boot, persisted on LUKS partition
- vpn.create-peer uses wg genkey/pubkey (no nvpn dependency)
- wg-address service depends on archipelago-wg, not nostr-vpn
Networking fixes:
- Remove nos.lol from default relays (requires PoW, events rejected)
- Add Tor hidden service for private relay (port 7777) — NAT'd peers
can reach relay over Tor for NostrVPN signaling
- Fix Tor hostname sync race: wait loop before copying hostname files
- Add tor-hostnames + wireguard dirs to LUKS partition setup
- Include relay in hostname sync loops (setup-tor.sh + first-boot)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace disconnected "Generate Invite" + "Add participant" with a 2-step
wizard: enter phone npub → get invite QR + mesh details. Backend vpn.invite
now accepts optional npub param to add participant in the same call. Modal
shows network ID, node npub, and relay URLs for manual app configuration.
Also includes nostr-vpn service hardening (rate-limit restarts, reset-failed
before enable).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The nvpn daemon config at /var/lib/archipelago/nostr-vpn/ is owned by
root, but the backend runs as archipelago. Direct write silently failed,
so adding a second phone's npub never reached the daemon — service
restarted with stale config. Now falls back to sudo cp for root-owned
paths, and first-boot sets ownership to archipelago.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- VPN card: relay URLs, device management, invite QR, add participant
- Backend: vpn.invite, vpn.add-participant, vpn.peer-config RPCs
- nvpn v0.3.7 system service (fixes event processing bug in v0.3.4)
- First-boot: auto-configure nvpn with node identity and endpoint
- Service: AF_NETLINK for WireGuard, NoNewPrivileges=no for sudo wg
- TASK-50: networking stack reliability from first install
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Install progress bar replaces action buttons (no overlay)
- Hide status badge during install/uninstall
- Uninstall keeps progress state until container disappears from WebSocket
- Uninstall RPC timeout increased to 660s (Bitcoin UTXO flush)
- Installing apps appear in My Apps immediately as placeholders
- Auto-configure Tor hidden service for every app on install
- Widen Tor module visibility for install hooks
- Only clear stale install entries on error status
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add vpn.create-peer, vpn.list-peers, vpn.remove-peer RPC methods
- Generate WireGuard config + QR code (SVG) for mobile device connection
- Add "Add Device" modal on Network page with QR scanner support
- Remove old build-iso.yml (replaced by build-iso-dev.yml)
- Remove container-tests.yml (tests run in dev workflow)
- Remove container orchestration tests from dev workflow (redundant)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add system.settings.get/set RPC methods for Claude API key management
- Save key to secrets/claude-api-key, restart claude-api-proxy service
- Home Network card now fetches VPN status via vpn.status RPC
- Shows provider name (nostr-vpn, tailscale) instead of just "Connected"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FIPS stays in the marketplace as an installable container app.
NostrVPN is the native system service; FIPS is a separate optional app.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Convert NostrVPN from container app to native systemd service
- Auto-configure VPN with node's Nostr identity after onboarding
- Add nostr-vpn.service with proper capabilities (NET_ADMIN, NET_RAW)
- Remove FIPS from marketplace, container config, nginx, image-versions
(consolidated into NostrVPN — same mesh VPN concept)
- Add AIUI inclusion step to dev CI workflow
- AIUI installed on VPS build server for ISO inclusion
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- FIPS container expects FIPS_NSEC/FIPS_NPUB, not FIPS_NOSTR_SECRET
- NostrVPN container doesn't have a 'start' binary — use image default
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add three new marketplace apps:
- Routstr (v0.4.3): Decentralized AI inference proxy with Cashu payments
- Nostr VPN (v0.3.4): Mesh VPN with Nostr signaling + WireGuard tunnels
- FIPS (v0.1.0): Self-organizing encrypted mesh network
Includes status UI dashboards for headless apps (nostr-vpn-ui, fips-ui)
with usage instructions, node identity display, and container logs.
Nostr identity injected via env vars for all three apps.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical:
- BUILD_VERSION was hardcoded as "1.3.0-alpha" — now reads from Cargo.toml
This caused ALL ISOs to show v1.3.0 regardless of actual binary version
Kiosk:
- Remove --disable-gpu flags (broke display scaling on some monitors)
- Add --start-fullscreen --window-size for reliable fullscreen
New apps:
- Nostr VPN, FIPS, Routstr, noStrudel, BotFights, NWNN, 484 Kitchen,
Call the Operator, Arch Presentation, Syntropy Institute, T-0
Rust: suppress dead_code and unused_assignments warnings
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bitcoin Knots needs more memory headroom (was OOMing at 2g during IBD).
Reduce dbcache from 4096 to 2048 on large disks to stay within the 4g
container limit. Low-memory systems get 2g (was 1g).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move handle_remote_relay from remote_input.rs to remote_relay.rs
- Android: lifecycle-aware WebSocket reconnection on app resume
- Cleaner module boundaries between xdotool input and browser relay
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Container stability:
- Merge scan results instead of full replacement (prevents UI flapping)
- Absence threshold: 3 consecutive missed scans before removing from state
- container-list RPC uses cached scanner state for consistency
- Increased Podman API timeout 30s → 60s (scanner + health monitor)
- Keep crashed containers visible as "exited" instead of podman rm -f
- Resolve host-gateway IP via ip route (podman 4.3.x compatibility)
ISO build fixes:
- AIUI web app inclusion: searches 5 paths + CI step to copy from build server
- Claude API proxy: systemctl enable with symlink fallback
- AIUI nginx: try_files =404 (was /aiui/index.html redirect loop)
- Build version set to 1.3.0
Container fixes:
- lnd-ui: nginx listens on 8080 (was 80, Permission denied in rootless)
- first-boot: image-versions.sh sourced from correct path with validation
- first-boot: host-gateway resolved to actual gateway IP
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- NESPortraitController layout for vertical phone use
- Updated NESController and NESKeyboard components
- Remote input WebSocket handler and API route registration
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Containers installed via marketplace need host.containers.internal
to resolve for Tor proxy (9050) and inter-service communication.
Was only in first-boot-containers.sh and podman_client.rs, not in
the direct podman run path used by package.install RPC.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The backend's post-install hooks create archy-bitcoin-ui, archy-lnd-ui,
archy-electrs-ui containers but with only NET_BIND_SERVICE cap. Nginx
inside these containers crashes on chown in rootless podman.
Added --user=0:0, CHOWN, DAC_OVERRIDE, SETUID, SETGID caps to match
the first-boot-containers.sh pattern. Also fixed manifest publish
Python error (git log fails in rsync'd workspace with no .git).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a node was already known (via link-node) but had an empty onion
address, the peer-joined handler returned early without updating the
onion. Now it patches missing onion/pubkey fields on existing nodes.
Also adds update_node() to federation storage and updates the
architecture comparison doc with system resources, StartOS/umbrelOS
tabs, Web5 section, and comparison view.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LND crashes with "netlinkrib: address family not supported by protocol"
in rootless podman because it needs NET_RAW to enumerate network
interfaces during TLS certificate generation. Added to capabilities
in config.rs, first-boot-containers.sh, and container-specs.sh.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
LND v0.18+ crashes with "netlinkrib: address family not supported"
because rootless podman blocks netlink access for TLS cert SAN
enumeration. Fix: add tlsextraip=0.0.0.0 and tlsextradomain=lnd
to lnd.conf so LND skips interface enumeration.
Also: fix status label to show "crashed" for both exited and
stopped containers with non-zero exit codes (previously only
caught "exited" state, but podman reports "stopped" for
restart-looping containers).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the Bitcoin RPC 60-second gate that blocked 13+ dependent containers
(mempool, electrumx, btcpay, lnd, fedimint) from being created on first boot.
Containers now always get created and auto-restart via health monitor once
Bitcoin becomes responsive — the designed recovery path.
Additional hardening:
- Validate archy-net creation with retry (silent failure broke DNS)
- Verify critical images are loaded, re-load from tarballs if missing
- Create SearXNG settings.yml before container start (was missing)
- Run reconciler automatically after first-boot failures
- Add load-images as explicit systemd dependency with 900s timeout
- Propagate config write errors in install.rs (bitcoin.conf, lnd.conf)
- FileBrowser password change: retry loop (6 attempts) + 0o600 perms
- Post-start verification: detect containers that exit immediately
- Add 2s dependency waits between container starts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Install, start, and failure events logged to
/var/log/archipelago-container-installs.log with timestamps
- Enables post-mortem debugging of container lifecycle issues
- UI container hooks: try registry pull before local build fallback
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- session.rs: use OnceCell for remember_secret to prevent concurrent
requests on first boot from generating different HMAC secrets, which
caused CSRF token mismatch on every state-changing RPC call (app
install, start, stop all failed with "CSRF token missing or invalid")
- install.rs: write lnd.conf with Bitcoin RPC credentials before LND
container starts (prevents "bitcoin.mainnet must be specified" crash);
inject Bitcoin RPC auth into bitcoin-ui nginx.conf; add proper error
logging to UI container build/run steps; fix UI containers to use
--network=host (they proxy to localhost backend/bitcoin RPC)
- Tor: remove After=tor.service from archipelago-tor-helper.path to
break systemd ordering cycle that prevented Tor from starting on boot
- Seed screen: compact grid layout (2 cols mobile, 4 cols sm+) with
tighter padding to fit kiosk displays without scrolling
- Dockerfiles: remove nonexistent assets/ COPY from bitcoin-ui, fix
electrs-ui to COPY qrcode.js and EXPOSE 50002 (matches nginx.conf)
- image-versions.sh: add UI container image variables for registry
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace fragmented random key generation with a single 24-word BIP-39
mnemonic that deterministically derives all node keys: Ed25519 (DID),
secp256k1 (Nostr/Bitcoin), BIP-84 xprv (Bitcoin Core), and LND aezeed
entropy. New onboarding flow: seed generate → word verification → identity
naming. Restore path enabled via 24-word entry. Includes seed RPC handlers,
mock backend support, LND/Bitcoin Core wallet-from-seed integration, and
UI polish across settings and discover views.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
system.stats (Home page) and monitoring collector both used df /
which shows the small 29GB root partition. Now prefers
/var/lib/archipelago (the LUKS encrypted data partition) when it
exists — showing the actual 1.8TB storage users care about.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical:
- fix: container installs fail with "statfs: no such file or directory"
Root cause: NoNewPrivileges=yes in systemd blocks sudo inside backend.
Fix: use std::fs::create_dir_all + podman unshare chown (no sudo needed)
- fix: Tor services.json never written — \$ARCHY_TOR_DIR escaping bug
- fix: kiosk white screen — increase health wait to 60s, add --disable-gpu
Improvements:
- feat: LUKS encryption badge in Server disk stats (backend detects dm-crypt)
- fix: GRUB theme text scaling on 4:3 monitors — explicit fonts, wider menu
- fix: suppress default Debian MOTD (custom profile.d welcome is enough)
- fix: install error messages now show "Failed to pull/start" instead of
generic "Operation failed" (middleware.rs allowlist expanded)
- fix: container-tests CI — source cargo env before running tests
- docs: interactive container architecture diagram (HTML)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- fix: login disconnect — verify session before WebSocket connect
- fix: 403 on app install — distinguish CSRF vs RBAC errors, only retry CSRF
- fix: health monitor now watches ALL containers (removed skip list for
backend services like nbxplorer, databases, UI containers)
- fix: server.get-state added to CSRF-exempt list (read-only)
- fix: ISO build includes container-specs.sh and lib/common.sh in rootfs
so reconcile actually works on fresh installs
- fix: gamepad nav — improved Server tab zone nav, focus styles, autofocus
- chore: move L484 web-only apps to Services tab
- chore: install store for cross-view install tracking
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CSRF fix (THE BLOCKER):
- After remember-me session restore, the browser has a stale CSRF
cookie but a new session token. ALL subsequent RPC calls return 403.
- Fix: exempt read-only polling methods (node-messages-received,
server.echo, system.stats, tor.status, etc.) from CSRF validation.
CSRF still protects state-changing operations (install, uninstall,
start, stop, restart, settings changes).
Reboot fix:
- The separate /tmp/archipelago-reboot.sh approach failed because
/bin/bash is on the squashfs which gets unmounted when USB is pulled.
- Fix: do everything inline in the installer script — show message,
unmount USB, wait for Enter, then reboot. Use sysrq-trigger first
(kernel-level, doesn't need userspace binaries).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Version per build:
- Health endpoint returns "1.2.0-alpha-{git_hash}" using GIT_HASH env
- CI passes git hash to cargo build
FileBrowser auto-login:
- filebrowser-client.ts: include CSRF token + credentials:include
- First-boot: generate random password, store at secrets/filebrowser/
- Set FileBrowser admin password to match after container creation
Nostr relay:
- Use docker.io/scsibug/nostr-rs-relay:0.9.0 (not in our registry)
UID mappings:
- Added electrumx (UID 1000), mysql-mempool, archy-btcpay-db, nextcloud-db
522 tests pass, Rust compiles clean.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Container name resolution:
- New all_container_names() — single source of truth for every app's
container name variants (bitcoin-knots/bitcoin/bitcoin-core, etc.)
- Covers all historical naming patterns and multi-container stacks
Start/Stop/Restart:
- No more silent failures (let _ = podman...). Every operation logs
the command, checks exit status, and returns real errors to the UI.
- Restart uses stop+start fallback when podman restart fails
(handles rootless podman loopback adapter errors)
- "No containers found" error when app doesn't exist
Tor helper:
- Install archipelago-tor-helper.path + .service in rootfs
- Enable the path unit so backend can manage Tor as non-root
- Copy tor-helper.sh to /opt/archipelago/scripts/
Verified: container with proper caps can stop/start/restart cleanly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Critical: headless services (Bitcoin, LND, Electrumx) need companion
UI containers that serve web dashboards. These were only built for
Bitcoin, and only on bundled ISO builds.
Changes:
- install.rs: auto-build UI containers for LND (port 8081) and
Electrumx (port 50002) in addition to Bitcoin (port 8334)
- build-auto-installer-iso.sh: always bundle docker UI source files
(was skipping for unbundled builds — they're tiny HTML, not images)
- Dockerfiles: fix nginx base image tag 1.29.6→1.27.4 (matches registry)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fedimintd v0.10.0 requires --data-dir and --bitcoind-url as CLI args,
not just env vars. Container was exiting with usage error.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- LND: add --bitcoin.active --bitcoin.mainnet and all bitcoind
connection args as container CMD args (was only env var before)
- SearXNG: add volume mount + auto-create settings.yml on install
(container exits immediately without it)
- Default caps: all containers get full rootless podman baseline
Tested on .198:
- Bitcoin Knots: running, syncing (942803 blocks)
- Grafana: running, migration complete
- Vaultwarden: running, keys created
- SearXNG: running, listening on 8080
- LND: needs bitcoin container named 'bitcoin-knots' on archy-net
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All containers now get CHOWN+FOWNER+SETUID+SETGID+DAC_OVERRIDE+NET_BIND_SERVICE
as the default cap set. Rootless podman needs these for:
- CHOWN/FOWNER/DAC_OVERRIDE: file ownership in mapped UID namespace
- SETUID/SETGID: internal user switching (entrypoint scripts)
- NET_BIND_SERVICE: port binding in network namespaces
Tested on .198: Grafana, Vaultwarden, Bitcoin Knots all start successfully.
Previously failed with "Permission denied" or "loopback adapter" errors.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bitcoin Knots failed to start with "failed to set loopback adapter up"
because cap-drop=ALL removed NET_BIND_SERVICE, which rootless podman
needs for network namespace setup.
- Add NET_BIND_SERVICE to Bitcoin/LND/Fedimint capabilities
- Add NET_BIND_SERVICE as default for ALL apps (rootless podman needs it)
- UID mapping fix from previous commit also included
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
create_data_dirs now chowns data directories to the correct mapped
UID for rootless podman (host_uid = 100000 + container_uid).
Previously only Grafana (UID 472) was handled. Now all containers
get the correct ownership:
- Bitcoin Knots: 100101 (container UID 101)
- Grafana: 100472 (UID 472)
- LND: 101000 (UID 1000)
- MariaDB: 100999 (UID 999)
- Postgres: 100070 (UID 70)
- All others: 100000 (UID 0, root)
Without this, containers fail with "Operation not permitted" on
chown during startup because rootless podman restricts UID operations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Secure flag on session cookies broke HTTP LAN access — browsers refuse
to send Secure cookies over plain HTTP, causing 401 redirect loop.
Fix: check X-Forwarded-Proto header. Only set Secure when request came
over HTTPS. HTTP on LAN works, HTTPS still gets Secure cookies.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add identity.create + server.echo to UNAUTHENTICATED_METHODS
- Clear web/dist before frontend build to prevent stale artifacts
- Add autocomplete attrs to login inputs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
FileBrowser crash fix:
- Add --cap-add=NET_BIND_SERVICE (port 80 needs it with --cap-drop=ALL)
- Add --cap-add=DAC_OVERRIDE for rootless volume access
- Both in first-boot script and backend config.rs
Test script fixes:
- Extract csrf_token cookie and send as X-CSRF-Token header on RPC calls
- Add --phase1-only flag for safe install-only checks (no side effects)
- Auto-test service uses --phase1-only so it doesn't steal onboarding
Install fixes:
- Pre-create ~/.local/share/containers (ReadWritePaths mount namespace error)
- Fix console-setup.service: add After=tmp.mount + ExecStartPre mkdir /tmp
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>