The step-3 template's condition for showing the picker vs. the progress/
result view was `!flashJob?.active && flashJob?.stage !== 'done'`. That's
true for "no job started yet" (flashJob is null) — but ALSO true for a job
that just FAILED (active:false, stage:'failed'), since 'failed' !== 'done'.
So a failed flash silently reverted to the family/board picker instead of
showing the failure state (error message + log tail) — reported live as
"it went to the download screen and then back to the flash screen" with no
visible error.
Now shows the picker only when no job has been started yet (`!flashJob`);
once a job exists, the progress/result view is always shown, whether it's
still running, succeeded, or failed — the existing error/log-tail markup in
that view already handles all three, it just wasn't being reached.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The "couldn't confirm the board automatically" warning was checking the
display label text (/v3/i, /v4/i) instead of the same vid:pid table the
backend actually uses to resolve the board. A Heltec V3's CP2102 bridge
chip reports "CP2102 USB to UART Bridge Controller" in its USB strings, not
"Heltec", so meshDeviceImages.ts's fallback label ("LoRa radio (CP2102
serial)") never matched the regex — showing the low-confidence warning (and
leaving the board picker empty) even though the backend's
resolve_flash_board can safely auto-detect V3 via vid:pid 10c4:ea60.
Now checks the same vid:pid directly from detected_device_info, matching
mesh::flash::resolve_flash_board exactly. V4 still has no auto-match entry,
same as the backend — its vid:pid (303a:1001) is the ESP32-S3's generic
native-USB descriptor, not V4-specific, so manual selection is still
required there.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
AppLauncherOverlay sat at z-2400 under BaseModal's z-3000, so an app
opened from e.g. the Transactions modal loaded invisibly underneath it.
z-4000 lets the existing enter animation play over the modal; closing
the app returns to the modal you came from.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pruned nodes can't run the Mempool app, but tx links blindly opened it
anyway. Now: local app when running; otherwise an external explorer
(default tx1138.com) behind a one-time amber consent modal that spells
out what the other server's operator learns (tx of interest + IP) and
lets the user point at their own instance (placeholder mempool.guide).
Wallet Settings gains an On-chain tab (explorer URL + don't-warn toggle);
tabs renamed Cashu/Fedi so five fit in the row.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Backend streams LND /v1/transactions/subscribe (fires on mempool arrival
and each confirmation) and nudges the /ws/db revision per event; the Home
wallet card subscribes and refetches (debounced 800ms). An incoming
broadcast now appears while the 30s poll is still asleep. Reconnects
forever with capped backoff when LND is down/locked/absent.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
LND's RPC answers before its p2p server finishes loading, so connect/open
during that window failed red with the raw "server is still in the
process of starting". Now: quiet ~30s retry first; if still starting, a
calm amber "still finishing its startup" notice instead of an error.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Android/rust/archy-fips-core: leaf-only fips node as a JNI cdylib (fips
pinned to the fips-native fork rev with VpnService fd support), built by
gradle via cargo-ndk (arm64), tested on host
- ArchyVpnService: split-tunnel VpnService routing only fd00::/8 (MTU 1280,
foreground specialUse); FipsManager handles the one-time VPN consent and
silent auto-start — no settings surface at all
- pairing QR now fully configures the mesh: fnpub/fip/fhost/fudp/ftcp plus
fanchors (the node's seed-anchor list, npub@addr/transport) so the phone
can rendezvous through public anchors when the LAN endpoint is unreachable
- default seed anchors gain the two dual-transport join.fips.network test
anchors (23.182.128.74:443/tcp, 217.77.8.91:443/tcp); anchor adverts are
Nostr kind-37195 events
- device token rides the password field end-to-end: backend accepts tokens
wherever it accepts the password, so scan = instant login (WebSocket auth
+ WebView form injection unchanged); token logins skip TOTP
- ServerEntry.meshIp + IPv6-bracketed URLs; WebView retries the mesh address
on main-frame errors, auto-login and origin checks honor it
- companion v0.5.0 (versionCode 20), arm64 abiFilter; FipsNative.available
gates everything so non-arm64 still runs as a plain companion
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- auth.createDeviceToken / listDeviceTokens / revokeDeviceToken RPCs; only
SHA-256 hashes persist in data_dir/device-tokens.json
- auth.login accepts {token} (same rate limiter, skips TOTP like remember-me)
- pairing QR now carries name (server name, fallback "My Archipelago"),
tok (instant login), and FIPS mesh params from new fips.pair-info RPC
(npub, fips0 ULA, transport ports)
- companion onboarding drops the WireGuard install/tunnel screens — remote
access moves to the FIPS mesh embedded in the companion app
- fix: fips daemon UDP bind now 2121, matching the published container port
and fleet rosters (was upstream's 8668 — inbound UDP was dead on bridged
installs, mesh silently rode TCP 8443)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Plugging in any LoRa radio now surfaces the device setup modal EVERY time
(dismissals clear on unplug; works while mesh is enabled; 2-poll debounce
so ordinary reconnect blips don't flash it), showing what's currently on
the stick before anything is written:
- mesh.probe-device RPC: identifies the firmware read-only in the same
Reticulum->Meshcore->Meshtastic order as auto-detect. Meshtastic yields
region/modem-preset/channels (want_config is a read); MeshCore yields
name/node-id + firmware version via the previously-unused
CMD_DEVICE_QUERY; RNode via the bare KISS DETECT (no daemon spawn).
Path must be a detected candidate port; the live session's port refuses.
- "Keep As Is": manage_radio=false persists in MeshConfig and the session
skips every on-connect config write (region, channel, PHY params, advert
name) — the radio runs exactly as flashed, hot-swappable.
- "Set Up with Archipelago Settings": second screen shows the params that
will be applied (channel, region, the node's persisted RF params - e.g.
the validated Portugal preset - with the per-region community plan as
fallback) before writing anything.
- Stale-type fix: disconnect now resets device_type/firmware_version, so
a swapped stick no longer shows the previous radio's firmware; probed
kind pins device_kind so the right probe runs first on connect.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Home wallet card counted every incoming tx with num_confirmations < 3
in the pulsing 'Incoming N' badge. Lightning history hardcodes 1 conf and
ecash/fedimint/ark receives are normalized to 1 conf, so instant payments
qualified permanently — old receives showed as 'incoming' forever (seen
on .228 with week-old lightning orders).
- On-chain keeps confirmation-based logic (< 3 confs, expires naturally).
- Instant rails (lightning/cashu/fedimint/ark) now surface in the incoming
panel only for 5 minutes after receipt — the nice arrival moment without
the perpetual pending state.
- Panel rows: 'Unconfirmed / N conf' badge and mempool link are now
on-chain-only; instant rows get a rail badge instead.
- Home now polls wallet balances+transactions every 30s (like Web5.vue),
so pending on-chain receives actually flip to confirmed and the badge
appears/expires without a manual wallet action.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Regression: only X-Frame-Options apps were routed to the companion's
in-app WebView; iframeable apps fell through to the iframe session, which
is slower on the phone and loses the native back/forward/reload controls.
Now any launch inside the companion (ArchipelagoNative.openInApp bridge
present) goes to the WebView — both openSession and the legacy open()
overlay fallback. Mobile web/PWA keeps the iframe session. With regression
tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The info card above the action buttons now shrinks with the viewport and
scrolls internally (min-h-0 + overflow-y-auto in a max-h-full flex column),
keeping Cancel / Sign & Publish always visible. The hero disc and viz ring
scale down on narrow or short screens via a --seg-radius CSS variable so
the animation honors the smaller ring.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pine-openwakeword manifest (wyoming-openwakeword 2.1.0, :10400, /custom model
dir for the future Yo Archy model). Pine 1.3.0: launcher page gains a live
node-status card fed by /api/pine/status via a same-origin nginx proxy, and
copy for the new intents / Claude fallback / mesh announcements. Catalogs,
app-session config, drift ids regenerated; release catalog embeds 56
manifests (unsigned until the ceremony). Framework PT test plan in docs/.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Enabling Nostr discoverability now opens a signer overlay (same visual
language as the app NIP-07 identity picker) before anything is published:
a locked signer row fixed to the node's discovery key — deliberately not
pickable, personal identities are never used — plus the exact signed
content (DID, npub, software version, kind 30078/NIP-33 format) and a
Sign & Publish confirm. Disabling stays immediate. Also seeds the mock
with discovery OFF to match the production default, and defaults
dwnSyncLabel to 'Unknown' when the backend omits sync_status.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The mock previously returned a placeholder string with no nostr_npub, so the
new Federation signing-details panel showed 'loading…' forever against the
mock. Now returns a distinct discovery key — 64-char hex plus its real
NIP-19 bech32 encoding (verified against the official test vector) —
mirroring production where the node discovery key is separate from the
personal identity keys.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a 'Signing details' disclosure showing who signs the presence event —
a locked identity row (styled like the NostrIdentityPicker overlay) fixed to
the node's own discovery key, deliberately not pickable so personal
identities can never be used for node discovery — plus a human-readable
breakdown of the signed content: DID, signing npub, software version, and
the event format (kind 30078, NIP-33 replaceable, d-tag archipelago-node),
with a note that every event carries a NIP-01 Schnorr signature.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
On single-column (mobile) the dashboard cards now order My Apps, Wallet,
Cloud, Network; desktop keeps its existing two-column layout via
lg:order-none resets.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Wallet scan modal: fallback buttons no longer flash on open (spinner while
the camera auto-starts, video hidden until live — also kills the Android
WebView play-glyph), decode rate 10/s -> 4/s (preview visibly lagged on
phones), clear messages for BOLT12 offers and LNURL/lightning addresses
(unsupported by the LND backend; full LNURL-pay is a queued feature),
zero-amount invoices in BIP21 URIs prefill from amount=.
- Pine 1.2.0: launch-page copy reflects the auto-seeded HA wiring (only
speaker pairing stays manual), adds the ask-Archy hint and the
silent-answers power-cycle troubleshooting tip (firmware FIFO bug).
- bitcoin-core/knots: rpcuser/rpcpassword move from bitcoind argv (world-
readable in host ps) to a 0600 config file inside the container tmpfs;
the salted txrelay rpcauth hash stays on argv. Catalog regenerated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace the display-mode dropdown with three inline buttons (right panel /
over whole app / fullscreen) so switching iframe modes is a single click.
Active mode is highlighted orange. Mobile keeps its own bar untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The kiosk image ships no color-emoji font, so the wallet card's 🥜 and ⚓
drew as empty squares on TVs. Swap them for inline stroke SVGs matching
the neighbouring rows (wallet card + scan modal), and add
fonts-noto-color-emoji to the ISO package set so remaining emoji across
the UI (peer files, chat, content) render on future installs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
App.vue skipped starting the browser-side remote relay on kiosks, citing a
system-level xdotool injector that no longer exists — the backend's
remote-input path is relay-only (validate + broadcast). With no consumer,
companion input was silently dropped on TVs. Start the relay on kiosks
like everywhere else; rides the next release + ISO via the web bundle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two gaps kept the wallet QR scanner camera-less outside a desktop browser:
- Companion app: the WebView's default WebChromeClient silently denies
getUserMedia. Both WebViews (main + in-app overlay) now implement
onPermissionRequest — granting video capture only, requesting the
app-level CAMERA permission on first use (manifest already declares it).
- Plain-http origins (mobile web/PWA on a LAN node): browsers hide
navigator.mediaDevices entirely, no permission can bring it back. New
"Take photo of QR" fallback uses <input capture=environment> — the
native camera needs no secure context — and decodes the shot locally
with qr-scanner's scanImage. Live preview still used when available.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All Zeus channel surfaces now read: "Open a channel with Zeus Olympus
node and start sending and receiving Lightning payments. Minimum
150,000 · maximum 1,500,000 on-chain sats required."
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AIUI persists aiui-theme in localStorage and a stored value beats its
prefers-color-scheme fallback, so kiosk profiles from before the launcher
forced a dark color scheme keep white panels forever. Kiosk boot now pins
the same-origin key to dark, healing existing profiles on next load.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The onboarding-era :global(html.kiosk-mode .view-wrapper) transform:none
rule also matched the dashboard's route wrappers, freezing every list<->
detail depth transition into a jump cut on kiosk. Scope that rule to the
onboarding viewport and give the dashboard kiosk-safe 2D equivalents
(scale + opacity, no translateZ/blur/preserve-3d) that the software
compositor can animate.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>