You can find an example configuration at config.example.json. This guide tries to list some of the existing configuration flags.
DataPath describes the URL where your ./meshviewer.json file is located. It can be relative. There should be a list of nodes and links.
{
"dataPath": ["https://yanic.batman15.ffffm.net/"], "siteName": "Freifunk Frankfurt",
"maxAge": 21, # default 14, number of days a node is marked as new
"maxAgeAlert": 5
"nodeZoom": 19, # default 18, max. 19, OpenStreetMap zoomlevel when a node is selectedYou can add some general links via a list. They get displayed in the header of the sidebar.
"linkList": [
{
"href": "https://freifunk.fail/contact/",
"title": "Kontakt"
},
{
"href": "https://freifunk.fail/de/imprint/",
"title": "Impressum"
}
],Defines a List of Maps that are used as a background.
"mapLayers": [
{
"name": "OpenStreetMap", # Displayed in the browser
"url": "https://tiles.ffm.freifunk.net/{z}/{x}/{y}.png",
"config": {
"type": "osm",
"maxZoom": 19,
"invertInDarkMode": true,
"attribution": "<a href='https://github.com/freifunk/meshviewer/issues' target='_blank'>Report Bug</a> | Map data © <a href\"http://openstreetmap.org\">OpenStreetMap</a> contributor"
}
},
{
"name": "BaseMap.de (Vektor)",
"url": "https://sgx.geodatenzentrum.de/gdz_basemapworld_vektor/styles/bm_web_wld_col.json",
"type": "vector",
"config": {
"attribution": "© basemap.de / <a href='https://www.bkg.bund.de'>BKG</a> (2025) <a href='https://creativecommons.org/licenses/by/4.0/'>CC BY 4.0</a>; Datenquellen: © GeoBasis-DE / <a href='https://www.bkg.bund.de'>BKG</a> (2025) <a href='https://creativecommons.org/licenses/by/4.0/'>CC BY 4.0</a>; außerhalb Deutschlands: © <a href='https://www.openstreetmap.org/copyright'>Open Street Map Mitwirkende ODbL v. 1.0</a> ; © <a href='https://openmaptiles.org/'>OpenMapTiles</a>"
}
}
],The invertInDarkMode boolean allows you to automatically invert standard map tiles (like light OpenStreetMap) via CSS when the app switches to dark mode, matching the dark aesthetic without needing a dedicated dark tile server. Note that this property is ignored for vector map layers.
fixedCenter defines the default area that is visible when opening the map.
"fixedCenter": [
[50.5099, 8.1393],
[49.9282, 9.3164]
],Via geojson you can add custom features to the map like district boundaries.
"geo": [
{
"json": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"stroke": "#555555",
"stroke-width": 2,
"stroke-opacity": 1,
"fill": "#6db743",
"fill-opacity": 0.5,
"segment": 3
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
6.170628106033859,
50.80453937432226
],
[
6.173592924692929,
50.80457163351845
]
]
]
}
}
]
},
"option": {
"style": {
"color": "#555555",
"weight": 5,
"opacity": 0.4,
"fillColor": "#555555",
"fillOpacity": 0.1
}
}
}
],
}Map human readable names to the domain from the individual node as stated in the meshviewer.json to be displayed as an info when the node is selected.
"domainNames": [
{ "domain": "ffffm_60431", "name": "60431 Frankfurt am Main" },
{ "domain": "ffffm_default", "name": "Default" }
],Add Links to external Sites like wikis or monitoring. You might use one of the following variables as placeholders:
| variable | description |
|---|---|
{NODE_CUSTOM} |
node_name with custom regex |
{NODE_ID} |
node_id from meshviewer.json (legacy 12-digit hex IDs, numeric IDs, or dotted IDs like node.a03; no - inside a node ID!) |
{NODE_NAME} |
hostname |
{TIME} |
Here are some examples:
"nodeInfos": [
{
"name": "Clientstatistik", # required, title of the section
"href": "https://freifunk.fail/dashboard/db/nodes?var-name={NODE_CUSTOM}&from=now-7d&to=now", # required, link to website
"image": "https://freifunk.fail/render/dashboard-solo/db/node-details-for-map?panelId=7&var-id={NODE_ID}&from=now-7d&to=now&width=600&height=300&theme=light&_t={TIME}", # optional
"title": "Knoten {NODE_ID} ({NODE_NAME})" # required, is used as helptext if an image is present, otherwise text of the link
},
],Renders charts by querying json data directly from Grafana. This reduces the CPU load on the Grafana server and allows for higher resolutions than the image rendering API.
Requires Grafana's HTTP API to be accessible from the browser via a reverse proxy or with CORS allowed on the Grafana server.
The example below uses nodeCharts. The other …Charts are configured identically.
linkCharts replaces $source and $target instead of $node.
"grafana": {
"url": "/_grafana", # Base URL of Grafana; use a reverse-proxy path or full HTTPS URL
"orgId": 1 # Grafana org ID, see `/admin/orgs` (default 1)
},
"nodeCharts": [
{
"name": "Traffic", # Section heading shown above the chart
"datasourceUid": "abc123xyz", # Datasource UID — see below
"datasourceType": "influxdb", # Grafana datasource plugin ID
"query": "SELECT mean(\"rx\") AS RX, mean(\"tx\") AS TX FROM \"traffic\" WHERE \"node\" = '$node' AND $timeFilter GROUP BY time($interval) fill(null)",
# $node is replaced by meshviewer; $timeFilter/$interval is replaced by Grafana
"format": ".2~s", # optional, d3-format specifier for axis and stats values (default ".2~s")
"unitSuffix": "B/s", # optional, string appended to formatted values, e.g. "B/s"
"from": "now-7d", # optional, time range start (default "now-7d")
"to": "now-1m", # optional, time range end (default "now-1m")
"maxDataPoints": 300,
"series": [
{ "name": "RX", "color": "#73BF69" },
{ "name": "TX", "color": "#F2495C", "negate": true }
]
}
],Finding the datasource UID: In Grafana, go to Configuration → Data Sources → select the datasource.
The UID is visible in the browser URL: /datasources/edit/{uid}.
Finding the query: In Grafana, go to your panel, Edit and copy the query.
series[] fields:
name— must match the series name returned by the query (column alias or measurement name)color— any CSS color value, e.g."#73BF69"negate— settrueto flip the sign, e.g. for TX on a combined RX/TX chart
TODO
"globalInfos": [
{
"name": "Wochenstatistik",
"href": "https://freifunk.fail/dashboard/db/deck?from=now-7d&to=now",
"image": "https://freifunk.fail/render/dashboard-solo/db/deck?&panelId=22&from=now-7d&to=now&width=600&height=300&theme=light&_t={TIME}", # optional
"title": "Bild der Wochenstatistik"
}
],Nodes can display a picture of the device type. These need to be provided as svg pictures. There is an existing repository with these pictures (see source below).
"devicePictures": "https://freifunk.github.io/device-pictures/pictures-svg/{MODEL_NORMALIZED}.svg",
"devicePicturesSource": "<a href='https://github.com/freifunk/device-pictures'>https://github.com/freifunk/device-pictures</a>",
"devicePicturesLicense": "CC-BY-NC-SA 4.0", "node_custom": "/[^a-z0-9\\-\\.]/ig", # regex that gets applied to node_name, e.g. used for normalisation of domain namesBoth the deprecation and the EOL warning can be turned off with "deprecation_enabled": false - but we wouldn't suggest it.
You can insert custom HTML into the deprecation and eol warning via "deprecation_text":"" and "eol_text":"" respectively.
"deprecation_text": "Hier kann ein eigener Text für die Deprecation Warning (inkl. HTML) stehen!", # text that gets displayed when a node with a device defined in deprecated (see below) is selected
"eol_text": "Hier kann ein eigener Text für die End-of-Life Warnung (inkl. HTML) stehen!", # same as deprecated, but for eol
"deprecation_enabled": true, # turns off eol and deprecation warnings
# define a list of devices that are deprecated
"deprecated": [
"D-Link DGS-1210",
"TP-Link Archer C2 v3",
],
# define a list of devices that are End of Life
"eol": [
"A5-V11",
"AP121",
]
}