{"id":51161554,"url":"https://github.com/thatsfguy/agnostic-lora-net","last_synced_at":"2026-06-26T14:01:41.470Z","repository":{"id":367346047,"uuid":"1264123657","full_name":"thatSFguy/agnostic-lora-net","owner":"thatSFguy","description":"App-agnostic LoRa mesh backbone — per-direction link-quality routing, CSMA/CAD, end-to-end Reticulum transport, phones over BLE, images over LoRa, live web mesh map. nRF52840 + SX1262.","archived":false,"fork":false,"pushed_at":"2026-06-25T16:03:27.000Z","size":816,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-25T16:22:20.049Z","etag":null,"topics":["ble","lora","lxmf","mesh-network","nrf52840","off-grid","platformio","reticulum","sx1262"],"latest_commit_sha":null,"homepage":null,"language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thatSFguy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-09T15:25:39.000Z","updated_at":"2026-06-25T16:03:40.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/thatSFguy/agnostic-lora-net","commit_stats":null,"previous_names":["thatsfguy/agnostic-lora-net"],"tags_count":21,"template":false,"template_full_name":null,"purl":"pkg:github/thatSFguy/agnostic-lora-net","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thatSFguy%2Fagnostic-lora-net","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thatSFguy%2Fagnostic-lora-net/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thatSFguy%2Fagnostic-lora-net/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thatSFguy%2Fagnostic-lora-net/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thatSFguy","download_url":"https://codeload.github.com/thatSFguy/agnostic-lora-net/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thatSFguy%2Fagnostic-lora-net/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34819597,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-26T02:00:06.560Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ble","lora","lxmf","mesh-network","nrf52840","off-grid","platformio","reticulum","sx1262"],"created_at":"2026-06-26T14:01:40.003Z","updated_at":"2026-06-26T14:01:41.454Z","avatar_url":"https://github.com/thatSFguy.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# agnostic-LoRa-Net\n\n[![build](https://github.com/thatSFguy/agnostic-lora-net/actions/workflows/build.yml/badge.svg)](https://github.com/thatSFguy/agnostic-lora-net/actions/workflows/build.yml)\n[![release](https://img.shields.io/github/v/release/thatSFguy/agnostic-lora-net)](https://github.com/thatSFguy/agnostic-lora-net/releases/latest)\n[![status: alpha](https://img.shields.io/badge/status-alpha-orange)](#project-status--alpha)\nMIT licensed.\n\n\u003e ⚠️ **Alpha, and feature-frozen by intent.** This is a single-maintainer project\n\u003e built for **my own use**, developed in the open under the MIT license. Everything\n\u003e below is measured on real hardware, but APIs and the wire format can still change,\n\u003e and there is **no stability or support guarantee**.\n\u003e\n\u003e **Scope is intentionally closed.** The feature set covers what I need, and the design\n\u003e goal from here is **security and the smallest possible attack surface**, not new\n\u003e functionality. **I am not taking feature requests.** You are very welcome to **use it,\n\u003e fork it, and build on it** — and **new-board PRs are welcome _after you've tested them\n\u003e on the hardware yourself_** (see [Contributing](#contributing)).\n\nAn **app-agnostic LoRa mesh backbone** — a dumb, efficient transport that moves\naddressed packets between nodes the way the internet moves IP. Applications ride on\ntop as **opaque payload** (a Reticulum app, a phone over BLE, anything); the backbone\nitself is never programmed per-app.\n\nThe novelty (vs. Meshtastic/MeshCore/Reticulum): **link-quality-aware routing with\nindependent per-direction paths** — asymmetric/one-way links are *used*, not discarded.\n\n## Why not just Meshtastic / MeshCore?\n\nSame radios, same physics — different class of network. All numbers below are\n**measured on real hardware**, not projected:\n\n| | Meshtastic / MeshCore | **agnostic-LoRa-Net** |\n|---|---|---|\n| Encryption | AES channel keys; public-key DMs | **end-to-end Reticulum crypto** — opaque to the transport; the backbone never sees plaintext |\n| Delivery confirmation | optional, non-cryptographic ACK | **cryptographic delivery proof** from the recipient's key |\n| Large payloads | short messages; no file transfer | **images / files**: 14 KB delivered loss-free (SAR + 4-deep queue + transfer-complete ACK; ~210 B/s at SF7) |\n| Routing | managed flooding — every node rebroadcasts (tolerates asymmetric links, but no link-quality routing) | **link-quality distance-vector**, independent per-direction paths (q_rx / q_tx) |\n| Channel access | carrier-sense + randomized backoff | **CSMA via hardware CAD** (listen-before-talk) + jittered recovery timers — zero fragment loss across a 53-chunk transfer under load |\n| Reliability stack | flood + optional ACK/retry | per-hop ARQ **+** end-to-end NACK repair **+** dedup |\n| Addressing | device-derived node ids (+ public keys) | **self-certifying identity**: 16 B node id = `blake2b(node's Ed25519 pubkey)`, signed announces prove the id↔key binding; register/resolve directory, mobility-ready |\n| PHY management | app/config setting | **runtime-retunable** (freq/BW/SF/CR/power, staged+persisted) with a documented network retune safety protocol |\n| Overhead | configurable broadcast intervals | **beacon period auto-scales with SF** (~constant 0.3 % duty; configurable `net beacon`) |\n| Telemetry | built-in device telemetry; no per-neighbour link metrics | battery %, **per-neighbour RSSI/SNR + SNR margin**, neighbour tables — free in beacons + on-demand status; mapped live |\n| RF power | fixed / manual | **autonomous closed-loop optimiser** (Tier-1 controller): signed, mesh-wide, tunes each node against its weakest link; mobility-aware (raise fast / trim slow); self-heals via on-device auto-revert |\n| Management UI | phone app | **one zero-install control plane** (`agnctl` dashboard): live map + decision feed + node config + in-browser firmware flashing, all over Web Serial / Web Bluetooth |\n\nThe honest trade: our short texts are ~4× bigger on air than Meshtastic's\n(~200 B vs ~50 B) — that's the cost of end-to-end encryption and proofs, not of\nthe mesh. At equal SF, texts deliver in ~1 s and prove in ~2 s.\n\n## Status — proven on real hardware\n\nValidated end-to-end on 2× RAK4631 + a Seeed XIAO nRF52840 (Wio-SX1262), all SX1262:\n\n| Capability | Where |\n|---|---|\n| Non-blocking SX1262 transport (never blocks `loop()`) | `radio_hal.*` |\n| Neighbour discovery + **per-direction** link quality (q_rx / q_tx) | `lib/mesh` |\n| Distance-vector routing, announces piggybacked on beacons | `router.*`, `announce_codec.*` |\n| Directed link-layer unicast (negotiated 1-byte aliases) | `neighbor_table.*` |\n| **Multi-hop forwarding** (deliver / forward / drop) | `forwarder.*` |\n| Hop-by-hop **ARQ** (ACK + retry) over a non-blocking TX queue | `link_arq.*` |\n| **Reliable file transfer** (SAR fragment/reassembly + CRC + NACK) | `sar.*` |\n| Runtime **link blocking** (the Tier-1 \"block a bad link\" control hook) | `router.block()` |\n| **Reticulum** running over the mesh (announce + proven echo) | `reticulum/`, `scripts/rns_*` |\n| **BLE + LoRa coexistence (Req 1)** + phone-app ⇄ BLE ⇄ mesh ⇄ BLE ⇄ phone-app | `-DAGN_BLE`, `web/chat-demo.html` |\n| **Signed control plane** — Ed25519 POWER/CONFIRM/BLOCK/UNBLOCK, replay-countered, auto-revert rails | `lib/mesh/control.*`, `controller/internal/sign` |\n| **Autonomous RF optimiser** — mesh-wide, weakest-link, mobility-aware (Tier-1 controller) | `controller/internal/policy` |\n| **Consolidated web control plane** — live map · decision feed · node Configure · in-browser Flash | `controller/` (`agnctl`, served at `:8080`) |\n\nThe routing/codec/relay/alias/ARQ/SAR logic is **portable C++ in `lib/mesh`**, host\nunit-tested (**77 cases, `pio test -e native`**) and cross-compiled unchanged onto the\nnRF52. The Tier-1 controller is **Go** (`controller/`, stdlib-only), with its own host\ntests (`go test ./...`). Current firmware: **v0.14.0**.\n\n\u003e **BLE note:** the original plan assumed BLE+LoRa coexistence required forking\n\u003e MeshCore (because every hand-rolled attempt had failed). On this firmware it **works\n\u003e without the fork** — the failures were from *blocking* radio code, and ours is\n\u003e rigorously non-blocking. The MeshCore fork is no longer on the critical path.\n\n## Layout\n\n```\nplatformio.ini            envs: wiscore_rak4631 · xiao_nrf52 · promicro · tracker_t1000_e · heltec_v4 · compile_check · native\nboards/ · variants/       project-local board defs + vendored pin-map variants (RAK / XIAO / Pro Micro / T1000-E / Heltec V4)\ninclude/board_config.h    per-board SX1262/LR1110 wiring + network-wide PHY (906.625 MHz, BW250, SF9, 22 dBm, sync 0x4D)\ninclude/fs_compat.h       persistence shim: Adafruit LittleFS (nRF52) / core LittleFS (ESP32)\ninclude/packet.h          on-air frame format (link + network headers)\nradio_hal.*               non-blocking SX1262/LR1110 transport (default SPI, per-board TCXO/RXEN/FEM)\nlib/mesh/                 PORTABLE core (no Arduino — builds for nRF52 + host):\n    link_metric · neighbor_table · routing_table · router      link quality + per-direction DV\n    announce_codec · forwarder · link_arq · sar                wire codec · relay · ARQ · file transfer\nsrc/main.cpp              firmware: prober + routing + forwarding + ARQ + SAR + console + tunnel + BLE + signed control + mobile flag\nlib/mesh/control.*        Ed25519 signed-control codec (POWER/CONFIRM/BLOCK/UNBLOCK), shared with the controller\nlib/mesh/telemetry.*      battery + status query/reply codec (per-neighbour q/RSSI/SNR + mobile flag, fw 0.11.0)\ncontroller/               Tier-1 controller (Go, stdlib-only): `agnctl` — console ingest → live topology,\n                          airtime capture, signed control, autonomous power optimiser, served web dashboard\ntest/test_*/              host unit tests (Unity): mesh · codec · forward · alias · arq · sar · control · telemetry · …\ndocs/hardware-bringup.md  flashing + bring-up runbook\ndocs/tcp-bridge.md        app-integration guide: TCP bridge + tunnel protocol (distributable)\ncontroller/README.md      Tier-1 controller: flags, margins, step, cadence, key custody\nreticulum/interfaces/AgnosticLoraInterface.py   Reticulum custom interface (tunnels RNS over the mesh)\nscripts/                  host harnesses: sar_test · sar_multihop · tunnel_test · rns_echo · rns_demo\nweb/chat-demo.html        Web Bluetooth clear-text chat demo: phone/laptop ⇄ BLE ⇄ mesh\nweb/flash.html            Web Serial/BT commissioning hub: flash + provision \u0026 configure\n                          (controller key · BLE PIN · radio · battery · mobility · console)\nweb/map.html              live mesh map (Leaflet): nodes on real geography, per-direction link\n                          quality/asymmetry/SNR margin, battery badges, gateway console\ndocs/INTEGRATING-AGNOSTIC-LORA-NET.md  third-party integration guide\ndocs/remote-config.md     remote node config + the network-wide retune safety protocol (Tier-1)\ndocs/identity-vs-locator.md  design boundary: mesh routes on node-id locators, apps address on identity hashes\n```\n\n## Build \u0026 test\n\nPlatformIO (`nordicnrf52` + Adafruit nRF52 core + RadioLib 7.x; host `g++` for tests):\n\n```bash\npio test -e native               # 77 host unit tests for lib/mesh (no hardware)\npio run  -e wiscore_rak4631      # RAK4631 mesh firmware (BLE compiled in, off by default)\npio run  -e xiao_nrf52           # Seeed XIAO nRF52840 + Wio-SX1262 (SoftDevice s140 v7)\npio run  -e promicro             # Pro Micro nRF52840 + SX1262\npio run  -e tracker_t1000_e      # Seeed SenseCAP T1000-E (nRF52840 + Semtech LR1110)\npio run  -e xiao_esp32s3         # Seeed XIAO ESP32-S3 + Wio-SX1262\npio run  -e heltec_v4            # Heltec WiFi LoRa 32 V4 (ESP32-S3 + SX1262)\npio run  -e compile_check        # host compile-verify on a stock Feather nRF52840\n```\n\nMost boards carry an SX1262, so one firmware covers them; the **T1000-E** carries a\nSemtech **LR1110** instead (the radio HAL selects RadioLib's LR1110 class via\n`AGN_RADIO_LR1110`, the only chip-specific seam — RF-switch setup). Per-board pins, TCXO\nvoltage, RXEN/FEM RF-switch and power-enable live in `include/board_config.h` (values\nfrom MeshCore / Meshtastic). The radio sits on the default `SPI` remapped to the LoRa\npins (`SPI.setPins` on nRF52, `SPI.begin(sck,miso,mosi,ss)` on ESP32) with a\ncrystal-mode fallback — matching MeshCore's working RAK4631 init.\n\nThe **ESP32-S3 targets** (Heltec V4, XIAO ESP32-S3) have no SoftDevice (BLE is left out\nfor now — console over USB serial), persistence on the ESP32 LittleFS\n(`include/fs_compat.h`), and the node ID folded from the eFuse MAC. The Heltec's SX1262\nsits behind a GC1109/KCT8103L front-end module driven in lockstep with TX/RX; **that\nFEM/VEXT control is board-revision-specific — verify on first flash** (see the note in\n`include/board_config.h`). ESP32-S3 boards flash over USB serial — **in-browser from the\nweb hub via ESP Web Tools**, or with `esptool` / `pio run -e \u003cenv\u003e -t upload` — not the\nnRF52 UF2/DFU path.\n\n**Flashing + bring-up: [`docs/hardware-bringup.md`](docs/hardware-bringup.md).** Node\nIDs auto-derive from each chip's FICR (nRF52) or eFuse MAC (ESP32), so boards differ\nwithout configuration.\n\n## The stack\n\n- **Routing** — each node measures `q ∈ [0,1]` per direction from RSSI/SNR, runs\n  Babel-style distance-vector, and forwards toward `next_hop()`. Forward and return\n  paths are computed independently, so asymmetric links are first-class.\n- **Reliability** — every directed hop requests a 1-byte-sequenced ACK and retransmits\n  (`link_arq`); all TX paths share a non-blocking outbound queue so beacons, forwards,\n  ACKs and retransmits never collide mid-air.\n- **App transport (SAR)** — payloads larger than one frame are fragmented, reassembled\n  and CRC-verified; a missing-fragment NACK recovers end-to-end loss. Proven by\n  transferring a real image byte-perfect over 1 and 2 hops (`scripts/sar_*`).\n- **Runtime console** (USB serial / BLE): `send`/`block`/`unblock`/`info`/`sbegin`+`xfer`+\n  `dump`/`tunnel`/`rf`/`ble`. `block` and `rf` (live radio retune: freq/BW/SF/CR/power/\n  sync, staged + `rf apply`, persisted) are the local stand-ins for the Tier-1\n  controller's signed control commands — see [`docs/remote-config.md`](docs/remote-config.md).\n\n## Control plane — the `agnctl` Tier-1 controller\n\n`controller/` is the **Tier-1 controller** (Go, stdlib-only — builds offline; `crypto/ed25519`\nfor signing). It tethers to one node over USB, reads the console stream into a **live global\ntopology**, and — with a controller key — signs and pushes control commands into the mesh. It\nstays **optional**: kill it and the Tier-0 mesh keeps running, and every connectivity-reducing\ncommand has an on-device auto-revert, so a controller crash self-heals.\n\n```bash\ncd controller\ngo run ./cmd/agnctl -port /dev/ttyACM0 -optimize -apply -http :8080   # live: optimise + serve dashboard\ngo run ./cmd/agnctl -file testdata/session.log -optimize -http :8080  # replay a log, no hardware\n```\n\n- **Autonomous RF power optimisation (mesh-wide).** A node transmits at one power, so the binding\n  constraint is the **weakest outbound link it must keep** — the optimiser tunes each node against\n  that, not just its link to the gateway. The tethered gateway's own links carry measured SNR;\n  remote links arrive as routed **telemetry** (`status \u003cid\u003e`), which since **fw 0.11.0** carries\n  per-neighbour SNR/RSSI so remote links are measured too (older/quality-only links can be raised\n  but never trimmed — a quality estimate saturates). Every change is step-limited; a *decrease*\n  applies provisionally and the controller only **CONFIRM**s it after re-observing the node, else\n  the node's 60 s dead-man revert restores it.\n- **Mobile vs fixed nodes.** A node self-reports mobility (`mobile on|off`, persisted, surfaced in\n  telemetry). **Fixed** nodes get optimised straight down to a target margin; **mobile** nodes get\n  a higher **reserve band** and an **asymmetric** loop — raise fast when the link weakens, trim\n  slowly and only after the margin stays strong — a slow analogue of cellular closed-loop power\n  control suited to the mesh's ~15 s feedback. (🚗 = mobile, 📍 = fixed in the UI.)\n- **Signed control** (`lib/mesh/control` ↔ `controller/internal/sign`, byte-identical, gold-tested):\n  POWER / CONFIRM / BLOCK / UNBLOCK, Ed25519-signed with a monotonic replay counter; flooded to the\n  target and ACKed back through the mesh. ROUTE override and remote PHY retune are still TODO.\n- **Resilient + self-contained.** The serial link auto-reconnects across node reboots/USB\n  re-enumeration (the dashboard stays up through gateway blips). The controller also **serves the\n  firmware** it flashes (`/fw/`, default `-fwdir ../web/fw`) — so it works fully offline, no\n  external release fetch needed.\n\n**The dashboard** (`-http :8080`, one consolidated single-page app — `localhost` is a secure\ncontext, so Web Serial / Web Bluetooth work):\n\n- **Dashboard** — per-node power/margin/battery/**firmware**, decision feed, gateway console, fixed/mobile.\n- **Map** — per-direction link quality (colour + width + value labels), direction arrows, asymmetry\n  badges, the optimiser's decision under each node, neighbour count, battery, 🚗/📍 — click a node to\n  focus its links and see *why* the optimiser chose a power.\n- **Configure** — connect a locally-attached node over Web Serial/BLE: radio PHY (with retune\n  warning), battery calibration, BLE pairing/PIN, the mobile flag, raw console. (A port of\n  `web/manage.html` into the control plane.)\n- **Flash** — in-browser nRF52 serial DFU: pick a board, ① reboot to bootloader, ② select the\n  re-enumerated bootloader port \u0026 flash, with a UF2 fallback. (A port of the standalone hub.)\n\nSee [`controller/README.md`](controller/README.md) for flags (margins, step, cadence, key custody) and scope.\n\n## Reticulum over the mesh\n\nThe backbone is a transparent **Reticulum interface** — RNS packets ride as opaque\npayload; *our* routing carries them. (Not RNode emulation, which would bypass our\nrouting.) The node's `tunnel` mode turns USB serial into a binary HDLC pipe carrying\n`[node-id][payload]`; `reticulum/interfaces/AgnosticLoraInterface.py` plugs that into\nRNS. `scripts/rns_demo.py` runs two isolated RNS instances (no LAN path) bound to two\nRAKs and round-trips a cryptographically-proven echo **over the mesh**.\n\n**Building your own app (phone or otherwise)?** See\n[`docs/tcp-bridge.md`](docs/tcp-bridge.md) — a distributable guide to the TCP bridge and\nthe tunnel wire protocol, for both Reticulum apps and custom apps in any language.\n\n## BLE (Req 1)\n\nEvery board build includes a SoftDevice BLE Nordic UART Service alongside the mesh\n(`-DAGN_BLE` is on for all envs). It's **off by default** and the SoftDevice is enabled\n*lazily* on the first `ble on`, so a node that never uses BLE pays no runtime/power cost.\nBLE frames are tunnelled into the mesh and deliveries come back out over BLE, so the full\npath is **app → BLE → node → LoRa → node → BLE → app** — proven on hardware with two RAKs\n(`web/chat-demo.html`, a Web Bluetooth chat). The BLE links stay up through LoRa traffic; that's\nReq 1.\n\n**Pairing is PIN-secured.** The UART requires `SECMODE_ENC_WITH_MITM`, BLE is off by\ndefault, and a 6-digit pairing PIN is set per node. Management is **out-of-band over USB**\n(you can't configure BLE security over BLE itself) via `web/flash.html`'s Provision \u0026\nconfigure step (Web Serial): **Connect → Enable BLE → read the PIN → pair your phone with\nit.** Console equivalents:\n`ble on|off`, `blepin [random|\u003c6 digits\u003e]`. The PIN is shown **on demand only**\n(never in periodic output).\n\n## Standalone web apps (zero install, no controller needed)\n\nWhen a controller is running, flashing/config/map all live in the **`agnctl` dashboard**\n(above) — that's the primary path. The standalone single-file web apps remain for\n**offline / no-controller** use (e.g. bench-flashing before a controller exists). Web Serial /\nWeb Bluetooth need a secure context — `localhost` and HTTPS both qualify.\n\nThe hub is also published to **GitHub Pages** at\n\u003chttps://thatsfguy.github.io/agnostic-lora-net/\u003e (CI stages the latest firmware into `./fw/`\nsame-origin, so the in-browser flasher works straight from the hosted page). To run it\nlocally instead:\n\n```bash\nbash scripts/refresh_web_fw.sh     # build firmware for all boards into web/fw/\npython3 -m http.server 8000        # then open http://localhost:8000/web/\n```\n\n- **`web/index.html`** — landing page: a simple menu that routes to the apps below (served at\n  the GitHub Pages site root).\n- **`web/flash.html`** — commissioning hub: **flash** a board (nRF52 boards via in-browser serial\n  DFU — `web/nrf-dfu.js`, a byte-faithful port of adafruit-nrfutil, with UF2 fallback; ESP32-S3\n  boards — XIAO ESP32-S3, Heltec V4 — via **ESP Web Tools**), then **provision \u0026 configure** it on\n  one connected page — controller key, BLE PIN, node name, radio PHY (with retune warning), battery\n  calibration, mobility, and a raw console. (\"Skip — already flashed, just configure\" jumps straight\n  to that page for an existing node.) The firmware source defaults to the local `./fw/`; the GitHub\n  release URL is an optional fallback.\n- **`web/manage.html`** — the standalone node manager was folded into `flash.html`'s\n  Provision \u0026 configure step; this file now just redirects there.\n- **`web/map.html`** — gateway-centric mesh map (Leaflet, real geography): per-direction link\n  quality/asymmetry/SNR-margin, battery badges, gateway console.\n- **`web/chat-demo.html`** — Web Bluetooth **clear-text chat demo**: flash a couple of nodes,\n  connect a browser tab to each over BLE, and message across the mesh to confirm it works\n  (unencrypted — a bring-up/test tool, not a secure messenger).\n\n(`nrf-dfu.js` is vendored into `controller/internal/httpd/` so the dashboard's Flash tab serves\nit too — keep the two copies in sync.)\n\n## Bridges: putting other stacks on the mesh\n\nThe node's `tunnel` mode turns the console (USB or BLE) into a framed binary\npipe carrying `[typed address][opaque payload]` — that one contract is the\nintegration surface for everything:\n\n- **Serial (today, proven)** — `reticulum/interfaces/AgnosticLoraInterface.py`\n  binds a full RNS stack to a USB node; the mobile app does the same over BLE.\n  Wire contract: [`docs/tcp-bridge.md`](docs/tcp-bridge.md), worked examples:\n  [`docs/INTEGRATING-AGNOSTIC-LORA-NET.md`](docs/INTEGRATING-AGNOSTIC-LORA-NET.md).\n- **TCP bridge (shipped: [`scripts/tcp_bridge.py`](scripts/tcp_bridge.py))** —\n  expose a USB node on the LAN: multi-client, serial-reconnect, atomic frame\n  writes. Custom apps speak the §4.2 framing over the socket; Reticulum users\n  can also run Path A from the doc (an RNS instance bridging\n  `TCPServerInterface` ⇄ `AgnosticLoraInterface`).\n- **KISS TNC mode (shipped, fw ≥ 0.7.1)** — `kiss \u003cnode-id\u003e` (persisted) turns\n  the USB console into a standard KISS TNC: RNS's stock `KISSInterface` and the\n  wider packet-radio ecosystem work unmodified; the node stays manageable over\n  BLE. Honest caveat: KISS carries no destination, so a KISS node pins traffic\n  to its configured peer — point-to-point by construction, not a replacement\n  for the typed envelope.\n\n## Project status — Alpha\n\nThis is **alpha software**, built and maintained by one person in the open, **for that\nperson's own use**. What that means in practice:\n\n- **It runs, and the numbers are real** — the capabilities and measurements above are\n  validated on actual hardware, not aspirational. But coverage is bench-scale (a handful\n  of nodes), not field-hardened at fleet scale.\n- **The wire format isn't frozen.** The on-air frame format, console grammar, controller\n  protocol, and web/controller APIs can change between commits. There are no compatibility\n  promises across versions yet — expect to reflash the whole mesh together.\n- **The feature set _is_ closed.** This is feature-complete for what I need, and **I am not\n  taking feature requests** — feature issues will be closed. From here the work is\n  **hardening and attack-surface reduction**, not new functionality. See\n  [What's left](#whats-left) for the few security/robustness items still in flight.\n- **Board support is the one open lane.** New-board PRs are welcome — but only ones the\n  **submitter has tested on the real board** (see [Contributing](#contributing)).\n- **Free to use, no support guarantee.** Use it, fork it, run your own mesh. Bug reports\n  are welcome; there's no SLA, and best-effort is the promise.\n\n## Contributing\n\nThis project is **closed to feature requests and feature PRs** — it's feature-complete for\nmy use, and the design priority now is security and a minimal attack surface. The one place\ncontributions are genuinely welcome is **new board support**, plus bug fixes, security\nreports, and docs/test improvements that don't grow the feature set.\n\n**New-board PRs — the rule is: you must have tested it on the real board.**\n\n- **What a board PR adds:** a variant under `variants/` + a `board_config.h` block + a\n  `platformio.ini` env. The radio HAL is the only chip-specific seam — keep the change\n  confined to that seam and the board glue.\n- **You must have flashed and run it.** Because boards can't be validated blind (FEM/RF-switch,\n  TCXO, and TX-path details bite), a board PR has to come from someone who has the board in\n  hand and has exercised it. State explicitly in the PR **what you tested and what you\n  didn't** — at minimum: it boots, joins the mesh, and sends/receives over LoRa. The PR\n  template walks through this checklist. **Untested board PRs will not be merged.**\n- **Keep the core portable and unchanged.** The mesh logic in `lib/mesh/` builds for both the\n  host and the MCU with **no Arduino dependencies**. A board PR should not need to touch it;\n  if you think it does, open a bug first.\n- **Run the tests before you push:** `pio test -e native` (firmware core) and\n  `go test ./...` in `controller/` (Go control plane). CI runs the build matrix in\n  [`.github/workflows/build.yml`](.github/workflows/build.yml).\n\nBug reports and **security reports** ([SECURITY.md](SECURITY.md)) are always welcome. No\nformal CLA. Be kind; assume good faith.\n\n## What's left\n\nThe scope is closed, so this is **my own hardening/robustness backlog**, not a roadmap and\nnot a request queue. Security and attack-surface items come first; the feature-shaped\nentries below are explicitly **not planned** — they're noted for honesty and as fork\nterritory.\n\n- **Security / hardening (the actual priority):**\n  - **Controller-key rotation / re-key.** The controller key is the network's write\n    credential and currently can't be rotated — the highest-value hardening item.\n  - **Verify-rate limiting on the announce + control RX paths**, so a flood of bogus\n    signatures can't burn CPU/airtime (see [SECURITY.md](SECURITY.md) for the known surface).\n  - **Keep the attack surface from growing** — this is why the feature set is frozen.\n- **Energy** — nodes currently run continuous RX with the MCU spinning; the highest-value\n  power win is to **light-sleep the nRF52 between radio interrupts** (DIO1 already wakes it),\n  plus a deep-sleep role for leaf/tracker nodes. Robustness, not a feature — may happen.\n- **Not planned (fork territory):** signed ROUTE override, remote PHY retune over the signed\n  path, transfer boost, Reticulum/LXMF UX work (Sideband, an RNode-compatible BLE front-end).\n  These are real and interesting — they're just out of scope for what I maintain.\n- Polish: FCC dwell-time handling for the 906.625 MHz fixed channel. (Flash wear is\n  already a non-issue: writes are config-only — save-if-dirty — plus the signed-control\n  replay counter on each accepted command, so ~0 writes/day with no controller and ~12 on\n  a node the optimiser actively holds down, comfortably inside the nRF52840's endurance with\n  LittleFS wear-levelling. A held node could drop near zero by skipping the no-op heartbeat\n  re-assert persists, but it isn't worth the complexity yet.)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthatsfguy%2Fagnostic-lora-net","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthatsfguy%2Fagnostic-lora-net","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthatsfguy%2Fagnostic-lora-net/lists"}