New map for Neanderfunk, still beta: https://neander.map.freifunk.space/.
Below, what differs in meshviewer, yanic and unifi_respondd from upstream and what runs around it.
Each point a pinned commit, plus the status upstream.
The forks are merged to upstream status, local changes remain one commit per feature (git log --no-merges --first-parent).
meshviewer
- Fork: Neanderfunk/meshviewer, Branch
neanderfunk, AGPL-3.0 like the original. Pinned: GitHub - Neanderfunk/meshviewer at f34bd2398b3a5850473a3a8a5e2f1d5a83d220bb · GitHub - Base: freifunk/meshviewer
df76ed7(26.09.2026), current status, including external GeoJSON via URL (#476). - One commit per feature, explained in
NEANDERFUNK.mdin the fork. All new configuration keys are optional; without them the branch behaves like the original.
Features
prometheus-directasdatasourceType: Charts in the node window directly fromquery_rangeof a Prometheus API, without Grafana in between. For us VictoriaMetrics. Diagramme im Knotenfenster direkt aus einer Prometheus-API · Neanderfunk/meshviewer@ec4db83 · GitHubchartRanges: a time period bar across all charts of a node (24 h, 7 days, 30 days, 1 year), the selection persists when switching nodes. Zeitraum der Diagramme im Knotenfenster umschaltbar · Neanderfunk/meshviewer@1aacff9 · GitHubairtime: one line per radio band „Airtime Channel N“ behind system load, bars like in HopGlass (green rx, orange tx, blue rest of occupancy), tooltip with the three shares. No line without data. Airtime je Funkband im Knotenfenster · Neanderfunk/meshviewer@7f059f5 · GitHubnodeValues: table rows in the node window with values from time series. For us no longer used as of 27.09. Wertezeilen im Knotenfenster aus einer Prometheus-API · Neanderfunk/meshviewer@2b498d5 · GitHub- WMS layers: a layer with
layersis set up as WMS, for us for official aerial imagery NRW. WMS-Ebenen als Grundkarte · Neanderfunk/meshviewer@538c986 · GitHub - Label per theme:
map.labelColor,labelShadowColor,labelColorDark,labelShadowColorDark,labelOffset. In dark theme, light text on a non-inverted, light base map was unreadable. Knotenbeschriftung: Saumfarbe, Schriftfarbe und Abstand einstellbar, … · Neanderfunk/meshviewer@6cbff98 · GitHub - Coordinates in one line: the coordinate picker additionally offers „latitude, longitude“ to copy, for example for SNMP Location in the UniFi Controller. Koordinaten in einer Zeile · Neanderfunk/meshviewer@74911e1 · GitHub
statisticsLinks: links at the top of the Statistics tab, for us to the overview in Grafana. Links oben in der Statistik (statisticsLinks) · Neanderfunk/meshviewer@cb3fd16 · GitHubserviceLink: small gear icon next to the node name,hrefwith{NODE_ID}. Zahnrad zum Service-Menue im Knotenfenster (serviceLink) · Neanderfunk/meshviewer@ebd56c0 · GitHubpauseHiddenAfterMinutes: a tab that is invisible for that long no longer loadsmeshviewer.jsonevery minute, when returning immediately once. For us 20 min. Hintergrund-Tabs laden nach einer Weile nicht mehr neu · Neanderfunk/meshviewer@55f81ea · GitHubipCopyButton: copy button after each IP address. Kopierknopf hinter jeder IP-Adresse · Neanderfunk/meshviewer@df03d16 · GitHub
Bugfixes
- Service Worker delivers the map from foreign paths. A link to Grafana on the same domain ended up in the loading screen.
navigateFallbackanswers every path without file extension withindex.html; upstream has taken paths with extension out since #453 (merged 07.09.2026), without extension still not. Fix:navigateFallbackDenylistextended to our paths, hardcoded, so only for us. Service Worker liefert unter /grafana/ und /nf/ nicht die Karte aus · Neanderfunk/meshviewer@63830a1 · GitHub - Axes with half clients. At 0 to 2 clients, d3 set marks at 0.5 and 1.5, without decimal places twice 0 and twice 1. Fix:
integer: trueper chart. Ganzzahlige Achsen fuer Groessen, die es nur ganzzahlig gibt · Neanderfunk/meshviewer@8e07d9b · GitHub - Start view behind the sidebar.
fitBoundscalculated with the entire map field, the western edge of the nodes lay under the sidebar. Fix:paddingTopLeftby the width of the open sidebar, as long as it stays under half the map width, so not on mobile. Startausschnitt spart die Seitenleiste aus · Neanderfunk/meshviewer@7b1dedc · GitHub
Upstream status (freifunk/meshviewer, 28.09.2026)
- Airtime: #28 „integrate features from hopglass“, closed 09.09.2023, the point „showing Radio Channel (and Airtime) on map“ in it NOT checked.
- Charts: #388 „[FEATURE] add client side rendered charts“, merged 16.06.2026, that’s the Grafana way, on which
prometheus-directbuilds. Open: #402 (link to Grafana), #403 (time format for short periods), PR #417 (InfluxDBALIAS BYfor multiple series). - Dark theme: #423
invertInDarkMode, merged 07.08.2026. Nothing on label colors per theme. - Related, not the same: #82 (link to node subdomain), open since 24.12.2023.
- No issue or PR found for WMS, coordinate line, statistics links, gear icon, pause in background, copy button, and start view.
Limits
- Charts, airtime and value rows require a Prometheus API that the browser can reach. For us the same host under
/nf/prom/, read-only. - Airtime knows only the bands that yanic knows: 11g (2.4 GHz) and 11a (5 GHz). 6 GHz is missing (yet).
- Tested only on our instance, with Firefox and headless Chromium; mobile view only for the start view (400 px).
- The pause in the background works around a Firefox bug: Firefox 149, three map tabs over hours, 35, 10 and 11 GB per tab, of which according to
about:memory11.1 GBheap/committed/unused-pages/dirtywith 0.8 GB used. Chromium over 60 updates: heap constant 21 to 26 MB. Open as Mozilla bug 2070900 (NEW since 10.09.2026). If Mozilla fixes it, the pause goes away. meshviewer.jsonat 1716 nodes: 1.9 MB, with gzip 299 KB, once per minute.
yanic
- Fork: Neanderfunk/yanic, Branch
neanderfunk, AGPL-3.0 like the original. Pinned: GitHub - Neanderfunk/yanic at 1abda031b4f07502323958bc09e5a5da0dc65ed1 · GitHub - Base: FreifunkBremen/yanic: Yet another node info collector - for respondd to be used with meshviewer to Grafana (with influxdb or graphite) old repo: https://github.com/FreifunkBremen/yanic/ old webpage: https://freifunkbremen.github.io/yanic/ - Codeberg.org
89bd94c(04.05.2026), current status, v1.9.0 plus the influxdb2 type conversion. - One commit per feature, explained in
NEANDERFUNK.md. All new keys optional; without access points in the network the branch behaves like the original.go test ./...at the final status green.
Features
- seeds: in addition to multicast, also unicast to addresses from a file, for networks that don’t get multicast to the nodes. Addresses from the batman translation table via EUI-64 of the bat0 MAC; nothing responds via originator MACs. Zusaetzliche Zieladressen aus einer Datei (seeds) · Neanderfunk/yanic@04166e1 · GitHub
- statistics.neanderfunk: reads the additional values from
neanderfunk-responddand writes them via the Influx output into the time series. statistics.neanderfunk lesen und in die Zeitreihen schreiben · Neanderfunk/yanic@20c5b02 · GitHub - Subtract access point clients from the router. An AP that a proxy like unifi_respondd reports hangs as a LAN device behind a Freifunk router; its WLAN clients appear in the translation table as clients of the router and were counted twice. APs are recognized by
software.firmware.base(nodes.accesspoint_firmware, default["UniFi"],[]turns it off). For the router,clients.totalis reduced by the clients of its APs that are online, never below its own WLAN clients. Because the value in the node data is corrected, map, time series and overall statistics match equally. At LVR on 25.09.2026: 836 of 837 AP clients behind the router of their AP, at 105 routers 1007 of 1291 clients. Clients der Accesspoints beim Router abziehen · Neanderfunk/yanic@b34fb20 · GitHub - Aliases like in hopglass-server (
nodes.aliases_path): a file sets pernode_idparts of the nodeinfo over the reported, format{"<node_id>": {"nodeinfo": {...}}}, merged according to RFC 7396."location": nullremoves a node from the map without taking it out of list and statistics. The reported nodeinfo remains, if an alias disappears, the reported takes effect immediately, even offline. The file is reloaded every minute, a corrupted file changes nothing. Aliase wie bei hopglass-server (nodes.aliases_path) · Neanderfunk/yanic@012e401 · GitHub - Remove offline nodes on request (
nodes.remove_dir): one empty file per job with thenode_idas name, processed at each save. Only removed if offline; if it reports again, it appears new. Offline-Knoten auf Anforderung entfernen (nodes.remove_dir) · Neanderfunk/yanic@7eba4fd · GitHub
Bugfixes
- One-sided AP-router link. Only the AP reports the link, the router side had TQ 0, meshviewer colored it like half a connection by average, type remained
unknown. Now the outputmeshviewer-ffrgbenters both sides, typeother. Ordinary one-sided batman links remain unchanged. Links der Accesspoints von beiden Seiten, als Kabel · Neanderfunk/yanic@7d3bd74 · GitHub - AP-AP as cable. The commit before drew links between two APs (UniFi mesh, wireless LLDP neighbors) as cable. Now AP-AP remains
wifiwith the values of both sides, only AP-router is cable, with test. Links zwischen zwei Accesspoints als Funk · Neanderfunk/yanic@99f734d · GitHub
Upstream status (Codeberg FreifunkBremen/yanic, 28.09.2026)
- Unicast: #21 (PR, unicast to nodes that don’t answer multicast) unmerged closed 2017, the behavior is in yanic today anyway (
sendUnicasts); #13 and #121 closed. Open #209 (configurable target port, 04.01.2022). Nothing on addresses from a file. - Additional fields: #171 (PR, custom respondd fields), merged 29.12.2019; #142 (channel and airtime in
meshviewer.json), closed 14.04.2022. Whether #171 would already bring our values into the Influx output is not tested. - Links: #149 (same TQ in both directions), closed 04.01.2019; #239 (PR, link type everywhere), merged 29.06.2025; open #218 (
is_activefor links, 02.02.2023). Nothing on APs reported via proxy and double-counted clients. - Remove: #81 (PR,
contrib/yanic-remove-node, a script for the state file), merged 02.11.2017. Nothing on a request directory during operation. - Aliases: nothing; #50 affects only the hopglass output format.
- Context: open #156 „Should yanic be also a respondd daemon“ (21.01.2019).
Limits
statistics.neanderfunkonly reads fields from our package, they don’t exist elsewhere. Everything else is generic.- seeds is currently not active for us.
- AP detection depends solely on
software.firmware.base. The router of an AP is the batman neighbor that the AP reports; if the proxy assignment is wrong, the correction is also wrong. - Aliases and removal are tested only with our own service menu, not with a real hopglass alias file.
unifi_respondd
- Fork: Neanderfunk/unifi_respondd, Branch
neanderfunk, GPL-3.0 like the original. Pinned: GitHub - Neanderfunk/unifi_respondd at 6a0ef19520e9f87a34b2683cb8def825ea8b4e21 · GitHub - Base: freifunkMUC/unifi_respondd
6976651(18.09.2026), also the current status of upstream main. - One commit per feature, explained in
NEANDERFUNK.md. 113 tests at the final status green. Withoutoffloader_by_apandinterfaces, the fork behaves like upstream except for bugfixes.
Features
- Router per access point (
offloader_by_ap): Upstream knows exactly one Freifunk router per site, all APs inherit its domain, gateway, next hop and map line. New: a file{"aps": {AP-MAC: {"router": MAC}}}, for us generated from the batman translation table. Its entry takes precedence overoffloader_macof the site. Router je Accesspoint aus einer gemessenen Zuordnung (offloader_by_ap) · Neanderfunk/unifi_respondd@5471b90 · GitHub - site_code of the router: APs additionally report the
site_codeof the node they are behind. Maps that filter per location bysite_codewould otherwise leave them out. APs melden den Ortscode ihres Routers auch als site_code · Neanderfunk/unifi_respondd@a42459a · GitHub - All interfaces, filter per domain, cache (
interfaces,cache_seconds): one process listens on all named interfaces, recognizes the domain by the interface on which the request arrives (IPV6_PKTINFO) and responds only with APs whose router is there. Each collector in the mesh, even a foreign one, sees exactly the APs of its domain. The controller is questioned at most once per interval (default 60 s), on error the last data persists. For us on 43 batman instances. Auf allen Schnittstellen lauschen, je Domain filtern, Controller zwis… · Neanderfunk/unifi_respondd@87d0026 · GitHub - Coordinates without address lookup: SNMP Location no longer goes to Nominatim,
geopyis gone.parse_location()reads decimal point or comma, separated by comma, semicolon or space, and URLs copied from Google Maps (pin!3d/!4d, then?q=, then map center/@). Koordinaten ohne Adresssuche, gaengige Schreibweisen gesaeubert · Neanderfunk/unifi_respondd@d17a64a · GitHub - Cardinal directions and degrees/minutes/seconds: N/S/E/W before or after the number, with direction also swapped order, the format Google Maps displays (
51°17'14.8"N 6°21'13.7"E), and degrees with decimal minutes. Rejected only if contradictory. Koordinaten mit Himmelsrichtung und Grad/Minuten/Sekunden · Neanderfunk/unifi_respondd@b3f9f54 · GitHub - Airtime from the controller: The controller gives per radio module percent (
radio_table_stats:cu_total,cu_self_rx,cu_self_tx), Gluon reports continuous counters, from whose difference yanic forms percents. The fork lets the counters continue with the clock, each with the last known percent value, so yanic calculates exactly these percents. Airtime aus dem Controller · Neanderfunk/unifi_respondd@4fa1954 · GitHub - Share port 1001 (
SO_REUSEADDR), so on the same machine mesh-announce can answer for the machine itself. A unicast request only gets one of the two, at most costs one round trip. Port 1001 teilen (SO_REUSEADDR) · Neanderfunk/unifi_respondd@ff9999c · GitHub
Bugfixes
- Null Island. APs without SNMP Location stood at 0/0 in the Gulf of Guinea, maps with calculated view pulled it there. Upstream sets
lat, lon = 0, 0as default. Fix: no location instead of 0/0, even if 0/0 is entered; the AP then stands in list and graph at its router, only not on the map. Ohne Koordinaten kein Ort statt 0/0 · Neanderfunk/unifi_respondd@727951f · GitHub - Airtime 0%. In rx/tx were byte counters of SSIDs instead of radio time, on the 2.4 GHz band even that of the 5 GHz band,
activewas missing. Fix: airtime from controller, see above. - Send errors ended the service. If
sendtofailed, the exception ended the process, systemd restarted it after 30 s. For us every five minutes, cause a full IPv6 neighbor table (EINVAL). Fix: log, continue with the next packet. Sendefehler beenden nicht mehr den Dienst · Neanderfunk/unifi_respondd@4b445cc · GitHub
Upstream status (freifunkMUC/unifi_respondd, 28.09.2026)
- Location: #95 (don’t geocode empty SNMP Location) and #97 (limit recursion), both merged 18.09.2026; #9 (coordinates from SNMP Location), merged 02.01.2022. Nothing on renouncing Nominatim, cardinal directions, DMS or 0/0.
- Radio: #41 (wireless infos), merged 13.05.2024; #50 (tx/rx_bytes), merged 25.08.2025. Nothing on airtime from
radio_table_stats. - Router: #22 (neighbour/offloader), merged 10.01.2022; #35 (link to offloader sometimes missing), closed 24.08.2022. Nothing on per-AP assignment.
- Domains: #24 (domain add), merged 13.01.2022; #39 (fallback domain), merged 26.10.2024. Nothing on multiple interfaces and
site_code. - Controller: #21 (ask once), merged 07.01.2022; #98 (skip site on error), merged 18.09.2026; open PR #96 (reuse session).
- Crashes: #36, closed 23.01.2025 with #43; #37 (systemd workaround), merged 31.03.2022. Nothing on
sendto. - Open, would affect our commits on merge: PR #94 (multi-controller, omada), PR #71 (provider-agnostic: UniFi, Omada, UISP), PR #70 (abstract base classes).
Limits
- The AP-to-router assignment is bound to our infrastructure: it arises from the translation tables of our map server. The list of manufacturer prefixes in it is maintained, not complete.
- APs without a measured router the service does not report; a wrong router would be worse than a missing AP. Out-of-band managed installations cannot be measured this way.
- 6 GHz is missing, yanic knows only 11g and 11a. On 25.09.2026, none of the 545 APs reported an active 6 GHz module.
- Noise is not provided by the controller. Airtime is as old as the controller data, a few minutes.
- Tested with one controller and only on our instance. In the field only northern hemisphere and eastern longitudes, the rest with tests.
- Numbers, 25.09.2026, first measurement after commissioning: 544 APs online, 1021 clients on APs; sum of all clients around 3050, without subtraction at the router it would have been around 4000.
Time series, Grafana, map server
- Repo: Neanderfunk/mapserver, BSD-3-Clause: setup scripts per layer, generators for yanic, meshviewer and nginx configuration, the Grafana dashboards as generator, Checkmk checks, documentation. Builds the three forks from their
neanderfunkbranch. Pinned: GitHub - Neanderfunk/mapserver at 11fcd8599d6aeb1b5c89194d8ca2d04eadc49b83 · GitHub - Unchanged from packages or fixed upstream commits: VictoriaMetrics 1.112.0 (Debian trixie), Grafana 13.2.2, mesh-announce
40be9a1, Tunneldigger client9a9a427(C).
Time series
- yanic writes via the Influx output to VictoriaMetrics, metric names
<measurement>_<field>, for examplenode_load,link_tq,node_airtime11g.chan_util. Retention 400 days. - Discarded on receipt:
owner(contact information, yanic knows nono_ownerfor this output) and frequency labels. A channel change would otherwise create a new time series each time, a device came so to 16 in a day. The frequency remains as a measured value. - Read-only public: https://neander.map.freifunk.space/nf/prom/api/v1/, released only
query,query_range,series,labels,label/<name>/values,export,status/tsdb,federate; everything else 403. Same source from which the node window draws. - 89,643 time series (28.09.2026).
Grafana
- Grafana, readable without login.
- Dashboards node (linked from node window), domain, community (linked at top of Statistics tab), supernode. Generated per script (
zeitreihe/grafana/dashboard-erzeugen.py), not maintained by hand. - Community: Gluon nodes and supernodes as large numbers, progressions for firmware, hardware and targets, traffic counted only once instead of adding node and supernode sides.
Map server
- One map for all 48 domains, all configuration from a domain table, one meshviewer build.
- Tunnel per domain, in the mesh only one asking node: no
gw_mode, no clients, no DHCP, no forwarding, only link-local,accept_raoff, fixed locally managed MACs, a speaking name at the broker. - mesh-announce answers for the map server as
map-neanderfunk-<domaincode>, otherwise it would be a node without data for every other map. Shares port 1001 with unifi_respondd. - Start view without outliers: nodes up to 25 km distance form a group, even across chains; the largest group and each 10% of nodes count; 0/0 never counts. At 1002 nodes with location 977 are in the core. Startausschnitt ohne Ausreisser · Neanderfunk/mapserver@875dd68 · GitHub
- Service menu behind Authentik, only for maintainers: remove offline nodes, override coordinates or remove from map, change names, with history. Technically via
aliases_pathandremove_dirin yanic. - Address in coordinate picker via its own cached proxy instead of directly from browser to Nominatim: only latitude and longitude on 5 decimal places onward, at most one request per second, cache 30 days.
- Delivery with gzip since 27.09.2026; Debian’s nginx default compresses only
text/html. - IPv6 neighbor table:
gc_thresh31024 (Debian default) not enough for 43 batman instances,sendtofailed withEINVAL. Now 16384.
Limits
- No vector maps, base map is raster tiles via our tile proxy plus WMS NRW.
- A second installation from the repo from scratch has not happened, the setup scripts went through only once here.
Feedback from reusers welcome.