KiekR.app beta

Work in progress KiekR — Documentation

KiekR Documentation

KiekR is a MeshCore companion app for iOS and Android — it pairs with the same MeshCore Companion hardware the official app uses, not a separate KiekR device. It speaks the identical on-air protocol, so KiekR users and official-app users share one seamless mesh, but it adds features for serious operators: multi-identity history, manual path control, default-region pinning, repeater ACL workflow, public coverage-map contribution, and an MQTT bridge.

Overview

KiekR is a third-party MeshCore client. It connects over Bluetooth Low Energy to any MeshCore-running radio (see Supported hardware), turns it into a chat-and-coverage device, and lets you join the public mesh that already exists.

If you already run the official MeshCore app, KiekR works with the same radio without changing anything on it: same on-air bytes, same repeaters, same protocol — switch apps on the same node and the radio does not notice. Channels and contacts live on the radio and survive an app switch. DMs and chat history live in the app — neither the official MeshCore app nor KiekR exports them, so they stay with whichever app wrote them. For everything that is exportable (contacts, channels, settings), KiekR reads and writes the official MeshCore backup format; anything KiekR-specific stays out of that file and exports separately as its own KiekR bundle, so the MeshCore-compatible file stays pure stock-shape. Goal: 100% interoperability with the official protocol.

What KiekR adds on top is documented in the Features unique to KiekR section. Most of those features either expose mechanics that already exist in the protocol but are hidden in stock UIs, or solve recurring operational pain points (which region am I in? which path should I send over? did the repeater actually accept my login?).

Supported hardware

KiekR aims to support every device that MeshCore firmware supports — currently 60+ board variants. The app reads each device's model byte at connect time and adapts UI to its capabilities (GPS / no GPS, button count, screen / headless, advertised radio bands).

  • MeshCore firmware: any official build, any supported board. Some features need raw-packet access from the firmware (the MQTT bridge and parts of the coverage-map contribution) — they degrade gracefully when the firmware does not hand us raw bytes.
  • Tested: a representative subset of MeshCore-supported boards across nRF52840 and ESP32 families. Other variants should work; please file an issue if your board misbehaves.

There is no device-name allow-list in the BLE scanner. Anything advertising the MeshCore service UUID will be offered for pairing.

Install

Android

Main source: the Google Play Store — KiekR is now in open beta, install it directly from the listing. Alternatively, you can sideload the APK directly:

  1. Download the latest app-release.apk from kiekr.app.
  2. On your phone, allow installation from your browser (or file manager). On Android 13+ this is a per-app permission under Settings → Apps → Special access → Install unknown apps.
  3. Open the APK and tap install. Subsequent updates can be installed over the top — settings, identity, and message history are preserved.

Sideloaded installs do not auto-update; check kiekr.app for new APK releases or switch to the Play Store listing for automatic updates.

iOS

KiekR for iOS is currently TestFlight-only; an App Store release will follow. Apple's review policy means the iOS version lands noticeably later than Android, so it lags behind on features for now. The plan is full parity, with both platforms shipping through their respective stores.

To join the TestFlight, install the TestFlight app from the App Store and follow the invite link on kiekr.app. Once the public build is approved, this section will be updated with the App Store link.

Firmware

You do not need to flash any custom firmware to use KiekR. The official MeshCore firmware works out of the box.

Pair a node

KiekR talks to your radio over Bluetooth Low Energy. The first time you connect to a node:

  1. Power on your MeshCore radio and make sure Bluetooth is on. MeshCore firmware advertises whenever no client is currently connected.
  2. In KiekR, open Nodes → + and pick the radio from the discovered list. Devices are listed by their advert name (or a generic model name if no advert has been heard yet).
  3. Approve the BLE pairing request on the phone. If the radio prompts for a PIN, enter the one shown on the device screen (T-Echo, Heltec V3) or use the default 123456 for headless boards.
  4. Once paired, the Companion is remembered. Re-connecting only takes a tap; you do not need to re-pair after reboots.

Identity behaviour

On first connect, KiekR reads the radio's on-device Ed25519 identity (its long-lived public key) and stores it. From that point on, your DMs and signed adverts use that identity — exactly the same key the stock MeshCore app would use on the same node.

When you connect to a different node that has its own identity, KiekR adopts the new node's key silently and keeps a history of every identity you have ever used. This is the "multi-identity history" feature; see below for what it means for your DM history.

Channels, DMs, regions, paths

Channels

A channel in MeshCore is a shared 16-byte AES-128 secret. Anyone who has the secret can read messages on that channel; messages are sent flooded, so every repeater within range relays them. The default channel is named public and ships with a well-known key, so out of the box you can chat with anyone on the mesh.

Hashtag channels (for example #test, #ping) use the MeshCore-standard #-prefix convention — it is part of the protocol, not a KiekR addition. The hash is part of the channel name, not a routing prefix, and is preserved exactly as typed — including casing — to stay byte-compatible with what stock MeshCore apps see on the wire.

Direct messages

Direct messages are end-to-end encrypted between two identities (Ed25519 keypairs). The recipient must have heard your advert at least once — that is how you exchange public keys without a server. KiekR shows DMs in a separate Chats list per identity.

Outgoing DMs are sent as flood by default; the stock firmware routes them via a learned contact slot if one is established. The standard manual-path override (set a hand-picked sequence of repeater hops) is available too — same surface as in the official MeshCore app.

MeshCore regions

A MeshCore region is a named 16-byte HMAC scope that limits which repeaters relay a given message. Regions exist so a repeater in your village can stay quiet for traffic from a different village without losing the public mesh. KiekR lets you discover regions automatically (every advert lists which regions a repeater participates in) and pick a default region for your outgoing messages.

Paths

When you DM a peer through a repeater chain, MeshCore learns the path it travelled and stores it on your radio as a contact path. Future messages to that peer follow the same hops, which is faster and uses less airtime than a fresh flood. KiekR shows the learned path on each contact's detail screen.

Paths can go stale (a repeater goes down, the peer moves). When you suspect the learned path is dead, open the conversation with that contact and trigger a fresh path discovery from the path indicator — KiekR floods a discovery packet and replaces the stored path with whatever comes back. The path will also re-learn passively the next time a message from that contact reaches you over a different route.

Repeaters & ACL

Repeaters relay messages on the mesh. Most are run by community volunteers. Recent stock firmware versions added a per-repeater Access Control List (ACL): the repeater silently drops privileged requests (path discovery, neighbour list, telemetry, region admin) from senders not on its ACL. Public chat traffic is unaffected; only the diagnostic / admin requests are gated.

Group A vs Group B

KiekR groups repeaters into two visual states based on whether your current identity has been ACL'd:

  • Group A — no ACL row established. You can chat through the repeater normally, but path discovery, neighbour list, and telemetry will silently fail. There is no on-air signal that the repeater dropped your request, so without this gating you would just see "nothing happens."
  • Group B — you have a successful login on file for this repeater (with this identity). Privileged requests should work.

KiekR shows an ACL badge on each repeater's detail screen so you can see at a glance which group you are in.

Logging in to a repeater

You log in to a repeater by sending it your password (set by the operator). KiekR caches the successful login per (identity, repeater) pair — switching to a different identity will require re-logging in even on the same repeater. Logins survive across app restarts.

Repeater admin commands (set region, change name, reboot) are reached over RF, never over BLE. Your own connected node is not a repeater and therefore has no region-admin CLI.

Features unique to KiekR

These are the features that distinguish KiekR from the official MeshCore app. Most of them either solve recurring operational pain or expose mechanics that the protocol already supports but the stock UI hides.

Multi-identity history

Every node you have ever connected to leaves its identity (Ed25519 keypair) in KiekR's identity history. Switching to a different node silently switches the active identity to that node's key, but the previous identity stays on disk so you can switch back without losing DM history.

DMs are scoped to identity (the encryption key changes with the identity), but channels, regions, and contacts are app-global. A v2 explicit identity picker is on the roadmap; v1 is silent so KiekR matches stock-app behaviour by default.

Per-message region display

Every message bubble carries a region chip. KiekR classifies each message into one of four states — named region (chip shows the region label), region unknown (the message carried region framing but the key wasn't resolved), from device (drained from the radio's offline queue, no on-air envelope), or no region (plain flood with no region scope). Classification is shared with iOS via the core library so both platforms render identical chips for the same message.

The right repeater when key prefixes collide

When two repeaters share a public-key prefix (it happens; pubkey prefixes are not globally unique), older clients pick "the most recent advert" and frequently end up on the wrong one. KiekR ranks candidates by a stronger signal: GPS proximity to your last known location, then over-the-air receipt vs. directory hearsay, then advert freshness as a tiebreak.

You will rarely notice this feature exists — that is the point. It removes a class of "I sent to the wrong repeater" mistakes that previously required manual intervention.

Tools — diagnostics and tuning

Settings → Repeater admin (under the Tools group in Settings) is an area for advanced tunables most users will never touch. Today it hosts region-admin-related knobs (region admin is the per-repeater command surface for operators with administrative access):

  • Region-admin CLI timeout — how long to wait for a repeater's reply to a region-admin command before giving up. Default 30 s; range 10–60 s in 5-second steps.
  • Retries per page — how many extra attempts the core library makes per neighbour-list page if the firmware does not reply within the per-attempt window. Useful on flaky BLE or radio links.
  • Region-admin auto-save — write changed region settings back to the repeater immediately on edit, instead of waiting for an explicit save tap.

Tunable neighbour fetch

Neighbour fetch retrieves the repeater's neighbour list page-by-page. Page size is firmware-fixed and not user-tunable. The retry count (Retries per page) is adjustable in Settings → Repeater admin so you can compensate for lossy links without inflating page requests.

Online hop resolution

MeshCore paths encode repeater hops as short public-key prefixes (1–3 bytes depending on the path encoding setting). When a hop prefix doesn't match any locally heard advert, KiekR can look it up in an online directory — api.kiekr.app or analyzer.meshcorenetz.de. The Message details sheet (long-press any message) shows the decoded hop chain with a globe button to trigger a directory lookup for unresolved hops. Resolved hops are drawn colour-coded on a map via Show path on map.

Directory lookups are opt-in. Enable sources in Settings → Sharing → Directory. For hands-free operation, Settings → Auto-resolve lets KiekR fire lookups automatically when it notices an unknown hop, heard repeater, or missing neighbour row — off by default.

Stock-compatible bundle export

KiekR can export your full setup (identity, channels, contacts, radio settings, position) as a single JSON file. The format matches the official MeshCore app's export verbatim, so you can:

Import into a fresh KiekR install on a new phone, import into the official MeshCore app, or hand-edit the file to tweak anything. KiekR-only fields (login cache, manual paths, MQTT brokers) live in a separate encrypted KiekR bundle so the stock-compat JSON stays pure stock-shape.

MQTT bridge

KiekR can act as a bridge from the radio mesh to one or more MQTT brokers. Every raw LoRa packet your radio receives — including encrypted bytes the app cannot read — is forwarded to the broker. This makes the bridge useful for protocol analysers (Meshcorenetz, LetsMesh) and any MQTT-aware system that can decode the raw MeshCore packet format.

This is a KiekR-only feature; the official MeshCore app does not include an MQTT bridge.

Setting up a broker

  1. Open Settings → Community upload and tap Add broker in the MQTT upload section: host, port, TLS toggle, optional username/password.
  2. Fill in the two required observer fields: a 3-letter IATA airport code that identifies your region on the analyser (e.g. HAM, MUC, JFK) and an observer name that appears next to your packets (the app prepends "KiekR_" automatically).
  3. Optionally restrict to "Wi-Fi only" so the bridge does not burn cellular data when you are on the road.

Topic structure & payload

Topics follow the default pattern meshcore/{IATA}/{PUBLIC_KEY}/packets (customisable per broker). Payloads are raw packet bytes — not JSON. JWT is used only for broker authentication when the broker requires it (e.g. LetsMesh), not as a payload wrapper. Topic and auth settings are configured in the broker editor inside Settings → Community upload.

Coverage-map contribution

KiekR can contribute to the public MeshCore coverage map at map.kiekr.app. The map shows, in real time, where repeaters are, how they connect, and which regions they cover — all derived from cryptographically-signed observations contributed by KiekR users.

Turning it on

Open Settings → Community upload and enable Upload to map.kiekr.app. From that point on, your radio's heard adverts (which already arrive over the air anyway) get forwarded to the meshcoremap server signed with a key generated locally for that purpose only — distinct from your MeshCore identity. There is no IP, no analytics, no message content.

Three contribution tiers

  • Tier A — claim your own repeater. If you operate a repeater, KiekR offers to publish it to the map after a successful neighbour-list or region fetch (which proves your identity is on the repeater's ACL). Your repeater appears as a public pin with name, location, and regions you choose to expose.
  • Tier B — relay heard adverts. Every signed advert your radio hears is forwarded. The server verifies the advert's Ed25519 signature and stores the position. Tier-B-only repeaters are invisible everywhere public — no pins, no edges, no read API — but their positions do feed coverage-polygon geometry, so regions they help cover are represented even before the operator claims the repeater via Tier A.
  • Tier C — region enrichment. If a repeater is already public on the map but its region tags are incomplete, you can contribute additional region observations. Strictly additive merge — you cannot remove someone else's pin or relabel it.

How regions become polygons

A region polygon answers a single question: where is this region audible? For each region R, every R-tagged member M contributes a self-disc at its own position; for each repeater H that heard M, a capsule (a rectangle capped with semicircles) is drawn between M and H — regardless of whether H itself carries region R. The reasoning: H proves it can pick up M's R-region adverts at its location, so the corridor between M and H is inside R's audible footprint. Direction matters: only "H heard M" draws a capsule, not "M heard H" — M having heard a faraway transmitter says nothing about M's own reach. The union of all capsules and self-discs is the region polygon. Vertex precision is bounded to a 500 m grid, then buffered by 200 m and simplified at 150 m — that is the privacy floor; no individual operator's exact location can be reverse-engineered from the polygon alone.

Operator opt-out

Repeater operators who do not want their hardware to feed the map can put a 🚫 (no-entry sign) anywhere in the broadcast advert name. The server then drops the repeater from every code path — pin, polygon, read API. The opt-out is enforced server-side so even relayed adverts respect it.

Privacy contract

KiekR is built around the assumption that your phone, your identity, and your location are yours. The contract:

  • No analytics, no telemetry-to-vendor, no crash-reporter that phones home. The app does not connect to any KiekR-operated server unless you have explicitly enabled the coverage-map contribution or the MQTT bridge.
  • Your MeshCore identity (the Ed25519 key that signs your adverts and decrypts your DMs) lives only on your radio and your phone. It is never uploaded anywhere.
  • Coverage-map contributions are signed with a separate key generated for that purpose only. Revoking a coverage-map key does not affect your MeshCore identity, and vice versa.
  • MQTT publishing is configured per-broker by you. KiekR has no default broker; nothing leaves the phone unless you set up a broker yourself.

For the legal version of this contract see the privacy policy.

Troubleshooting

My repeater is on Group A and login does not seem to do anything.
Check that the repeater runs a recent MeshCore firmware that supports the per-repeater ACL (older builds did not). Then verify the password matches what the operator set; KiekR caches the wrong-password failure so you may need to manually retry from the repeater detail screen.
I sent a DM but the recipient never got it.
DMs require the recipient to have heard your advert at least once — without that, you do not have a path to encrypt against. Check the contact has a learned path; if not, send a couple of adverts from your side and wait for the recipient to come back into range.
My phone says paired but messages do not arrive.
BLE connection state can desync between phone and radio. Toggle the connection in the My node tab — tap Disconnect then Connect. If still stuck, restart Bluetooth on the phone (faster than rebooting the radio).
After a fresh install, the app says my identity changed.
A fresh install briefly uses an "app-generated" identity until you connect to a radio. The first time you connect, KiekR adopts the radio's identity and silently retires the auto-generated one. The "identity changed" warning is suppressed in this case; if you see it anyway, please report it in the KiekR Discord or to info@kiekr.app.
How do I reset everything?
Go to Settings → Backup and import any backup file using the Replace mode — this wipes your local channels, contacts, and regions and replaces them with the bundle contents. For a full local data clear without a backup file, use Android's system-level app data clear (Settings → Apps → KiekR → Storage → Clear data). Your radio is never touched — identity, channels, and contacts on the radio survive, and the next connect silently re-adopts them.