2098 Commits

Author SHA1 Message Date
archipelago
97358d2314 feat(kiosk): display-size presets in Settings (Auto/Large/Balanced/Native)
system.kiosk-display.get/set RPCs write /etc/archipelago/kiosk-display.conf
(sourced by the kiosk launcher) and try-restart the kiosk so the choice
applies immediately. The Settings section only appears on nodes that have
the kiosk unit installed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:27:04 -04:00
archipelago
1b78da1d7a feat(lnd): accept amount_sats on lnd.payinvoice for zero-amount invoices
Zero-amount BOLT11 invoices need the payer to supply the amount; forward
it to LND REST as the amt field. The wallet scan modal unlocks its amount
field for such invoices and passes the user's entry through.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:15:35 -04:00
archipelago
0213da3fc5 fix(kiosk): background art rendered black — never paint a background on <html>
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m44s
html.kiosk-safe-area set background:#000 on the root element. With a root
background, body's background stops propagating to the canvas and paints
as a normal block background instead — which by CSS paint order covers
negative-z-index descendants. The dashboard/login art lives in
.bg-perspective-container at z-index:-10, so every kiosk display showed
solid black behind the UI while desktop browsers (no root background)
showed the art. Verified live on the Framework 4K TV via CDP: making the
root transparent restores the art instantly. Keep the black on body only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:14:49 -04:00
archipelago
8968d41ca9 feat(wallet): QR scan modal — camera + animated QR, wired into Home/Send/Receive
New WalletScanModal scans QR codes with the device camera (BarcodeDetector
with jsQR fallback) including animated/multi-frame QRs via qrloop, then
routes what it saw: BOLT11 invoices -> lightning pay (amount locked from
the invoice when present), bitcoin:/BIP21 URIs -> on-chain send, Cashu
tokens -> redeem, Fedimint invites -> join. Entry points: camera button on
the wallet card (far right), and a Scan button centered between the
Close/Send and Close/Receive buttons of both modals.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 13:14:31 -04:00
archipelago
be5122048b fix(kiosk): correct --window-size to DIPs + panel-derived layout target
The kiosk window was sized in physical pixels, but Chromium interprets
--window-size in DIPs: with any device-scale > 1 the window painted
scale-times larger than the panel, and with no window manager in the
kiosk X session --kiosk/--start-fullscreen could not snap it back. On a
4K TV at scale 3 that meant an 11520x6480 window — 9x the panel area —
which blew the compositor tile-memory budget ("tile memory limits
exceeded, some content may not draw") and froze the screen on the last
painted frame; smaller scales cropped content off the right/bottom.

- Size the window in DIPs (panel / scale) so DIPs x scale = the panel.
- Panels >=2560 wide now target a 1920-wide layout (4K -> scale 2.0,
  validated on the Framework's 72" 4K TV: spacious desktop layout at 2x
  sharpness); smaller panels keep the 1280 target (1080p TV -> 1.5).
- Optional ARCHIPELAGO_KIOSK_MAX_WIDTH cap: drop to the best <=N-wide
  xrandr mode for weak hardware (TV upscales instead of us rasterizing).
- Source /etc/archipelago/kiosk-display.conf so the upcoming Settings
  display-resolution picker can manage these knobs per node.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 12:46:15 -04:00
archipelago
918b0275a9 feat(ui): MeshCore RF frequency-plan presets by country + Custom entry
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m47s
The RF section shipped as four bare fields; restore a prepopulated plan
list (dropdown) with Custom unlocking free entry, per operator direction.
Preset labels carry the full values, so the number fields render only in
Custom mode — no duplicated display. Presets limited to verifiable
sources: the repo's MeshCore community plans (EU 868/433), the MeshCore
firmware's own build defaults (platformio.ini arduino_base 869.618/62.5/
SF8 CR5; companion example 915.0/250/SF10/CR5), and the operator-attested
Portugal plan (869.618/62.5/SF8/CR8). Stored params are matched back to a
named preset on load; hand-editing flips the dropdown to Custom.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 08:36:59 -04:00
archipelago
a71a18bffd fix(pine): serve the Connect-to-WiFi button over an http->https redirect
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m47s
The launcher must be a secure context for Web Bluetooth, but the UI opens local
apps as http://host:port (resolveAppUrl), so an https-only launcher (v1.1.0)
would break the Open button with a TLS mismatch. Serve http on 10380 (the Open
target) that 301-redirects to the https listener on 10381, so the new tab lands
on https where navigator.bluetooth is available. Bump pine 1.1.0 -> 1.1.1;
re-sign the catalog (only the pine entry changes).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 08:32:52 -04:00
archipelago
24cab326e2 feat(pine): interactive "Connect Pine to WiFi" button + republish catalog
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m47s
Replace the launcher's text instructions with the real Improv-over-BLE
Web-Bluetooth provisioner (a "Connect Pine to WiFi" button, SSID/password
fields, live log) ported from the pine repo. Web Bluetooth needs a secure
context, so the launcher now serves HTTPS (self-signed): :80 (published 10380,
the Open target) 301-redirects to :10381, and open_in_new_tab keeps BLE out of
an iframe. Bump pine 1.0.0 -> 1.1.0.

Regenerate + re-sign releases/app-catalog.json (only the pine entry changes vs
v1.7.109) so nodes reconcile the button page live. Signed by the release-root
key did🔑z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 08:25:24 -04:00
archipelago
811ac1ce50 chore(ota): go live with the signed v1.7.109-alpha manifest
Supersedes the v1.7.108 hold now that the manifest is signed and the
release assets are being published.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 07:53:12 -04:00
archipelago
69b1a45872 chore(ota): hold the live manifest at v1.7.108 until v1.7.109 is signed and its assets exist
A prep commit accidentally carried the in-progress v1.7.109 manifest onto
main, so nodes saw an unsigned 1.7.109 with download URLs that 404. Restore
the signed v1.7.108 manifest until the v1.7.109 publish flow completes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 07:52:09 -04:00
archipelago
9823e76e8f chore: release v1.7.109-alpha v1.7.109-alpha 2026-07-21 07:50:10 -04:00
archipelago
8d4f6cb75e docs(release): third v1.7.109 bullet (Pine voice assistant) + version-bump prep
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m44s
The release gate requires ≥3 curated changelog bullets; the Pine app is
user-facing and deserved one anyway. Carries the version bumps and manifest
from the aborted cut so the tree is clean for the re-run.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 07:34:26 -04:00
archipelago
4bac839fb3 test(ui): did-wallet launch port follows its manifest (8088)
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m49s
The pine catalog regeneration (c1dfc667) refreshed generatedAppSessionConfig
from the manifests, where did-wallet publishes host port 8088 — the test's
8083 expectation was stale. The test asserts manifest-generated ports are
used, so it must track the manifest.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 06:59:47 -04:00
archipelago
ea80815c98 style: cargo fmt over the pine container list
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 06:55:19 -04:00
archipelago
4348581681 docs(release): curated v1.7.109-alpha changelog + What's New
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m47s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 06:54:05 -04:00
archipelago
999a100531 feat(mesh): RF-params settings UI + expose lora_radio_params in mesh.status
Some checks failed
Demo images / Build & push demo images (push) Has been cancelled
Device panel gets MeshCore RF fields (frequency MHz, bandwidth kHz, SF, CR)
wired to mesh.configure's validated lora_radio_params; shown only for
MeshCore radios, all-empty leaves the radio's flashed settings untouched.
Human units in the UI, firmware field units on the wire. Per operator
direction the panel's static stat rows duplicating editable fields (name,
region, channel) are dropped — the editable control is the display. Stale
'adjust via a MeshCore client' hints updated: the node programs the radio now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 06:53:10 -04:00
archipelago
a5810d4e51 release: publish signed app-catalog with the Pine voice-assistant app
Regenerate + sign releases/app-catalog.json (65 apps) with the pine,
pine-whisper and pine-piper manifests embedded, so nodes can install the Pine
stack straight from the signed registry (raw URL on main). Signed by the
release-root key did🔑z6MkkidEnEpo6qHMCNSZoNKWtvQvxq3whnaME9wGgEFhq7ur.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 06:29:48 -04:00
archipelago
4c3cd4b6ad feat(mesh): operator-configurable Meshcore LoRa PHY params (freq/bw/sf/cr)
Archy set no radio params on Meshcore devices — SF/CR/BW/freq lived only in
device firmware, so a radio on the wrong preset could not be fixed from the
node (it hears RF energy but demodulates nothing; the fleet hit exactly this
on an RNode: docs/RETICULUM-TRANSPORT-PROGRESS.md item 4).

- protocol.rs: build_set_radio_params — wire format verified against the
  MeshCore companion firmware handler (CMD_SET_RADIO_PARAMS=11:
  [11][freq:u32 LE MHz*1000][bw:u32 LE kHz*1000][sf][cr]); byte-layout test
  pinned to the Portugal preset 869.618 MHz / 62.5 kHz / SF8 / CR8.
- serial.rs: MeshcoreDevice::set_radio_params (device reboots on OK).
- MeshConfig.lora_radio_params: Option — None (default) leaves the radio
  untouched, so only explicitly-configured deployments are affected.
- session.rs: provision on connect, gated on a persisted last-applied marker
  (SELF_INFO offsets shift across firmware versions) + the same attempt cap
  as region/channel so a refusing radio never reboot-loops.
- mesh.configure RPC: lora_radio_params arm with firmware-range validation.

mesh tests: 114/114 pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-21 06:10:45 -04:00
archipelago
c1dfc6672c feat(pine): surface Pine in the app catalog + UI
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m48s
- catalog.json (app-catalog + neode-ui/public): one "pine" entry, home
  category; engines stay out of the catalog
- appsConfig.ts: pine → home category, pine-whisper/pine-piper → SERVICE_NAMES
  (render in Services, not as extra cards), pine-* icon fallback
- generatedAppSessionConfig.ts: regenerated (pine port 10380 + titles +
  open-in-new-tab), via scripts/generate-app-catalog.py

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 05:14:32 -04:00
archipelago
8791ef60f5 feat(pine): wire the Pine 3-container stack into the orchestrator
Register "pine" as a manifest-driven stack (like netbird) across every
lifecycle site so install/stop/start/restart/uninstall/crash-recovery treat
the launcher + two Wyoming engines as one app:

- stacks.rs: pine_stack_app_ids() + install_pine_stack() (orchestrator-first,
  adopt existing, else bail — no hardcoded fallback)
- install.rs: dispatch package_id == "pine" to the stack installer
- app_ops.rs: canonical stack-member table + owning_package STACKS
- dependencies.rs: startup_order + needs_archy_net
- config.rs: all_container_names
- crash_recovery.rs: auto-start members + a StackRecoverySpec (anchor
  pine-whisper) so a genuinely-installed stack self-heals but orphan debris
  does not crash-loop
- docker_packages.rs: exclude the engines from the apps list

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 05:14:32 -04:00
archipelago
1730d02003 feat(pine): add Pine voice-assistant app package (manifests + icon)
Package the PineVoice/Wyoming local voice assistant as ONE store app "Pine"
that runs three rootless containers:

- pine-whisper — Wyoming faster-whisper STT (docker.io/rhasspy/wyoming-whisper
  3.4.1, model base-int8/en), publishes :10300
- pine-piper   — Wyoming Piper TTS (2.2.2, voice en_GB-alba-medium),
  publishes :10200
- pine         — nginx launcher serving a self-contained setup/status page
  (WiFi provisioning + Home Assistant wiring instructions), the Open target

The two engines publish their Wyoming ports so Home Assistant reaches them via
host.containers.internal. All rootless (cap-drop=ALL, no_new_privileges),
manifest-driven — no hardcoded podman/installer. The engines are internal stack
members (added to the drift-check INTERNAL_MANIFEST_IDS allowlist); only "pine"
carries a catalog entry.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 05:14:32 -04:00
archipelago
146a3bc738 chore: release v1.7.108-alpha
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m48s
v1.7.108-alpha
2026-07-21 03:42:21 -04:00
archipelago
302f880d99 docs(release): fold everything into v1.7.108-alpha changelog + What's New
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m46s
Supersedes the unpublished v1.7.107. Covers the multi-anchor FIPS fix, WiFi
self-heal, mesh fast-rejoin, kiosk TV-scaling, kiosk dark theme / VT hints /
UTF-8 logo / power-button hardening, and the ISO build fixes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:59:23 -04:00
archipelago
217eedc093 fix(kiosk): correct VT hints, UTF-8 logo, ignore accidental power-off
Three fresh-install kiosk issues found on a Framework node:
- MOTD said 'Ctrl+Alt+F7 for kiosk' / 'F1 for terminal', but the kiosk's
  Xorg runs on vt1 — so F7 was an empty black VT (the reported black screen
  and the failed 'return to kiosk'). Corrected: kiosk=F1, terminal=F2.
- The UTF-8 block-glyph logo corrupted intermittently because returning from
  the kiosk can leave the console VT out of UTF-8 mode; force it with ESC%G.
- A short power-button press shut the node down instantly. Ignore the short
  press, keep long-press poweroff, so accidental brushes don't kill the node.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:48:14 -04:00
archipelago
d79edb3a5c fix(ui): censor 'Fuck IPs Mesh' -> 'F*ck IPs Mesh'
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m49s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:42:19 -04:00
archipelago
0f13545375 fix(fips): ship a second, reachable default anchor (once-and-for-all)
Nodes depended on a single public anchor (185.18.221.160) that is
unreachable from most real networks — confirmed on the thinkpad WiFi and a
fresh Framework node, both of which islanded (is_root=true, depth=0). Its
DNS even resolves IPv6-first while the daemon is IPv4-only.

Stand up an Archipelago-operated FIPS anchor on vps2 (146.59.87.168, the
OTA host every node already reaches) and ship it as an additional default
anchor. default_public_anchor() -> default_public_anchors() -> Vec; load()
returns the set; the apply loop already dials each, so whichever the node's
network can reach wins. Verified end-to-end: a fresh node joined the tree
via vps2 in 3s (depth 1). anchors tests: 7/7 pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:42:18 -04:00
archipelago
7eb4d6a7bf chore: release v1.7.107-alpha 2026-07-20 17:00:20 -04:00
archipelago
db008abdfd fix(kiosk): force dark color-scheme so bundled AIUI renders its dark theme
AIUI themes via @media (prefers-color-scheme) and falls back to its light
variant (white panels) when the browser reports no preference — which the
minimal kiosk X session does, so AIUI came up light instead of its designed
dark. The main UI hardcodes dark and is unaffected. Push two independent
dark signals on the kiosk Chromium: GTK_THEME=Adwaita:dark (version-
independent, can never force light) and --blink-settings=preferredColorScheme=0
(0 = kDark in modern Chromium). Needs on-device kiosk verification.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 16:57:46 -04:00
archipelago
35e9c62441 docs(release): curated v1.7.107-alpha changelog + What's New
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m49s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 16:39:48 -04:00
archipelago
cb301b1aef fix(wifi,fips): self-heal polkit rule + fast-retry anchors after daemon restart
Two fleet fixes that OTA can deliver (unlike the ISO-only polkit rule):

- WiFi (issue #99): nodes drive NetworkManager from a system-level service
  with no logind seat, so the stock polkit rule denies them and Wi-Fi setup
  fails with 'Insufficient privileges'. Fresh ISOs since 2026-05 ship the
  scoped rule, but OTA'd nodes never got it (OTA replaces binary + web UI,
  not host system config). Add a startup self-heal that installs the rule if
  missing and best-effort ensures polkitd — both wrapped so an offline/locked
  apt can never fail startup; the rule is written regardless so it activates
  once polkitd lands. Idempotent on the rule's unique subject.user marker.

- FIPS: regenerating fips.yaml (this build does, once, on first boot after
  the OTA) restarts the fips daemon; for a few seconds /run/fips/control.sock
  is gone and every seed-anchor dial fails, islanding the node until the next
  5-min tick. Detect that exact failure (all dials fail on control.sock) and
  retry in 15s instead — bounded to 8 fast retries/episode so a node with no
  fips daemon falls back to the steady cadence rather than busy-looping.

FIPS unit suite: 33/33 pass.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 16:39:02 -04:00
archipelago
6a848c38cc fix(iso): extract nostr-rs-relay from its real path /usr/src/app
The relay image was rebuilt to the standard nostr-rs-relay layout — WORKDIR
/usr/src/app, execs ./nostr-rs-relay — but the build still cp'd from the old
/usr/local/bin path, so extraction silently produced nothing. Once the
missing-binary guard was added, that silent miss became a hard build
failure. Verified the binary is a valid amd64 ELF (GLIBC 2.34, under Debian
13's 2.40) at /usr/src/app/nostr-rs-relay.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 16:15:59 -04:00
archipelago
830811372b fix(iso): stop requiring the removed NostrVPN binary
The ISO build hard-failed at 'Required binaries not extracted: nvpn'
because it still pulled 146.59.87.168:3000/lfg2025/nostr-vpn:v0.3.7 — an
image that was deleted from the registry when NostrVPN was removed from
the product. The native nvpn daemon is already masked here (ln -sf
/dev/null nostr-vpn.service) and is never spawned at runtime (core vpn.rs
manages WireGuard/Tailscale, only reading a legacy nvpn config as
fallback), so extracting its binary was dead weight that bricked every
build once the image went away.

Drop the nvpn extraction and remove it from the missing-binary guard.
nostr-rs-relay stays required — its nostr-relay unit is still enabled and
the image still pulls. Change is in STEP 3, so the cached rootfs is
unaffected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 16:05:34 -04:00
archipelago
38cb3dd252 chore: release v1.7.106-alpha
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m52s
v1.7.106-alpha
2026-07-20 15:36:00 -04:00
archipelago
50170b866e docs(release): curated v1.7.106-alpha changelog + What's New; pin ISO FIPS to v0.4.1
The ISO built FIPS from an unpinned --depth 1 clone of upstream main, so
every build shipped whatever happened to be on main that day. Pin to
v0.4.1 via a FIPS_VERSION build arg — the version fips/config.rs renders
its typed config against, and the one validated in the field on .198 and
the thinkpad. The pin lands inside the STEP 1 recipe-hash range, so the
cached rootfs correctly invalidates on the next build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 15:09:03 -04:00
archipelago
c91887a397 chore: commit Cargo.lock version bump left over from v1.7.105-alpha
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 15:05:31 -04:00
archipelago
4fb200e938 docs: handoff for the v1.7.106-alpha OTA + ISO release
Captures the three commits this release carries, the FIPS 0.4.1 upgrade
state (2 nodes done, fleet not rolled), the peer-files diagnosis including
what is explicitly NOT explained, the open decisions not taken, and the
exact release + ISO ritual.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 15:01:38 -04:00
archipelago
5fd0d6c3c8 refactor(fips): generate fips.yaml from typed structs; enable mDNS LAN discovery
The daemon config was built by format!-ing a YAML string literal. Upstream's
config structs are #[serde(deny_unknown_fields)], so a key we get wrong does
not degrade — the daemon refuses to start and the node drops off the mesh.
String-built config made that a runtime discovery on a live node.

Replace it with a typed struct tree serialised via serde_yaml, verified
field-by-field against jmcorgan/fips v0.4.1, plus an exact-output snapshot
test so schema drift fails in CI rather than at boot. Also adds tests for
determinism (server.rs compares the render against disk to detect drift, so
instability would cause a reinstall+restart loop) and for the mDNS key path.

Enables node.discovery.lan.enabled (mDNS/DNS-SD, added upstream in v0.4.0)
so co-located nodes peer directly instead of depending on the public anchor
being reachable — an anchor blackhole on one segment currently islands a node
completely. Emitted unconditionally rather than version-gated: v0.3.0's
DiscoveryConfig has no `lan` field and no deny_unknown_fields, so a v0.3.0
daemon ignores it harmlessly and it activates on upgrade with no second
config migration.

Note: the first startup after this lands renders a config that differs from
disk, so the existing drift check reinstalls it and restarts the daemon once.
That is the intended self-healing path and settles immediately.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 14:47:22 -04:00
archipelago
3ab7fb521a fix(rpc): log the full error chain, not just the outermost context
RPC failures logged only the top-level anyhow context, so a peer-files
failure produced exactly "RPC error on content.browse-peer: Failed to
connect to peer" with the real cause discarded. That made it impossible
to tell a dead peer from a slow Tor circuit from a FIPS resolve failure
without reproducing by hand.

Switch the log line to `{:#}` so the whole context chain is rendered.
The client-facing message still uses `{}` through sanitize_error_message,
so no internal detail is leaked to callers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 14:03:33 -04:00
archipelago
9e3ac9ba8f fix(ui): show the FIPS/Tor transport pill on mobile peer files
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m48s
The peer-files header title block is `hidden md:block` (the global header
carries the peer name on mobile), which also hid the transport pill nested
inside it — so mobile users browsing a peer's files had no indication of
whether they were on FIPS or Tor.

Render a `md:hidden` pill alongside the peer icon so the transport is
visible at every width, without duplicating the peer name on desktop.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 12:47:29 -04:00
archipelago
e2f83c0157 chore: release v1.7.105-alpha v1.7.105-alpha 2026-07-20 03:40:38 -04:00
archipelago
d5f709a3c3 docs(release): curated v1.7.105-alpha changelog + What's New block
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m43s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 03:38:23 -04:00
archipelago
710f576c77 fix(ui): satisfy noUncheckedIndexedAccess in the WG retry ladder
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m43s
vue-tsc in the release build (unlike the gate's type-check) rejects
indexing WG_RETRY_DELAYS_MS with a bare counter; hoist the delay into a
local and gate on undefined instead of the length check.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 02:52:43 -04:00
archipelago
c1d309f21f style: cargo fmt crash_recovery.rs
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 02:28:49 -04:00
archipelago
9c39243969 Merge remote-tracking branch 'gitea-ai/fix/companion-autologin-replay-intro'
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m49s
2026-07-20 01:57:19 -04:00
archipelago
f25febf3bb fix(vpn): refresh a stored peer's WireGuard endpoint to the node's current IP
vpn.peer-config returned the config exactly as stored at creation time, so
after a node moves networks the reused companion peer's QR encodes the old
location's address (seen on .116: Endpoint = 10.125.9.0 from the previous
LAN) and the tunnel can never connect. Rewrite the Endpoint line with the
node's current address before rendering the QR, and persist it back so the
downloadable .conf matches. Endpoint detection is shared with
vpn.create-peer via current_wg_endpoint_host().

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:57:08 -04:00
Dorian
0636d611e0 fix(ui): auto-retry the tunnel-QR provisioning while a first install settles
On a fresh node the wgqr step is often reached while the backend is still
settling (services starting, backend restarting during orchestration): a
single 'Failed to fetch' left a permanently blank QR. Retry network-class
failures on a 2s/4s/8s ladder while the step is still on screen, then fall
back to the friendly error + Try again button.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 06:45:34 +01:00
archipelago
f13fdc6451 fix(recovery): don't brick-loop startup on stale crash-snapshot containers
After an unclean shutdown, crash recovery walked the running-containers
snapshot and retried 'no such container' failures (2 attempts, 10s
backoff) for containers that no longer exist. Recovery runs before the
server binds :5678 and notifies systemd ready, so a stale snapshot
pushed startup past TimeoutStartSec=5min — systemd killed the daemon
mid-recovery, the next boot saw a crash again, and the node looped
forever (151 restarts on .116 after a hard poweroff).

- pre-filter the snapshot against 'podman ps -a' and skip vanished
  containers outright (fail-open if the query fails)
- never retry a 'no such container' failure
- extend the systemd start timeout ahead of each container start so a
  heavy node recovering dozens of real containers isn't killed while
  making progress

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:32:42 -04:00
archipelago
163bc3af01 fix(tor): stop listing/pre-baking hidden services for uninstalled apps (#79)
Backend: tor.list-services now hides services whose name maps to a
known-but-uninstalled catalog app (aliases covered: bitcoin/knots/core,
electrumx/electrs, btcpay, mempool). The node's own service, the relay,
and custom user services always show.

ISO: first-boot tor setup no longer pre-creates hidden services for six
apps that may never be installed — only the node's own onion is baked.
It also only seeds services.json/torrc on FIRST boot instead of
clobbering backend-managed services on every boot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:32:33 -04:00
Dorian
ae12ff2517 fix(iso): fail builds on missing VPN binaries, skip units cleanly, invalidate stale rootfs cache
v1.7.104 shipped ISOs whose units crash-looped (nostr-relay 3s loop,
archipelago-diag 203/EXEC) because build-time extraction failures were mere
warnings and the cached rootfs never tracked its recipe:

- hash the rootfs-defining region of the build script and rebuild when it
  changes (stale cache shipped ISOs without wpasupplicant/iw/rfkill)
- refuse to build without nvpn / nostr-rs-relay unless
  ALLOW_MISSING_VPN_BINARIES=1
- ConditionPathExists on nostr-relay/nostr-vpn/diag units so a stripped
  image skips them instead of crash-looping
- post-install test: every enabled archipelago*/nostr* unit must have an
  existing ExecStart payload

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 06:08:04 +01:00
Dorian
cf4a8eef0e fix(core): throttle volume-ownership sweep to first-seen + hourly per container
The sweep podman-execs into every running container; doing that on each 30s
reconcile tick was a permanent conmon 'Failed to create container' storm on
hosts where exec from the backend's cgroup context fails (Debian 13 first
boot). Ownership drift is an install/OTA-time event — sweep on first pass
after a container appears, then at most hourly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 06:07:56 +01:00