Repository navigation
Follow Me
🇮🇱 עברית
A link that shows someone where you are, right now, on the same chart you are flying. No account, no server of ours, nothing to sign up for. You share a URL; whoever opens it watches your aircraft move across NavAid's map until you stop.
Planning and courtesy, not a tracking service. The relay is a free public broker, best-effort and unauthenticated. There is no history. A phone that loses signal simply stops publishing, and the viewer says so rather than drawing you where you last were.
Follow me is on in the live app.
-
Press Follow me in the toolbar. You can do this on the ground, before the flight — the link copies straight away and you can send it ahead; the viewer shows "waiting for a position" until you are moving. (The share button on the map — a dot with radio waves — is the same action, but it only appears once a fix is driving the map, so pre-flight sharing is the toolbar's job.)
-
Type an identifier — an aircraft code (
4X-CDE) is the obvious one, but a callsign or a name does just as well. Nothing verifies it, and it is not part of the link's address — see One link per device below. Spaces are stripped, so the space a phone keyboard adds after autocomplete does not travel with the name.The field is never empty when it opens: the code this device used last, or the flight it is about to fly (
LLHZ-LLHA), orNavAid. The name is a label, not an address — nothing collides — and a blank field is a share nobody makes. It opens selected, so typing a registration replaces the suggestion in one go.The question is asked in an app dialog, not
window.prompt: a browser may suppress that after you dismiss one, and an Android WebView shows it only if the host app implements it — which is exactly where Follow me gets used. A dialog nobody sees is a share nobody makes. -
The share sheet opens on a phone; otherwise the link is copied to the clipboard. If nothing is flowing yet, the confirmation says so — positions start once Location or Record is on.
-
Turn on Location or Record when you are ready; publishing begins with the first fix.
The button then reads Stop sharing, and the map button turns solid (◉). Press either to stop. Stopping clears the last position from the relay, so the link goes blank rather than freezing on the place you landed.
The button also reports what the connection is doing: connecting, reconnecting while the socket is re-establishing, and stopping while the last position is being cleared.
Stopping cannot hang. Clearing that last position needs the relay to acknowledge it, and a
relay that never answers — no signal, broker down, captive wifi — used to leave the button
reading stopping… for good. The wait is bounded (followMeStopMaxSec, 25 s), and pressing
the button again ends it immediately: sharing stops here, publishing ceases and the device
revokes its own consent. What cannot be promised is the position already retained on the relay,
so the app says the relay never answered and points at 🔑 New link, which does revoke it.
A page opened on someone else's link is watching them, and it does not share. Pressing Follow me there asks whether to leave the flight you are following: on yes it drops the watch and reloads the map without the link, which is the state you want to share from anyway. Everything else in the address is kept — language, tuning switches, a loaded route.
Opening a link while your own sharing is running stops it, and a stored share is not resumed on a page whose address carries a link.
There is no separate leave control, on the banner or anywhere else — the watch is wired into the page from the address it opened with, so the honest way out is the same page without that address. Three ways to get there:
-
Press Follow me and confirm. That is the one in-app route: it drops the watch and reloads
the map without
?follow=…#k=…, keeping language, tuning switches and the route you had. -
Open the address without the link — delete
?follow=…and the#k=…fragment, or open navaid.supino.org fresh. - Close the tab. Nothing is left running: the watch is the page, and a viewer stores nothing about the aircraft it watched.
Simply walking away also ends it in practice — a viewer only reads a retained position, it never publishes — but the banner stays on screen until one of the above.
Why so firm: the link a viewer holds carries a capability that can publish to the topic it is
watching. A page doing both is one mistake away from a share pointed at somebody else's
aeroplane. A fleet that wants both anyway — a chase plane, two aircraft flown off one phone —
turns on featureFollowMeShareWhileViewing, and even then a share mints this device's own id
and key.
Opening the link shows the chart with the aircraft on it:
- An aircraft symbol with the code beside it, nose pointing along the reported track — and it stays correct when the map is rotated.
- The map follows every fix. It is a live tracker, so each new position takes the centre.
- A banner under the toolbar:
4X-ABC · 2001 ft · 95 kt · 002° · 32.1000, 34.8000 · Last position 3s ago. Only what the aircraft actually sent — a missing altitude is left out rather than shown as zero. It reads correctly in Hebrew, with every aviation value kept left-to-right inside a right-to-left sentence. -
The heading is the one the pilot is reading. The wire carries a true track; the banner
prints it magnetic, through the same formatter as the pilot's own readout — reported from a
flight where the phone said
299and the computer following it said304, which is exactly the variation. A stationary aeroplane reports no course at all, so the phone falls back to its compass and the banner marks that the same way the readout does:~299°, where the aircraft is pointing rather than where it is going. - The aeroplane is drawn above the chart, not under it. The route, its waypoints and their names are painted on a canvas that sits above every map marker, so the followed aircraft used to disappear beneath a reporting point's circle. It has its own layer now.
- The map clock goes away while following. A follower's page is a live tracker: the hour is now, and nothing on it answers to a look-ahead.
-
Staleness is stated, not hidden. Past
followMeStaleSec(30 s) the banner says the feed has stopped and the aircraft fades. A map that quietly keeps drawing an aeroplane where it last was is worse than one that admits it does not know.
A viewer opening mid-flight sees the aircraft immediately: the last position is retained on the relay, so there is no wait for the next transmission.
A share carries the route as well, on its own retained topic beside the position. A viewer is asked, never overruled: the pilot is sharing a route (LLHZ → LLHA). Load it? This replaces the route on your map. Answering no means no for that plan — a reconnect re-delivers the same retained packet, and a second identical prompt is how a prompt gets dismissed unread.
The question is the app's own dialog, not the browser's confirm(). A WebView shows one only
when the host app implements it, so in the APK that question was never asked and silently
answered no.
Three things decide whether it is asked at all:
-
A plan is only offered while an aeroplane is flying it. Both topics are retained, and the route outlives the position — after a stop the relay may still hold the plan — so the route waits until a live, non-stale position has arrived. Reported as stopping the share tells me the pilot has a route.
-
A plan already on your map is not a question. Waypoint for waypoint: if it matches, there is nothing to decide, which is why answering either way appeared to do nothing.
-
A "no" survives a refresh. Both topics re-deliver on every page load, so the answer is kept per tab. Reported as refreshing asks about the route that is already loaded.
-
Sent when the share connects, so a link opened before the aeroplane moves still has the plan, and re-sent when the route changes — a diversion agreed in the air reaches the people watching. Debounced, and skipped entirely when the plan has not actually changed.
-
Encrypted with the same key as the position: the relay carries a plan it cannot read.
-
Gzipped first, because the payload is retained and sits on a free public relay until the share stops — a 20-waypoint plan with comm changes goes from about 7.6 KB to 0.6 KB. The packet says which compressor it used, so a viewer never has to guess or agree.
-
Stopping clears it with the position. A link that no longer says where the aeroplane is must not still hand out where it was going.
-
A route too big for
followMeRouteMaxKbis not published at all rather than half-published, and nothing else about the share stops working.
The app being killed mid-flight used to kill the link — and nobody re-shares a link at 2000 feet. The session (topic and key) is kept on the device, and NavAid resumes it on the next start, with a toast saying so.
featureFollowMePersist is on by default, in the shipped code as well as in the gist:
"the same link every time" cannot depend on a network fetch succeeding. So the link belongs
to the device, and it is the kind that can live in a club WhatsApp group.
| App force-quit, phone rebooted, tab reloaded | Same link, sharing resumes by itself |
| You pressed Stop | Same link. Publishing stops and the last position is cleared, so the viewer goes blank — but sharing again revives that same URL |
| A month later | Same link. followMeResumeHr no longer expires it while persistence is on |
| A different identifier typed | Same link. The name is a label on the feed, not part of its address |
| You pressed 🔑 New link | A new topic and key. The old link stops working for everyone holding it |
| Cleared site data, another phone, another browser profile | A new link — the session lives only on this device |
The trade, stated plainly. A link handed to someone keeps working for every later flight from that device, under any name. Stopping does not take it back and renaming does not take it back. New link is the only thing that does.
Beside Follow me, and offered whenever this device holds a link — sharing or not, because thinking better of who has it usually happens on the ground.
It confirms first, and the confirmation says what is actually lost rather than asking whether you are sure: the link this device has been sharing stops working for everyone who has it, including anyone you meant to keep.
- While sharing — a fresh link is minted and handed straight to the share sheet or clipboard.
- While idle — the stored capability is thrown away; the next share mints a new one.
The identifier survives either way: it is the label, not the capability.
featureFollowMeNewLink withdraws the button on its own — for a fleet that shares one standing
link per aircraft and does not want it broken mid-season — without withdrawing Follow me.
followMeLinkPerName (off) puts the identifier back inside the capability, which is how this
worked before: a different name mints a different topic and key, so one aircraft's followers
can never be handed the next one's flight. Note what it does not do — one session is stored
per device, so returning to an earlier name mints a third link rather than recovering the first.
The Share dialog has a Public checkbox, off unless you tick it (remembered on the device). The same setting is a toggle under Share in the menu — Public (every NavAid pilot sees me) — which works during a share too: ticked, you appear publicly from your next position; unticked, you are taken off the public channel at once.
- Your link is unchanged. It still shows only you, to whoever holds it.
- Public also puts your aircraft on a public channel of the same relay: identifier, position, altitude, speed and track. Signed, not encrypted — public means public: anyone on the relay can read it. Stop clears it.
-
Extra layers → NavAid pilots → Show public NavAid pilots draws every public aircraft, with
its identifier and speed · altitude beside it (the icon points along its true track). They grow a
little as you zoom in. A silent aircraft fades after 2 minutes and says how long ago it was last
heard (
95 kt · 2500 ft · 4 min); after 30 minutes it goes. -
Seeing others needs sharing publicly yourself. Until you do, the switch is dimmed and says
so. The one exception is the secret viewing link,
https://navaid.supino.org/?pilots=<secret>: it shows everyone without sharing, and fits the map to them. The app holds only the secret's SHA-256 (publicPilotsViewHash), so a new secret is a gist change. - Your own aircraft is never drawn as someone else — not even an old one: the phone remembers its last public ids, so a position left behind by an earlier link does not come back as a stranger.
- Nobody can move your aircraft. Its public id is the fingerprint of its signing key, and a packet under that id signed by anyone else is refused. Anyone can publish an aircraft of their own — as on any public feed. At most 200 are drawn.
This is a rule inside the app, not privacy: the public channel is readable on the public relay. A share that ends without its Stop (a crash, a flat phone) leaves its last position on the relay; viewers age it out.
Tunables: featureFollowMePublic (on), publicPilotsViewHash, followMePublicZoomGrow (0.12),
followMePublicDimSec (120), followMePublicDropSec (1800).
| The relay | Sees encrypted bytes on an unguessable topic. It cannot read your position or your aircraft code |
| Anyone with the link | Sees everything the aeroplane sends. It cannot publish: the link carries the verify half of the signing key, never the signing half |
| Us | Nothing. There is no NavAid server in this path |
| Everyone (only with Public ticked) | Your identifier, position, altitude, speed and track, unencrypted, on the public channel — see Public share |
The position is encrypted (AES-GCM) before it leaves the aircraft. The key travels in the
link's fragment — the part after # — which browsers never send to any server. The topic
is 16 random bytes; the aircraft code is a label inside the encrypted payload, not part of the
address.
Treat the link like a key, because it is one. Share it with people, not with places — and because it now outlives the flight, the stop and the name, 🔑 New link is the lock change.
Every packet is signed. The aeroplane holds an ECDSA P-256 private key; the link carries the
public half beside the encryption key (#k=<encryption>&v=<verify>), and the viewer checks the
signature on every fix and every route before drawing it. A follower who re-sent the topic with
a position of their own would be signing with a key they do not have, and the other followers
would ignore it.
The signature travels inside the sealed object, not appended to it — appending would have broken AES-GCM for every link already in the air.
A device remembers the verify key it has seen for an aircraft (up to 32 of them, in
navaid.followVerified), so a link with the v= stripped out cannot quietly downgrade a
viewer that was already checking. followMeRequireSignedLinks (off) goes further and refuses a
link with no verify key at all; off by default so links minted before signing existed still
open.
Values are what the live app runs with — the shipped defaults as amended by the tuning gist.
| Key | Live value | What it does |
|---|---|---|
featureFollowMe |
true |
Whether sharing is offered at all |
featureFollowMePersist |
true |
Keep one link per device, forever. Off: a stop deletes it, and it expires after followMeResumeHr
|
featureFollowMeNewLink |
true |
Offer the 🔑 New link button |
followMeLinkPerName |
false |
On: a different identifier mints a different link |
followMeResumeHr |
12 |
Hours a stored session may be resumed — only while persistence is off |
followMeRateSec |
2 |
Seconds between published positions |
followMeStopMaxSec |
25 |
How long Stop waits for the relay before stopping here anyway |
featureFollowMeRoute |
true |
Share the route beside the position |
followMeRouteCompress |
gzip |
How the shared route is compressed: gzip, deflate-raw or off
|
followMeRouteMaxKb |
64 |
Largest route to publish, measured on what goes on the wire |
followMeRouteDebounceMs |
1500 |
Wait after a route edit before re-sending |
featureFollowMeShareWhileViewing |
false |
Let a page share while it is watching someone else |
followMeStaleSec |
30 |
When the viewer calls the feed stopped |
followMeBroker |
wss://broker.emqx.io:8084/mqtt |
The public relay |
followMePlanePx |
26 |
Follower's aircraft symbol size |
followMePlaneColor |
#182839 |
Follower's aircraft symbol colour |
Turning featureFollowMe off in the gist also ends a session that is already running — a
control that has just been hidden cannot be used to stop sharing. That only happens when the
gist actually arrives: a failed fetch in the air never reads as "the feature was withdrawn".
Stored on the device only, never synced to Drive: navaid.followMeSession (topic + key — a
capability, not a setting) and navaid.followMeCode.
Both need paho-mqtt and cryptography:
python3 -m pip install -r scripts/requirements-follow-me.txt
Print a feed as text — useful for a flight log, or for checking a link really is publishing before blaming the phone:
./scripts/follow-me-tail.py 'https://navaid.supino.org/?follow=<id>#k=<key>'
# 18:29:30 4X-TST 32.10000, 34.80000 2001 ft 95 kt 007° age 2s
./scripts/follow-me-tail.py --json '<link>' | tee flight.jsonl
./scripts/follow-me-tail.py --once '<link>'Fly a simulated route into a link, to see the viewer work without an aeroplane:
python3 scripts/follow-me-simulator.py # LLHZ → LLHA template
python3 scripts/follow-me-simulator.py route.json --speed-kt 120
python3 scripts/follow-me-simulator.py --speed-factor 10It prints the follower URL it is publishing to. Keep that URL private — its fragment carries the key, which grants publishing as well as viewing.
-
The link lost its fragment. Chat apps sometimes truncate at
#. Without#k=…there is no key and nothing can be decrypted.follow-me-tail.pysays so explicitly. - The pilot stopped sharing, or the app is not running. The banner says "waiting for a position".
- The aircraft has no signal. Publishing stops; the banner goes stale after 30 s.
For pilots
- Quick Start
- Video tutorials · סרטונים
- Features
- The Map
- Auto zoom
- Toolbar
- Phone layout
- Safety notice
- Route and Legs
- Waypoints and Notes
- Inspector
- AI Assistant
- User Guide
- Frequency Changes
- VOR and Radial/DME
- Map Layers
- Bearing dial
- Extra Layers
- Airport charts
- Flight Plan
- Route check
- Flight planning form
- Submit a flight plan
- Fuel
- Print and Export
- Simulator Connect
- Google Earth
- GPS Track Recording
- Comm failure
- Follow me
- Bilingual UI
- Offline and PWA
- Settings and Persistence
- Keyboard and Touch
- FAQ and Troubleshooting
For developers
- Architecture
- Directory Structure
- Tuning Panel
- Hidden Tools
- LocalStorage Schema
- Nav-waypoints dataset
- Airfields dataset
- Service Worker
- Deployment
- Contributing
- Changelog
🇮🇱 עברית
לטייסים
- התחלה מהירה
- תכונות
- עוזר AI
- מדריך למשתמש
- זום אוטומטי
- שינויי תדר
- VOR ו-Radial/DME
- סרגל הכלים
- פריסת הטלפון
- הודעת הבטיחות
- שכבות מפה
- שכבות נוספות
- תוכנית טיסה
- בדיקת מסלול
- טופס תכנון טיסה
- הגשת תוכנית טיסה
- הדפסה וייצוא
- חיבור סימולטור
- Google Earth
- הקלטת מסלול GPS
- תקלת קשר
- עקבו אחריי
- ממשק דו-לשוני
- לא מקוון ו-PWA
- הגדרות ושמירה
- מקלדת ומגע
- שאלות נפוצות ופתרון תקלות
למפתחים
🌐 Live 🧪 Staging 📦 Repo 💡 Ideas / bugs