fix(mesh): Reticulum garbage-text + reconnect churn + signal bars + node naming/HTTPS
- reticulum.rs: send_text_msg was lossy-UTF8-mangling binary CBOR control envelopes (ReadReceipt etc.) before sending as LXMF text; base64-encode with a marker instead, decoded losslessly on receive. - typed_messages.rs: mesh.send-read-receipt fired automatically on every chat view with no is_archy_peer gate, so viewing a message from a stock (non-archy) LXMF peer auto-sent it an undecodable control envelope, surfacing as garbage text right after whatever it just sent. Now a no-op for non-archy peers. - mesh/listener/mod.rs: RX_STALL_TIMEOUT was 300s and forced a full auto-detect reconnect on any otherwise-healthy but quiet mesh link (visible as "Connecting..." flapping); this also wiped Reticulum's in-memory peer-address table every cycle, breaking messaging with peers who hadn't re-announced in the window. Bumped to 1800s. - reticulum.rs: persist the peer prefix/dest-hash/display-name table to disk so a restart doesn't force every peer back to "Anonymous Peer" until they re-announce. - decode.rs/frames.rs: Meshcore was discarding the SNR its wire format carries; wire it onto the peer record. Mesh.vue's signalBars() now falls back to SNR-based bars when RSSI is unavailable (always true for Meshcore); Reticulum has neither and correctly stays at 0/"no data". - system/handlers.rs, dispatcher.rs: new system.get-hostname RPC + cert regeneration (with a proper SAN) whenever server.set-name changes the hostname, so HTTPS doesn't add a mismatch warning on top of the self-signed one after a rename. - AccountInfoSection.vue: surface the mDNS hostname + http/https links in Settings (HTTPS needed for mic/camera secure-context features) — never forced, both keep working. - build-auto-installer-iso.sh: ship avahi-daemon so .local names actually resolve on the LAN, and give the self-signed cert a real SAN instead of a bare CN, both at image-build and install-time-fallback. - Mesh.vue/MediaLightbox.vue/mesh-styles.css: mic/attach-stack no longer closes on a plain hover-past; mesh images open in the shared lightbox and have a real download button; lightbox close button moves to bottom-center on mobile instead of under the status bar; mesh device panel gets the same height/padding as its sibling tabs. Verified: 108/108 mesh unit tests, deployed + confirmed healthy on .116/.198/.228 (matching binary hash across all three), live Reticulum messaging confirmed working end-to-end post-deploy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
99cd82ab0a
commit
bebf3bae10
@@ -68,6 +68,22 @@ const copiedOnion = ref(false)
|
||||
const copiedDid = ref(false)
|
||||
let copiedTimer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
// mDNS hostname — HTTPS (even self-signed) is required for mic/camera access
|
||||
// (getUserMedia refuses plain HTTP outside localhost); surface both so users
|
||||
// know where to go for features that need it, without forcing HTTPS on anyone.
|
||||
const mdnsHostname = ref<string | null>(null)
|
||||
const httpsUrl = computed(() => (mdnsHostname.value ? `https://${mdnsHostname.value}` : null))
|
||||
const httpUrl = computed(() => (mdnsHostname.value ? `http://${mdnsHostname.value}` : null))
|
||||
const copiedHttps = ref(false)
|
||||
async function copyHttpsUrl() {
|
||||
if (!httpsUrl.value) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(httpsUrl.value)
|
||||
copiedHttps.value = true
|
||||
setTimeout(() => { copiedHttps.value = false }, 2000)
|
||||
} catch { /* unavailable */ }
|
||||
}
|
||||
|
||||
async function copyOnionAddress() {
|
||||
const addr = serverTorAddress.value
|
||||
if (!addr) return
|
||||
@@ -150,6 +166,13 @@ async function init() {
|
||||
} catch (e) {
|
||||
if (import.meta.env.DEV) console.warn('node.nostr-pubkey unavailable', e)
|
||||
}
|
||||
// mDNS hostname for the "Access this node" card.
|
||||
try {
|
||||
const res = await rpcClient.call<{ mdns_hostname?: string }>({ method: 'system.get-hostname' })
|
||||
if (res?.mdns_hostname) mdnsHostname.value = res.mdns_hostname
|
||||
} catch (e) {
|
||||
if (import.meta.env.DEV) console.warn('system.get-hostname unavailable', e)
|
||||
}
|
||||
}
|
||||
init()
|
||||
</script>
|
||||
@@ -215,6 +238,28 @@ init()
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Access This Node Card — local hostname + HTTP/HTTPS. HTTPS (even
|
||||
self-signed) is needed for mic/camera access on some features; never
|
||||
forced, just surfaced so people who need it know where to go. -->
|
||||
<div v-if="mdnsHostname" class="bg-black/20 rounded-xl px-5 py-4 border border-white/10">
|
||||
<div class="flex items-center gap-3 mb-2">
|
||||
<svg class="w-5 h-5 text-white/70" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5.636 18.364a9 9 0 010-12.728m12.728 0a9 9 0 010 12.728m-9.9-2.829a5 5 0 010-7.07m7.072 0a5 5 0 010 7.07M13 12a1 1 0 11-2 0 1 1 0 012 0z" />
|
||||
</svg>
|
||||
<p class="text-xs font-semibold text-white/60 uppercase tracking-wide">Access on this network</p>
|
||||
</div>
|
||||
<p class="text-lg font-semibold text-white/95 mb-1">{{ mdnsHostname }}</p>
|
||||
<div class="flex items-center gap-2 text-xs text-white/50">
|
||||
<a :href="httpUrl!" class="hover:text-white/80 transition-colors underline">http</a>
|
||||
<span>·</span>
|
||||
<a :href="httpsUrl!" class="hover:text-white/80 transition-colors underline">https</a>
|
||||
<span class="text-white/30">(needed for mic/camera features)</span>
|
||||
<button @click="copyHttpsUrl" class="ml-auto text-white/40 hover:text-white/70 transition-colors">
|
||||
{{ copiedHttps ? 'Copied!' : 'Copy HTTPS link' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Release Notes Modal -->
|
||||
<Teleport to="body">
|
||||
<Transition name="modal">
|
||||
|
||||
Reference in New Issue
Block a user