Plumbs the BlobStore from blobs.rs into ApiHandler. The HMAC capability
key is derived from the node's Ed25519 signing key via a domain-separated
SHA-256 — rotating the identity rotates every outstanding cap (intentional
so a replaced node cannot honour old tokens).
New routes (added to nginx config in both server blocks):
- POST /api/blob — session-authenticated raw upload, returns
{cid, size, mime, filename, self_test_url}. The self_test_url is a
pre-signed cap pointing at the local node so the UI can verify the
round-trip without needing a peer pubkey.
- GET /blob/<cid>?cap=<hex>&exp=<epoch>&peer=<pubkey> — peer-facing,
HMAC-verified in constant time, expiry-checked, then streams bytes.
Mesh.vue gets a minimal "Attachment test (blob store)" section: file
picker → upload → cid display → "Verify round-trip" and "Open in new
tab" buttons. This validates Phase 3a end-to-end before we layer the
ContentRef typed envelope variant on top.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Timeout now wraps stderr reader + wait (was only wrapping wait, so
hung pulls were never killed). 23.182.128.160:3000 is now primary
registry since git.tx1138.com is unreachable.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. registries.conf includes docker.io search + fallback 23.182.128.160
2. First-boot pull_with_fallback() tries primary then fallback registry
3. FileBrowser created with noauth config on persistent volume
4. Backend dynamic registries.json pre-created in ISO
5. Filebrowser password secret created for token flow
Fixes: apps stuck at 0% download, filebrowser not working, dynamic
catalog not loading on fresh installs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
App catalog served from Gitea repos (app-catalog) with 35 apps.
Nodes fetch catalog dynamically — new apps appear without frontend
rebuild. Test app added and removed to verify pipeline.
Gitea manifest updated with internal_port/nginx_proxy for iframe.
Updated catalog.json, nginx configs, app session configs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prevents duplicate resolver directive error when both
nginx-archipelago.conf and external-app-proxies.conf are loaded
at http context level.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ISO builder: run npm ci before npm run build to prevent stale UI artifacts
- Unbundled ISO: clean container-images dir to prevent bundled tars leaking
- WireGuard: use After=network.target instead of network-online.target for
faster wg0 startup on install
- VPN status: check actual nvpn0 interface instead of config tunnel_ip to
prevent NostrVPN from showing standalone WireGuard IP
- ContainerApps: filter out not-installed bundled apps (fixes Bitcoin Knots
appearing on clean unbundled installs)
- Kiosk: persist kiosk mode to localStorage before /kiosk redirect so
App.vue can skip remote relay (fixes input doubling with companion app)
- IndeedHub: fix port mapping and X-Forwarded-Prefix passthrough
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- IndeedHub container port changed from 7777 to 7778 (7777 used by nostr-relay)
- Nginx proxy updated to route to 7778
- Backend config.rs port mapping updated
- Podman registries.conf switched to v2 format (fixes mixed v1/v2 error)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All hardcoded references to the old IP-based registry replaced across
Rust backend, Vue frontend, shell scripts, Dockerfiles, CI, and docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Unbundled ISO: first-boot only creates FileBrowser (marker file .unbundled)
Users install apps from Marketplace — no more bitcoin/mempool on clean install
- VPN status: read tunnel IP from config file (instant) instead of nvpn status (22s)
- Kiosk: App.vue skips remote relay on /kiosk path (prevents duplicate input)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ISO boot failed in emergency mode because:
- fsck.ext4 binary missing (no e2fsprogs in rootfs)
- LUKS data volume never opened (no cryptsetup-initramfs in initramfs)
Both packages were in the installer debootstrap but not the target rootfs
Dockerfile. The initramfs regeneration at install time now includes LUKS
support since cryptsetup-initramfs is present.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update all references from Debian 12 (Bookworm) to Debian 13 (Trixie)
- Enable SystemCallArchitectures, RestrictAddressFamilies, RestrictRealtime
in archipelago.service (safe on systemd 256+ which respects NoNewPrivileges=no)
- Update GLIBC compatibility checks from 2.36 to 2.40
- ISO filename, build container, and docs updated throughout
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ISO build no longer copies netavark from build host (Debian 13/GLIBC 2.41)
which broke container networking on Debian 12 targets. Rootfs already
installs netavark from Debian 12 repos — just configure the backend.
Install RPC now adopts existing containers (from first-boot) instead of
erroring on duplicates. Container scanner extracts real versions from
image tags and detects available updates against pinned versions.
Frontend shows update button with version info when updates are available.
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>
polkit denies reboot/shutdown for non-root users without a local seat
(e.g. SSH sessions). Since archipelago has NOPASSWD sudo, add shell
aliases so reboot/shutdown/halt/poweroff transparently use sudo.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two issues on fresh ISO install:
1. nostr-vpn.service was enabled in rootfs but env file doesn't exist
until first-boot generates Nostr identity — crash-loop on boot.
Now only enabled by first-boot-containers.sh after identity exists.
2. LUKS encrypted partition mounts over /var/lib/archipelago/, hiding
the relay config.toml the Dockerfile put there. Now copies relay
config and creates nostr-relay/nostr-vpn dirs on the LUKS partition.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Dockerfile RUN steps execute under /bin/sh (dash on Debian), which
doesn't support brace expansion {a,b,c}. The nostr-relay directory
was never created, causing the config copy to fail (build #444).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Runner is Debian 13 (glibc 2.41), ISO rootfs is Debian 12/bookworm
(glibc 2.36). Dynamic binary crashes with GLIBC_2.41 not found.
Musl static build eliminates the dependency entirely.
Also set GRUB_DISTRIBUTOR="Archipelago" so installed system boot
menu says "Archipelago" not "Debian GNU/Linux".
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add nostr-rs-relay as native system service (port 7777) for VPN
signaling. Every node runs its own private relay from first boot.
Update nvpn binary from v0.3.4 to v0.3.7 (fixes mesh event
processing). Add WireGuard helper and address service for peer VPN.
First-boot script configures relay, nvpn identity, relay URLs
(direct + Tor onion), and syncs daemon config.
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>
- Revert CI to normal cargo build --release (musl was false positive)
- Add acpid + acpi-support-base to rootfs packages
- Add acpi=force to GRUB and ISOLINUX boot params (installer + installed)
- Fixes "Maybe missing ACPI. Shutdown not powering off" on some hardware
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove ReadWritePaths sandbox (causes namespace error when /run/nostr-vpn
doesn't exist after reboot — /run is tmpfs)
- Detect both 'active' and 'activating' states in VPN status check
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
nvpn binary writes to $HOME/.config/nvpn. Set HOME to data dir,
create runtime dirs in ExecStartPre, remove overly restrictive
ProtectSystem/ProtectHome that blocked the binary.
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>
- Add NostrVPN as a native systemd service (extracted from container)
- Add VPN status detection for nostr-vpn in backend vpn.rs
- ISO build extracts nvpn binary from container image
- First-boot auto-configures NostrVPN with node's Nostr identity
- Change Claude Auth from login iframe to API key input field
- Remove duplicate ChangePasswordSection from Settings.vue
- FIPS and Routstr remain as installable container apps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove MemoryDenyWriteExecute=yes from archipelago.service — ring
(rustls) and secp256k1 (bitcoin/nostr) crypto libraries need
executable memory mappings that this restriction blocks
- Add + prefix to ExecStartPre so mkdir/chown run as root
- Use $HOME/archy instead of /home/archipelago/archy in CI workflows
so builds work on both .228 and VPS CI runners
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>
- Add firmware-linux-nonfree to ISO (fixes missing Realtek NIC firmware)
- Pre-create nbxplorer/Main and btcpay/Main data directories
- Fix fedimint data dir permissions (chmod 775 for non-root container)
- GRUB GFX fallback: gfxpayload=keep + console fallback for incompatible hardware
- Kill stale Chromium before kiosk restart (prevents duplicate processes)
- Suppress Rust warnings: #[allow(dead_code)] on run_boot_reconciliation,
#[allow(unused_assignments)] on history_dirty
- Version bump to 1.3.3
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Claude proxy no longer crashes when ANTHROPIC_API_KEY is not set.
Instead serves a 401 with a helpful message telling users to configure
their API key in Settings. Fixes blank AIUI on fresh installs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Onboarding:
- Persist current step in localStorage — page refresh resumes where user was
- Router afterEach saves step; guard redirects to saved step, not always intro
- Show npub alongside DID on restore success screen
UI fixes:
- Clipboard polyfill for HTTP contexts (fixes Copy DID crash on non-HTTPS)
- AppCard installing overlay shows for pkg.state=installing (survives refresh)
- Hide uninstall button during installation
- Frontend version bumped to 1.3.2
App store:
- OnlyOffice fully removed from marketplace, curated apps, app config
- Replaced with CryptPad references throughout
- Remove OnlyOffice from ISO capture patterns
Container stability:
- UI containers (bitcoin-ui, lnd-ui, electrs-ui) pull from registry first
- Added --cap-add FOWNER for rootless Podman compatibility
- electrs-ui now included in first-boot loop alongside bitcoin-ui and lnd-ui
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ISO build:
- Remove stale archipelago-rootfs-tmp container before creating new one
(previous failed builds leave it behind, blocking subsequent builds)
Container ports:
- OnlyOffice: fix LAN address from 8044 to 9980 (actual mapped port)
- Nginx Proxy Manager: fix from 8181 to 81 (correct admin port)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The CI build server's /opt/archipelago/web-ui/aiui resolves to the
same path as the build workspace. cp -r fails with "same file" error
which aborts the build under set -e. Use rsync instead (handles
same-src/dest gracefully), with cp fallback + || true.
This was the root cause of CI build #373 failure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Frontend:
- Add remote-relay.ts: receives companion input via /ws/remote-relay,
dispatches keyboard/mouse/scroll events into browser DOM
- Add CompanionIndicator.vue: NES gamepad icon when companion connected
- Wire relay start/stop to auth state in App.vue
Kiosk:
- Move Chromium data dir to /var/lib/archipelago/chromium-kiosk (encrypted)
- Disable MetricsReporting, AutofillServerCommunication, PasswordManager
- Remove --metrics-recording-only (contradicts disable-metrics)
CSS:
- Fix Chromium ghost rectangles: only apply preserve-3d + backface-visibility
during transitions, not always-on (causes Chromium to skip painting
off-viewport cards)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sync MODEL_MAP from deploy script to ISO build's inline claude-api-proxy.
Maps short model names (claude-sonnet-4) to full API IDs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backport from .228 live server:
- AIUI: use SPA fallback (try_files → /aiui/index.html) for client-side routing
- Remove cookie_session gates from AIUI proxies (API key managed by proxy)
- Apply to both HTTP and HTTPS server blocks
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>
The unbundled build was generating a 73-line inline script that only
created FileBrowser. This meant no lnd.conf, no UI sidecars, no
--add-host DNS fix for any app. Now uses the full first-boot-containers.sh
which handles both bundled (load tarballs) and unbundled (pull from
registry) modes, and includes all fixes for LND config, nginx sidecars,
and DNS resolution.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The backend shuts down in <1s. The 660s timeout was left from when
Bitcoin Core was managed by this service. With 660s, systemctl stop
hangs for 11 minutes if the process is already dead but systemd
hasn't noticed, blocking all deploys and restarts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend:
- Add --add-host host.containers.internal:host-gateway to LND and Bitcoin
Knots containers (fixes DNS resolution failure in rootless podman)
- Add --user 0:0 and DAC_OVERRIDE to nginx UI sidecar containers
(fixes chown crash in rootless podman for bitcoin-ui, electrs-ui, lnd-ui)
- Add hostadd to Rust Podman API client for web UI container installs
- Add Chromium privacy flags to kiosk launcher (disable telemetry)
Frontend:
- Fix onboarding reset on raw IP visits (trust localStorage as first-class
signal, skip boot screen when server is up but not onboarded)
- Fix seed regression: persist challenge indices in sessionStorage so going
back from Verify doesn't change which words are asked
- Remove glass container from seed Generate/Verify/Restore screens
- Add Back button to Restore from Seed screen
- Replace Network card: Tor (purple), VPN status (orange), Bitcoin sync (orange)
- Add ElectrumX to curated app list with correct .webp icon
- Install flow: navigate to My Apps immediately with toast, hide
installed/installing apps from marketplace and discover views
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The UNBUNDLED build path didn't copy scripts/lib/ to the ISO,
so install-tui.sh was never available on unbundled installs.
The installer sourced it but the file wasn't there — no animations.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>