2229 Commits

Author SHA1 Message Date
6c48de20e3 Merge pull request 'docs(handoff): app direct ports are IPv4-only over the mesh — node-side task' (#113) from docs/mesh-app-ports-v2 into main 2026-07-23 22:56:40 +00:00
Dorian
e17de63016 docs(handoff): app direct ports are IPv4-only over the mesh — node-side task
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 23:55:43 +01:00
archipelago
6ba4540c53 Merge vc25 (PR #111) — handoff reconciled: vps2 daemon restart resolved the mesh path; private-tree withdrawn
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 18:42:15 -04:00
archipelago
5454bed038 docs(handoff): RESOLVED — vps2 daemon degradation was the mesh blocker; privatization withdrawn
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 18:40:51 -04:00
128b78d083 Merge pull request 'feat(companion): 60s mesh probe window + session pre-warm from the VPN service' (#111) from feat/mesh-probe-window-prewarm into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m47s
2026-07-23 22:36:45 +00:00
Dorian
75ecd1d3ea feat(companion): 60s mesh probe window + session pre-warm from the VPN service
Implements the app-side recommendations from the node diagnosis
(HANDOFF-2026-07-23): first session through the public tree takes 15s+
when it works at all, far beyond the old ~8s probe window.

- connect(): mesh ULA probed inside a 60s budget, 15s per-phase timeouts.
- ArchyVpnService: session warmer probes every saved node ULA as soon as
  the tunnel is up (5s cadence first minute, then 60s keep-warm) so the
  UI hits a warm session and it never idles out.
- Served APK: 0.5.5 (vc25); handoff updated with 25-ping evidence that
  public-tree discovery currently fails outright (supports the
  private-tree infra decision).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 23:36:34 +01:00
archipelago
82fcccd113 docs(handoff): node-side mesh diagnosis — nginx v6 fixed, real blocker is session path quality
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 18:25:18 -04:00
archipelago
1e89362e71 fix(nginx): IPv6 listeners — companion mesh HTTP to the node's ULA never connected
Phones on the FIPS mesh reach the node at http://[<fips0 ULA>], but every
shipped nginx config listened on IPv4 only — nothing answered [::]:80/443,
so mesh HTTP failed with the tunnel fully healthy (found live on
framework-pt during the vc24 5G session, 2026-07-23). Canonical conf gains
listen [::] lines and patch_nginx_conf self-heals deployed nodes (covers
the sites-enabled -> archipelago-http dev variant via the existing
canonicalize pass); validated by the existing nginx -t + rollback flow.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 17:57:58 -04:00
908e6185c8 Merge pull request 'fix(companion): TUN reader no longer dies on non-blocking fd + VPN not metered' (#110) from fix/tun-blocking-fd-metered into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m54s
2026-07-23 21:51:11 +00:00
Dorian
0064cfccac chore(companion): lockfile for libc dep
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:48:42 +01:00
Dorian
53037b2b71 fix(companion): TUN reader no longer dies on non-blocking fd + VPN not metered
On-device diagnosis (phone on adb, 5G): mesh sessions established but no
packet ever entered the tunnel — Android hands the VpnService TUN fd over
non-blocking, and the fips fork's dedicated blocking-read thread treats
EAGAIN as fatal, dying at startup. archy-fips-core now forces the fd into
blocking mode before Node::start_with_tun_fd. Verified on-device: reader
survives, packets flow into the mesh, and the 30s anchor-link flap is
gone (stable 8+ min on 5G).

Also setMetered(false): Android 10+ defaults VPNs to metered, flipping
the phone into data-restricted behaviour whenever the mesh is up.

Served APK: 0.5.4 (vc24). Handoff updated with the remaining node-side
blocker: phone->anchor works, phone->node ULA gets no reply — needs
fipsctl introspection on Framework PT (suspect fork/daemon session or
routing mismatch).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:47:26 +01:00
078f3b3619 pulled in main 2026-07-23 21:42:02 +00:00
4222a8507c Merge remote-tracking branch 'origin/main' into archy-hwconfig 2026-07-23 21:40:06 +00:00
db2cafc657 Merge pull request 'fix(companion): guarantee dual-path peering — LAN p2p + public anchor, always' (#109) from fix/guaranteed-anchor-peering into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m56s
2026-07-23 21:18:00 +00:00
Dorian
e49af6ff40 fix(companion): guarantee dual-path peering — LAN p2p + public anchor, always
The whole point of FIPS: the phone peers with the node's npub using the
LAN endpoint as a direct p2p dial hint AND rendezvouses via a public
anchor when away — neither LAN nor 5G location may matter. The anchor
half only existed when the scanned QR carried fanchors, so pairing
against an older node left the phone LAN-only and everything died on 5G.

The Archipelago vps2 anchor (npub + 146.59.87.168:8444/tcp, lockstep
with core fips/anchors.rs) is now baked into upsertNodePeer as a
guaranteed peer. Mesh connect retry window widened so a first-ever
pairing survives the VPN consent dialog. Served APK: 0.5.3 (vc23).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:17:24 +01:00
a865306488 Merge pull request 'fix(companion): mesh VPN no longer blocks IPv4 + off-LAN connect via mesh ULA' (#108) from fix/mesh-vpn-and-offlan-connect into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m54s
2026-07-23 21:00:39 +00:00
Dorian
979113c598 fix(companion): mesh VPN no longer blocks IPv4 + off-LAN connect via mesh ULA
Field test on 5G (Framework PT): pairing scanned fine but connect
hard-failed on the scanned LAN IP, and enabling the mesh VPN killed the
phone's internet.

- ArchyVpnService: the TUN is IPv6-only, and Android blocks every
  address family the VPN holds no address for — allowFamily(AF_INET)
  (+ allowBypass) so normal traffic flows while only fd00::/8 is routed.
- ServerConnectScreen.connect(): when the LAN address doesn't answer and
  the entry carries a mesh ULA, bring the tunnel up (autoStartIfReady)
  and probe the ULA with retries before reporting failure — the scanned
  IP is a dial hint, the npub/ULA is the identity (npub-first contract).
- Served companion APK refreshed as 0.5.2 (vc22); handoff doc updated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 22:00:15 +01:00
420f756947 Merge pull request 'chore(android): companion 0.5.1 (vc21) — refresh served APK + deploy handoff' (#107) from chore/companion-apk-0.5.1 into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m57s
2026-07-23 20:55:28 +00:00
Dorian
35849c88c6 chore(android): companion 0.5.1 (vc21) — refresh served APK + deploy handoff
The APK served from the pairing QR predated today's scanner fixes, so
the download-then-scan flow still hit the laggy scanner. Bumps the
version so phones update in place, refreshes the served artifact
(signed v1+v2+v3, verified), and adds the archi-dev-box deploy handoff.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:53:29 +01:00
08927b88a8 Merge pull request 'feat(companion): saved servers keyed by node npub — pairing contract item 1' (#106) from feat/npub-keyed-server-entries into main 2026-07-23 20:47:52 +00:00
archipelago
0b7a1b84e4 Merge remote-tracking branch 'gitea-ai/main'
All checks were successful
Demo images / Build & push demo images (push) Successful in 2m51s
2026-07-23 16:47:13 -04:00
Dorian
bb4166954a feat(companion): saved servers keyed by node npub — pairing contract item 1
Re-scanning a node after a LAN renumber updated the FIPS peer (npub-
matched) but duplicated the saved-server entry (address-matched).
ServerEntry now carries the node's fnpub as a trailing serialization
field (legacy entries still parse) and every saved/active-entry match
goes through sameNode: npub identity first, address/port/scheme only as
the LAN-only fallback. Edit forms that don't carry the npub keep the
stored one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:47:10 +01:00
archipelago
ed2c14c88a fix(companion): pairing QR scannable again — cap anchors at 2, EC level L, 768px source
Three npub-bearing anchors pushed the pairing URI to ~600 chars (QR ~v23 at
EC-M) — phone cameras couldn't lock onto it off a screen. The phone only
needs the node itself (LAN p2p) plus one public rendezvous (vps2 preferred),
and screen scans don't need EC-M's damage tolerance — L drops a full
version tier.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:46:51 -04:00
42e98d6a86 Merge pull request 'fix(ci): demo-images path filter watched .github, file lives in .gitea' (#105) from fix/demo-images-path-filter into main
All checks were successful
Demo images / Build & push demo images (push) Successful in 3m4s
2026-07-23 20:30:13 +00:00
3e5feebd49 fix(ci): demo-images path filter watched .github, file lives in .gitea
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 20:29:48 +00:00
archipelago
4199c43cc9 Merge gitea-ai/main — companion native-scan PR #104 (newer scanner rev wins in WalletScanModal)
Some checks failed
Demo images / Build & push demo images (push) Has been cancelled
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:25:02 -04:00
archipelago
54ddd043bd feat(tv-input): gamepad->keyboard bridge daemon + controller-navigable modals
- archipelago-gamepad-keys: evdev->uinput daemon (pure python3 stdlib) that
  mirrors any attached controller as a virtual keyboard — D-pad/stick to
  arrows, A/B to Enter/Escape, X/Y to Space/f, shoulders to Tab/Shift+Tab.
  The browser sees real keys, so controllers work inside EVERY app iframe
  (IndeeHub, Jellyfin…) with zero per-app code. Ships via the ISO splice and
  bootstrap self-heal (kiosk nodes only), hotplug via 5s rescans.
  Implements docs/tv-input-iframe-apps.md.
- useControllerNav: an open [role=dialog][aria-modal] owns navigation —
  focus is pulled inside, arrows move spatially between its controls, Enter
  activates, Escape stays with the modal's own close handling. One standard
  mapping for every modal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:17:11 -04:00
archipelago
402183c0ae feat(appsession): mode switches never reload the iframe + strictly per-app display modes + TV focus
- The session subtree now ALWAYS lives under <body>; inline panel mode is
  emulated with a fixed-position rect synced from the layout placeholder
  (ResizeObserver + resize), so toggling panel/overlay/fullscreen no longer
  re-parents — and therefore no longer reloads — the app iframe.
- Display modes are strictly per-app: saved per-app pick → app default
  (IndeeHub: fullscreen) → panel. The global fallback is gone, so one app's
  mode can never change how another opens.
- The iframe takes focus when the app loads, so keyboard / the gamepad
  bridge work inside it immediately (TV).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:15:43 -04:00
archipelago
80875a2ed4 feat(wallet): invoice-first lightning send — amount locks from the pasted invoice
A pasted fixed-amount invoice auto-fills and locks the amount ('set by
invoice' — nothing to type, just review and send); zero-amount invoices get
an explicit 'enter how many sats' hint. Plus a clipboard Paste button on the
destination field (hidden where clipboard read can't work).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:15:43 -04:00
archipelago
f72d4b92ac feat(content): seller-picked payment methods + music always in the bottom bar + video PiP
- Paid sharing: AccessControl::Paid gains an accepted-methods list
  (lightning/onchain/ecash/fedimint; empty = all, back-compat). Sellers pick
  methods in ShareModal, gated on what the node can actually receive (LND
  running/channel open, ecash wallet, fedimint joined) with an ⓘ that
  explains exactly how to enable a missing rail. Enforced server-side (the
  invoice/onchain mints refuse non-accepted methods; the serve gate only
  honors tokens/hashes for accepted rails) and the buyer's pay modal only
  offers what the seller accepts.
- Purchased music: the two remaining lightbox paths now use the bottom-bar
  player — the immediate post-ecash-purchase viewer and the Paid Files
  tab's window.open.
- Picture-in-picture buttons on the peer video player and the cloud media
  lightbox (utils/pip.ts; Chromium/Safari, no-op elsewhere).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 16:15:11 -04:00
5d2c28da7b Merge pull request 'Companion: three-finger menu gesture, native wallet QR scanner, scan/upload chooser' (#104) from feat/companion-3finger-native-scan into main
Some checks failed
Demo images / Build & push demo images (push) Failing after 33s
2026-07-23 20:01:08 +00:00
Dorian
fbed32f95d feat(wallet): hand live scanning to the companion's native camera when present
The scan/upload chooser (already on main) keeps the first move; when the
Android companion's window.ArchipelagoQr bridge exists, 'Scan with
camera' opens the native CameraX modal instead of getUserMedia — which
the companion's plain-http origin doesn't even have, so the button now
shows there too. Decodes come back via window.__archyQrResult, status
lines mirror out via setStatus, and stopScanning closes the native
modal once a code is accepted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 21:00:28 +01:00
archipelago
d5fc3d01a4 Merge companion native-scan branch — scan/upload choose pane supersedes today's interstitial
Another agent's feat/companion-3finger-native-scan: native wallet QR scanner
in the companion (WalletQrScannerModal.kt), three-finger menu gesture,
WebView file uploads, and the same every-open scan/upload chooser as a
dedicated pane with native-scanner handoff. Conflict in WalletScanModal.vue
resolved in the branch's favor — its choose-pane implementation replaces the
in-pane interstitial from 6502f13e (same UX, plus the native handoff).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:58:17 -04:00
Dorian
87c324e36c feat(companion): native wallet QR scanner, gesture hint overlay, WebView file uploads
- WalletQrScannerModal: native CameraX+zxing scanner styled like the web
  wallet modal, with an upload-image decode path; opened by the web UI
  through the new window.ArchipelagoQr JS bridge. Decodes stream back via
  window.__archyQrResult; the page mirrors status lines out and closes
  the modal when it accepts a code. Detection/spend logic stays in the
  web modal.
- onShowFileChooser: <input type=file> was silently ignored by the
  WebView — file pickers (incl. the wallet's upload option) now work.
- GestureHintOverlay: one-time animation teaching the three-finger hold,
  armed ~2 minutes after login (gesture_hint_seen flag).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 20:56:01 +01:00
Dorian
15954aec14 fix(companion): smooth camera preview + no flash on scanner open
ZXing TRY_HARDER (plus the inverted retry) ran on every camera frame,
pegging a core — that CPU contention is what made the preview stutter.
Decode attempts are now gated to ~7/s; KEEP_ONLY_LATEST drops the rest.
PreviewView switches to TextureView (COMPATIBLE): the SurfaceView
default punches a window hole that black-flashes inside Compose fades
and ignores rounded-corner clipping. CameraQrPreview is now shared with
the wallet scan modal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 20:56:01 +01:00
Dorian
1df075f7b1 feat(companion): menu gesture is now a three-finger hold
Two-finger hold collided with two-finger scrolling — any scroll longer
than 500ms popped the NESMenu. Three fingers hold for the menu; two
fingers scroll, on the trackpad, both controllers and the gamepad.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 20:56:01 +01:00
Dorian
ff4013bd3b feat(wallet): scan/upload chooser on every open + native scanner handoff
The scan modal now always lands on a chooser pane — Scan with camera /
Upload image / paste — instead of auto-starting the camera. When the
Android companion's ArchipelagoQr bridge is present, live scanning is
delegated to the native camera modal (WebView getUserMedia lags);
animated-QR progress and errors mirror onto it via setStatus.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 20:52:42 +01:00
Dorian
37c8992d21 feat(companion): native wallet QR scanner, gesture hint overlay, WebView file uploads
- WalletQrScannerModal: native CameraX+zxing scanner styled like the web
  wallet modal, with an upload-image decode path; opened by the web UI
  through the new window.ArchipelagoQr JS bridge. Decodes stream back via
  window.__archyQrResult; the page mirrors status lines out and closes
  the modal when it accepts a code. Detection/spend logic stays in the
  web modal.
- onShowFileChooser: <input type=file> was silently ignored by the
  WebView — file pickers (incl. the wallet's upload option) now work.
- GestureHintOverlay: one-time animation teaching the three-finger hold,
  armed ~2 minutes after login (gesture_hint_seen flag).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 20:52:42 +01:00
Dorian
1ecb5c9aa3 fix(companion): smooth camera preview + no flash on scanner open
ZXing TRY_HARDER (plus the inverted retry) ran on every camera frame,
pegging a core — that CPU contention is what made the preview stutter.
Decode attempts are now gated to ~7/s; KEEP_ONLY_LATEST drops the rest.
PreviewView switches to TextureView (COMPATIBLE): the SurfaceView
default punches a window hole that black-flashes inside Compose fades
and ignores rounded-corner clipping. CameraQrPreview is now shared with
the wallet scan modal.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 20:51:57 +01:00
Dorian
5bae8273a2 feat(companion): menu gesture is now a three-finger hold
Two-finger hold collided with two-finger scrolling — any scroll longer
than 500ms popped the NESMenu. Three fingers hold for the menu; two
fingers scroll, on the trackpad, both controllers and the gamepad.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 20:51:49 +01:00
archipelago
265196bc2c docs: TV input design — keyboard/gamepad inside iframe apps, globally
Some checks failed
Demo images / Build & push demo images (push) Failing after 36s
evdev->uinput gamepad-as-keyboard daemon on kiosk nodes (works in any
iframe, any origin) + shell-side focus handoff via useControllerNav;
CDP/per-app injection rejected. Implementation order included.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:31:03 -04:00
archipelago
9f1daa0f8c feat(ui): per-app display-mode defaults (IndeeHub fullscreen) + gamepad plays media + node list scales
- Per-app default display mode: explicit user pick (remembered per app) ->
  app default -> last global -> panel; IndeeHub defaults to fullscreen.
- data-controller-primary: a media file card's gamepad-select clicks the
  card (audio -> bottom-bar player, video -> lightbox) instead of the
  card's download link.
- Discovered-nodes list scales with the viewport (max(14rem,45vh)) instead
  of cramming a long list into a fixed 224px box.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:31:02 -04:00
archipelago
960e41e164 fix(content): audio always plays in the bottom-bar player + ecash purchase is explicit two-step
- Owned/purchased and cloud audio never opens a lightbox: PeerFiles'
  owned-content viewer and the Cloud/CloudFolder preview route audio to the
  global bottom-bar player (video keeps the lightbox).
- Web5 shared-content 'Buy' no longer fires the node-ecash payment on
  click: it opens a confirmation with balance -> price -> balance-after,
  and pays only on explicit confirm.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:31:01 -04:00
archipelago
6502f13e29 feat(wallet): paste-send confirmation step + every-time scan/upload chooser
- Pasting an invoice/address now gets the same second-step confirmation the
  scan flow has: method, destination, live balance -> amount -> balance
  after (invoice-fixed amounts win over the typed one), for every rail
  including token minting. Nothing pays until Confirm & Send.
- The scan modal opens on a chooser every time: scan with camera or
  upload/take a photo of the QR — the camera no longer auto-starts, and
  Back only relights it if camera was the user's choice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:30:42 -04:00
archipelago
c58f84c6e9 feat(companion): npub-first pairing — node self-anchor + guaranteed public anchor in the QR
The pairing QR was effectively IP-first: the phone dialed the scanned LAN
host and went dark when the LAN renumbered or it left home. FIPS peers on
npubs; IPs are only dial hints. fanchors now leads with the paired node
ITSELF (fnpub @ current host, npub-keyed so LAN contact is direct p2p over
FIPS and survives DHCP), and fips.pair-info always includes the Archipelago
vps2 public anchor (pairing hint only — the node's own anchor file is not
modified) so the phone can rendezvous through the public mesh when away.
Contract doc updated with the REQUIRED app-side changes (built on the Mac).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:30:40 -04:00
archipelago
e59ea1da69 fix(mesh): never show mojibake device names — strict decode with readable fallback
Name fields sit at firmware-version-dependent offsets; when an offset lands
in binary data, from_utf8_lossy handed the UI replacement-character soup in
the mesh modals and settings panel. decode_mesh_name(): strict UTF-8 to the
first NUL, no control chars, else a readable fallback (short pubkey /
node-<id>).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:30:09 -04:00
archipelago
be6f06a573 fix(kiosk): overlay scrollbars — no more fat X11 scrollbar on scrollable views
--enable-features=OverlayScrollbar gives the thin auto-hiding scrollbars a
remote browser shows (Peers view had a permanent classic scrollbar on the
TV). Rides the existing kiosk self-heal + ISO splice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:30:09 -04:00
archipelago
8a450bb8f8 feat(audio): HDMI audio works out of the box — pipewire in ISO + router daemon + ELD boot-race heal
Root cause of silent HDMI on kiosk TVs (framework-pt, LG TV):
1) fresh ISOs installed NO audio stack even though the kiosk launcher
   expects PipeWire-Pulse — add pipewire/pipewire-pulse/pipewire-alsa/
   wireplumber/alsa-utils to the rootfs and put the archipelago user in
   'audio' (linger user manager gets no logind seat ACLs on /dev/snd);
2) the kiosk's boot-time Xorg modeset races the i915→HDA audio-component
   bind, the ELD notify is lost, and every HDMI profile stays
   'available: no' forever. Verified live: one off/on modeset re-delivers
   the ELD and WirePlumber immediately switches to HDMI.

New archipelago-audio-router daemon (same splice-from-configs pattern as
the kiosk, ISO + include_str! self-heal): routes the card to the best
available HDMI profile, keeps the default sink on it, migrates live
streams on hot-plug, unmutes (HDMI forced 100% — the TV owns volume), and
re-modesets a connected external output once when no ELD reports a
monitor. bootstrap::ensure_audio_stack() installs packages + router on
already-deployed kiosk nodes via OTA (apt through systemd-run — the
service sandbox keeps /usr and dpkg read-only). main.rs also spawns the
pine satellite keeper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:28:47 -04:00
archipelago
852ffc5b18 feat(pine): wyoming satellite keeper — re-point speaker entries when DHCP strands them
HA stores a voice satellite as a pinned LAN IP and never re-resolves; when
the LAN renumbers (framework-pt: entry at 192.168.1.241, LAN now
192.168.63.0/24) the speaker silently drops forever. A periodic keeper
probes IP-pinned wyoming entries, sweeps the node's local /24s for the
satellite's port when one dies, rewrites core.config_entries (HA stopped
around the edit so its shutdown flush can't clobber it) and starts HA again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-23 15:28:07 -04:00
5799c37111 fix(mesh): DTR/RTS reset settle time was far shorter than real boot time
Every one of Reticulum/Meshcore/Meshtastic's open() deasserts DTR/RTS on
every connection attempt (needed to clear stale line state, but the
transition itself resets ESP32-S3 native-USB boards and CP2102/CH340-
bridged boards wired for Arduino-style auto-reset — acknowledged in the
existing code comments). Each only waited 300ms before expecting a
handshake response — nowhere near real firmware boot time (LoRa radio
init alone routinely takes longer).

A single auto-detect cycle tries multiple protocols in sequence
(Reticulum, then Meshcore, then Meshtastic), each with its own open() and
thus its own reset. With only 300ms of settle per attempt, a board could
plausibly never finish booting from one attempt's reset before the next
attempt's open() reset it again — a self-sustaining "never finishes
booting" loop that would look identical to firmware/hardware flakiness
from the logs, regardless of which firmware family was actually flashed.
Confirmed live 2026-07-23 on both a Heltec V3 (Meshtastic) and V4
(Meshcore): continuous device-side FROM_RADIO_REBOOTED / boot-loop
symptoms with zero config-write-triggered reboots (manage_radio was false
for part of the test), pointing at the connection layer itself rather
than firmware config provisioning.

Bumped the settle delay from 300ms to 2s in Meshcore's and Meshtastic's
open() — full protocol handshakes that need real boot time. Left
reticulum.rs's probe_rnode settle at 300ms deliberately: it's a
cheap/fast KISS-detect gate designed to fail quickly for non-RNode
firmware (documented elsewhere as "~1s"), not a full handshake, and each
subsequent protocol's own open()+settle is what actually needs to cover
real boot time regardless of what probe_rnode did moments before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-23 19:10:06 +00:00