- sats/BTC unit toggle on the on-chain amount field with live conversion
hint; canonical value stays sats end-to-end
- Fast / Standard / Slow fee presets (1 / 6 / 144 block targets) plus a
custom pane taking target blocks or an explicit sat/vB rate
- confirm pane shows LND's fee estimate for the chosen speed via the new
lnd.estimatefee RPC (GET /v1/transactions/fee)
- lnd.sendcoins now accepts target_conf / sat_per_vbyte with the same
mutual-exclusion + range validation as channel opens
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Consumes the lnd.closedchannels RPC and closing/force_closing statuses
that shipped backend-side in 00b7e179 but never reached the panel:
- tab bar with per-state counts; pending covers pending_open/closing/force_closing
- closed-channel cards with close type, settled balance, block height and
closing-tx explorer link
- closing/force_closing status dots + closing-tx link on in-flight closes
- Close button hidden for channels already mid-close
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- scripts/build-iso-release.sh: single gated command from signed release
to tested ISO (preflight version/signature parity, release gate harness,
strict catalog drift, full Rust suite, artifact version checks, build,
mount smoke, best-effort QEMU boot)
- scripts/iso-smoke-test.sh: standalone mount-level ISO verification incl.
stale-binary version assertion inside the payload
- .gitea/workflows/build-iso.yml: resurrected ISO CI as dispatch-only job
calling the gated orchestrator (old one was stranded in _archived/)
- tests/release/run.sh: catalog drift now runs --strict (was silently
always-pass)
- test-iso-qemu.sh: headless mode used -append without -kernel, which
QEMU rejects; use -display none so -serial file: capture works
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- lnd.closedchannels: closed-channel history via /v1/channels/closed
- channel list now includes waiting-close and force-closing pending
channels with their closing_txid
- closechannel reads the close stream's first update with a dedicated
client instead of hanging until the closing tx confirms; returns the
closing txid in display byte order
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The wallet card (Home + Web5 views) now leads with a Total Bitcoin row
summing all rails (on-chain + lightning + cashu + fedimint + ark), using
the orange ₿ mark. The on-chain row swaps ₿ for a link/chain icon.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pins fips to Zazawowow/fips-native fast-join-pinned (upstream pinned rev
46494a74 + one patch: pending/queued discovery lookups re-fire the
moment the tree parent lands instead of riding out timeouts started
before the mesh could answer). Measured: session lands 225ms after the
route appears; fresh-join route wait no longer stacks doomed lookups.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First lookups fire before the phone's tree position settles and are
doomed; at 10s completion timeout each one cost 10s before the 1s
retry could fire (observed 19s to a route on a fresh 5G join, session
3ms after the route — discovery convergence was the whole cost).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stock fips pacing is tuned for always-on routers: a failed discovery
backs off 30s, session resends gap out to 8-16s, dead links redial at
5s→300s. On a phone that stacked into a 40s+ first connect over
cellular. Config-only tuning in build_config (no fork changes):
discovery backoff 1s (cap 30s), session resends 400ms x1.5 (10 max),
link redial 1s (cap 30s). Measured on-device: anchor +1.2s, node
session +5.4s from cold start over 5G.
Ops note: vps2 anchor daemon had degraded again (multi-second link
RTTs after 15h uptime) — restarted, recycle tightened 1d → 6h.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A pairing QR minted while the node UI was browsed over the mesh carried
npub….fips as fhost. Android's system DNS can't resolve .fips, so every
direct handshake dial failed and first connect crawled through public-
anchor discovery (2m35s observed) instead of one LAN hop.
- node UI: resolveServerUrl treats .fips names and IPv6 ULA literals as
phone-unreachable and substitutes the node's LAN IP (same guard as
tailnet/localhost)
- app: QR parser falls back to the url param's host when fhost is .fips
- app: peer store never keeps .fips dial addresses
- app: peer aliases slugged for the fips host map (spaces dropped
"Framework PT"/"Mesh anchor" from name resolution entirely)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
send_token_at split the mint swap's results with
partition(send_denoms.contains(amount)) — membership, not count. When a
change denomination equaled a send denomination (guaranteed when
spending from a proof worth exactly 2x the amount: send [n], change
[n]), the change proofs were serialized INTO the token, and the
receiver redeemed double. Both the wallet send flow and peer-files
purchases mint through this path.
The split now consumes exactly one proof per required send denomination
(everything else returns to the wallet as change) and hard-fails if the
mint returns an incomplete denomination set, so a token can never again
be silently over- or under-packed.
Note: tokens minted BEFORE this fix already contain the extra proofs
and will still redeem at their inflated value.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A settled payment now takes over the modal: animated check with radiating
rings (emerald + one Archipelago-orange), the amount large, method label,
then copyable payment hash (lightning) / transaction ID (on-chain) and a
settling note, with Send-another / Done. Replaces the old alert-line at the
bottom of the form. Reduced-motion honored.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The gesture-nav strip below the in-app browser's control bar was painted
black by inset padding, but painted-only areas don't consume touches in
Compose — stray taps fell straight THROUGH the overlay into the kiosk's
tab bar behind it (accidentally opening the AIUI chat tab). The strip is
now an explicit Box: solid black AND click-swallowing. Bottom inset
moved off the Column padding so the shield can own that region.
Served APK: 0.5.11 (vc31).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Why apps feel instant over the mesh and data panels didn't: an app
launch is ONE streamed HTTP fetch on a warm session; the panels were
built from SERIALIZED RPC round-trips, each costing a full mesh RTT.
- Wallet card (Home): the 7 balance/history RPCs fired one-by-one
(seconds by construction) — now all 7 in parallel, and the card paints
a persisted last-known snapshot (balances + recent transactions)
BEFORE any network round-trip. Refresh replaces it silently.
- Web5 connected nodes: listPeers + federationListNodes fetched
together instead of stacked.
- Peer files: the cosmetic peer-name lookup no longer blocks the
catalog/owned fetches it never depended on.
Rules going forward: never serialize independent RPCs; never show a
spinner where last-known data exists.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Apps-tab launches awaited package.credentials with a 5s timeout before
opening anything (home-card launches skip the gate — that's why they
always felt instant). The lookup now races a 1.2s budget: past it, the
app launches with the static fallback config while the RPC finishes
into a per-app memo, making every subsequent launch instant.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- SendBitcoinModal crashed AT MOUNT on device ('Cannot access R before
initialization'): the invoice-first block's watch() evaluates its
computed source at setup, which read effectiveMethod while still in
its temporal dead zone (declared 20 lines later). Send button did
nothing and the failure poisoned sibling modal data. Block moved below
effectiveMethod, with a comment so it can't drift back.
- Cold start injected 0px safe-area vars (onPageFinished can beat window
attachment; rootWindowInsets null) — UI lost top/bottom margins and
the tab bar sat in the gesture zone eating taps. Injection extracted
to injectSafeAreaVars(), re-fired from an OnApplyWindowInsetsListener
when real insets arrive, on page finish, and on reattach.
- MeshLoadingScreen wears the proper brand: pixel 'a' in the black
circle-container + F*CK IPS MESH wordmark.
Served APK: 0.5.10 (vc30). Reproduced+diagnosed live on-device.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Everything from tonight's remote field testing:
- Back-to-dashboard fixed: JS bridges on the retained WebView delegated
through live-composition callbacks (stale closures made apps refuse to
launch after remote ⇄ dashboard), and reattach forces a layout pass
(top/bottom UI was wrong until a tap).
- F*CK IPs MESH branded loader: full-screen on relaunch (startup race)
and during the first connect after scanning a node QR.
- Party 'Share this app' is now a QR of the public vps2 download link
(scan with any camera → install over any internet); direct APK
file-share kept as a secondary action.
- Mesh party pairing is MUTUAL: scanner announces itself to the scanned
phone's Flare /hello — both sides get the peer, a chat entry and a
'👋 joined the party' message; scanner auto-opens the chat.
- Party scanner asks for CAMERA permission (fresh installs/reinstalls
landed on a black preview).
- Node: device tokens mint unique companion-<id> names — pairing a
second phone no longer revokes the first phone's login (the source of
'reconnecting a lot' and the second phone's failure).
Served APK: 0.5.9 (vc29).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The WEB_VIEW route recreated the WebView on every return from the
remote screen — full reload + re-login every time. The kiosk WebView is
now retained in a holder and reattached live (no race, no reload);
clients/bridges/listeners are re-bound to the new composition on
reattach, and the instance is dropped on retry, disconnect, or server
change so errored/stale sessions still reload for real.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Party screen 'Share this app': shares this install's own APK through
the system sheet (FileProvider over cache/share/), so a nearby friend
gets the companion via Quick Share/Bluetooth with zero internet — the
party-mode premise end to end.
- Intro screen: Mesh Party button under Get Started — party works with
no node at all, so it belongs on the first screen a fresh install
shows.
- Served APK: 0.5.8 (vc28), ready for the QR/OTA/ISO cut.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Combined session state from both agents (shared checkout):
- Mesh Party / Flare (experimental): phone-to-phone FIPS via party QR,
Flare chat/beam on :5680, optional fixed UDP listen (2121); party-only
phones get the baked-in Archipelago anchor so two bare phones reach
each other across the internet, not just on a hotspot.
- Keep-warm mesh: a running healthy node survives app opens (restart
only when dead or peers changed — peersDirty); requestMeshRestart for
config changes. Cold start measured on-device: session in ~3.1s.
- Restored after regression: startup LAN-vs-mesh race (the party work
was based on a pre-#114 file copy) and same-node routing (BTCPay/
Pine/HA had returned to opening in the external browser).
- UI: top status-bar scrim removed (bottom-only bars); web app-session
mobile bar is solid black — app theme colour no longer bleeds through
the bar and its safe-area strip.
Served APK: 0.5.7 (vc27).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Two field reports from tonight's 5G session:
- 'Stuck connecting': the kiosk WebView always loaded the LAN URL first,
and Chromium burns a minute+ of connect retries on a dead LAN IP before
the mesh fallback fires. WebViewScreen now races a raw TCP probe — LAN
gets 2.5s, else the mesh ULA (patient, 12s) — BEFORE creating the
WebView, so startup lands on the right origin in seconds either side.
Retry re-races instead of blindly reloading the LAN URL.
- Pine/Home Assistant/BTCPay opened in the external browser over the
mesh: same-node detection compared one host, but the node has two (LAN
origin + mesh ULA) — kiosk loaded via the ULA meant app links failed
isSameHost and routeOutbound bounced them to the browser. Same-node now
matches either address, in the kiosk router, SSL allowances and the
InAppBrowser.
Served APK: 0.5.6 (vc26). Note: the binary also carries the in-flight
listen_port groundwork from the dev-box session's archy-fips-core WIP
(compiles clean, default posture unchanged); its source lands separately.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The companion reaches the node at its fips0 ULA, and the web UI builds app
links as http://[<ULA>]:<direct port> — but rootless-podman published ports
bind 0.0.0.0 only, so every app URL was refused over the mesh (:8334 first).
A reconcile loop in the backend mirrors public IPv4 listeners: any port
>=1024 on 0.0.0.0 without an IPv6 any-listener gets a v6-ONLY [::] forwarder
to 127.0.0.1, following /proc/net/tcp* so install/remove and hardcoded
companion ports are covered without touching a single container. Purely
additive: IPv4/LAN/Tor access paths are untouched, v6only listeners cannot
collide with or intercept v4 traffic, and foreign IPv6 listeners win.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>