Everything the app does, from the outside. Nothing here needs a server unless a section says so.
Building it or changing it is DEVELOPERS.md; the shape of the system is the README.
Every screenshot below was taken on a phone running a throwaway profile: synthetic trips, invented saved places and a mocked GPS position. Nothing in them is a real location.
The screenshots are older than the text. They show the previous layout — a search pill pinned to the top, a spin dock above a Walk/Bike/Moto/Car mode bar. The words below describe the app as it is now; the images are being retaken. Where the two disagree, believe the words.
Grab the APK from the latest release and install it, or build it yourself (see DEVELOPERS.md). Min SDK 26 (Android 8.0).
There is an iPhone app too, sharing the same core — it has no release download, because putting an iOS build on a phone needs a paid Apple Developer certificate. See On iPhone. The screenshots below are all Android.
Releases from CI are signed with a key that a local build does not have, so you cannot install a debug build over a release one (or the other way round) without uninstalling first — which deletes your trips and fog. Stick to one source. The Play build counts as a third source: Play App Signing re-signs it with its own key, so it can’t be updated by a GitHub APK either.
Everything above works with no account and no server. Sign-in only buys you sync, friends, convoys, circles and a shared fog of war — and it needs a server you run, since the published APKs deliberately ship with none baked in. Point Settings → Servers & sync at your own — your server tells the app which sign-in realm to use, and nothing here needs a custom build. See Pointing the app at your server.

The map fills the screen. Everything else sits in two places: a small stack of buttons at the top, and a sheet along the bottom.
Over the map
0 km/h
while you are still. The posted limit appears beside it when the road has one.The bottom sheet
Drag the sheet’s handle down to get more map, or up for the full spin sheet.
Older builds put the search pill at the top of the screen and a Walk/Bike/Moto/Car mode bar along the bottom. Both are gone: search moved into the sheet, walking and cycling were dropped, and the remaining two modes live inside the spin sheet.

Tap Spin in the bottom sheet, or drag the sheet up, to get the full Spin the map panel. Every control below lives in it.
Mode — Moto or Car. It sets the radius range, which roads a spin may land on, and whether the result is a destination or a loop.
Unexplored — a toggle. On, the draw is biased towards ground your fog of war hasn’t uncovered yet, so spinning sends you somewhere new rather than down the road you take every day.
Destination type — what the spin should aim at:
| Type | What it lands on |
|---|---|
| Road | Any road the mode is allowed on — the default lucky-dip |
| Viewpoint | OSM tourism=viewpoint |
| Food & drink | Cafés, restaurants, pubs, bars, ice cream |
| Sight | Castles, ruins, monuments, forts, memorials, attractions |
Radius — how far out to look, as the crow flies. Each mode has its own range and picks road types to match:
| Mode | Radius | Default | Roads it uses |
|---|---|---|---|
| Moto | 30–400 km | 120 km | The rural network — see round trips below |
| Car | 5–100 km | 25 km | Everything up to and including motorways |
Min distance — a floor, so a 100 km car spin can’t drop you three streets away. Leave it at Off for a true random draw.
Direction — bias the draw towards one of the eight compass sectors, or Any. Useful when the coast is one way and you’d rather not be sent into it.
Spin fires the draw. It samples a random sub-area of your circle rather than downloading every road inside it, which is what keeps a 400 km moto spin quick. Tap it again while it’s running to cancel.
Two buttons sit under it:

A spin returns three candidates, each routed, with distance and drive time. They are all drawn on the map as lettered pins — tap a pin or a row to commit to one. Reroll draws three new ones; Cancel drops them and leaves the map as it was.
Picking one draws the route and leaves the destination pinned. From there you can save it as a shortcut with the Save pin chip.
Moto mode doesn’t hand you a destination — it builds a loop. The slider sets total trip length, and the spin returns a ride out through the curviest roads around you and back to where you started.
Curviness is junction-aware: turn radius is estimated per vertex triple from the road geometry, and vertices that sit at intersections are excluded — so a left turn at a crossroads doesn’t score as a “curve”, only sweeping bends within a road do. The loop is handed to Google Maps as a multi-waypoint route, or driven in-app like any other route.
With a routing server configured, the loop is a single request that comes back following real roads. Without one, the app plans an approximate loop from your server’s own road data and says so.

Go, in the spin sheet, offers:
Pick one and the app remembers it: Go launches it directly next time. Long-press Go to bring the chooser back. Settings → Navigation shows the current choice — “Go currently launches: …” — and resets it.

In-app navigation shows the next maneuver and the distance to it, a then pill for the maneuver after that (so a turn-then-turn doesn’t ambush you), and a bottom bar with remaining distance, remaining time, arrival clock time and a progress track. Leave the line and it reroutes; while it’s off the route the bar says so. The road behind you fades as you drive it, so what’s left of the route is the part that stands out — in whichever colour you set under Settings → Appearance & map → Route line.
Your speed sits bottom-right with the posted limit beside it, and goes red when you’re over. Speed cameras — fixed cameras and Belgian trajectcontrole sections, both from OpenStreetMap — are drawn on the map; a chime warns when one is ahead and you’re over the limit. Inside an average-speed section, the running average for that section is shown next to your live speed, since that is the number the camera pair actually judges.
Navigation can avoid motorways or avoid narrow rural lanes — see Settings reference.

Two ways in:
A live card shows elapsed time, distance, top speed and — depending on the vehicle — max lean angle and cornering g. On a moto both are recorded; in a car only g.
Vehicle auto-detect: assign paired Bluetooth devices to a vehicle (an intercom to the moto, the car’s infotainment to the car) and a trip logs under that vehicle whenever the device is connected. With nothing connected, a trip that never picks up real driving pace is dropped rather than saved. These are Bluetooth Classic bonds, so there’s no scanning and no location permission involved — only connect/disconnect.
If a trip is filed under the wrong vehicle, fix it afterwards from the history list; false-positive detections can be deleted outright.
![]() Unexplored ground stays covered |
![]() The eye button turns it off |
Everywhere you have been is uncovered on the map, permanently. Everywhere else is under a scrim. The reveal radius around your track is configurable (200 m by default) under Settings → Fog of war, and the whole overlay can be switched off with the eye button when you just want to read the map.
Reset explored area wipes it and starts you back at nothing.
With Share fog with friends on, accepted friends’ explored ground is drawn alongside yours, and yours alongside theirs. It’s off by default and strictly reciprocal: the server only hands you a friend’s traces while you are sharing your own.
![]() Search |
![]() Saved places |
Search runs against Photon and streams suggestions as you type — no search button. Recent picks stay on top of the list, then live results, ranked with nearby hits first. Tapping a result drops it as the destination and moves the map there.
Saved places are named shortcuts, listed under You → Saved places — empty at first, prompting you to add Home, Work “or anywhere you stop often”. Add one after dropping or spinning a destination, or from that screen directly.
A saved place becomes a chip on the bottom sheet’s chip row via the + button next to Spin; one tap then makes it the current destination.
A spin gives you one destination. A route is the other way round: stops you chose, in the order you want them, kept for later. You → Routes lists them, each with its stop count, distance and time.
Unlike trips and fog, routes are not part of sync: they live on the phone, and a shared one only leaves it when you send it to someone. Export the ones you want to keep before a reinstall.
![]() You |
![]() Logbook |
![]() Badges |
The avatar in the search field opens You. At the top: your name, a
Profile & account link, and four lifetime totals — kilometres, rides,
places (municipalities entered) and badges earned. Under them, everything else
hangs off one list: Logbook, Routes, Saved places,
Badges & coverage (with your count, e.g. 8 / 22) and Social. Settings
is the gear in the top right.
Logbook tells your rides as a story, newest first. Each month is a chapter: the current one draws all its routes on one map with a one-line summary (rides, kilometres, new places), and older months fold to that one line until you tap them. A ride shows its route large and a title built from the towns it passed, the weekday and the time of day (“Sunday afternoon ride through Ronse and Kluisbergen”); a car trip gets a plain “Gent to Aalst”. At most one chip marks what stood out: a first, new places, the longest ride yet, or the twistiest of the month. Badges appear between the rides they were earned among. Filter by All, Moto or Car at the top. The ⋮ menu on a ride lets you rename it, change vehicle (for a misclassified trip), share its card or delete it. Renamed titles stay on this phone; they don’t sync.
Tapping a ride opens it: the route as a map with its highlights pinned (deepest lean, top speed, hardest corner, hardest braking, and the twistiest 5 km drawn in green) — tap a highlight to fly to it — then the places passed, new ones marked, and the road mix in plain words. Replay plays the ride back on the map. Deep dive folds out every number: overview, speed over distance and time in each speed band, cornering (lean left vs right, time per lean band), each hard event with its place and kilometre, 25 km splits with the twistiest highlighted, road classes, stops, engine data from an OBD2 adapter, and how the ride was recorded.
Badges track five categories — Distance, Top speed, Single ride, Places and
Coverage — with progress shown on the ones you haven’t earned yet. Coverage is
how much of a municipality’s road network you’ve actually driven, resolved from
OSM admin_level=8 boundaries; “Places” counts municipalities entered at all.
Social is the hub for everything involving other riders, and all of it needs an account on a sync server. It holds two entries: Friends and Circles.
Friends carries the Leaderboard and Convoys on one screen. Once signed in you can add friends and compare totals, rides and badges. Friends never see your trips or your map — only totals and badges, plus your fog if you have opted into sharing it.
Convoys are for riding together, and start with New convoy on that same screen: everyone in one sees the others move on the map in real time, and can share a spin so the group votes on where to go. A convoy is per-ride — it exists while you’re in it and is gone when the last member leaves. Push-to-talk is currently off — the relay accepts push-to-talk frames and drops them; everything else in a convoy works. The in-app copy still offers push-to-talk when you create one; that text is ahead of the server.
Circles are the long-lived counterpart: family or roommates rather than a ride. The Circles screen lists each one with its members and a Sharing state. A circle doesn’t end when you stop driving, never carries voice, and shows each member’s last known position — posted every couple of minutes, so it reads as “last seen”, not a live trail. Share a saved place into one and everyone sees arrivals and departures there; the geofence is worked out on your own phone, so the stream of fixes behind it never leaves it. Sharing is per person per circle and pausable at any time.
Both are invite-only and only ever from someone you’re already friends with. CIRCLES_AND_CONVOYS.md covers how the two share one mechanism, and documents the live protocol behind them.

Signing in happens in a browser, on your server’s own sign-in page. Tapping sign in opens a Custom Tab at the server’s identity provider (Keycloak); you enter your details there and it hands the app back a token. Detour never sees your password, and there is no password form, no registration form and no invite-code box in the app — accounts, resets and who may register all live in the realm’s own pages.
The same account drives trip/trace sync, so a reinstall restores your history and fog from the server.
![]() Settings |
Tracking & vehicles |
Settings is the gear in the top right of You. It is an index of six screens in four groups, each row showing its current state underneath.
Tracking & vehicles
Navigation
Appearance & map
1.2 or 1,2. Governs distances, g, fuel
economy, mount offset and map zoom. Speeds round to whole km/h so they never
show one, and map coordinates always use a point so a latitude/longitude pair
stays readable.Fog of war
Displays & media
OBD2 adapter
Servers & sync — three parts.
Status shows the server you are pointed at, whether you are signed in and when sync last ran (“Signed in as … · synced 6m ago”), and whether a server config file is ready to export.
Actions are Sync now, Export config and Import config — the last two move your whole server setup between devices, so you type it once.
Server takes one address covering routing, search, sync and live. Show
advanced splits it per service, for a deployment spread across hostnames;
anything left empty falls back to the single address. The screen explains the
one case where a single address cannot work: the API answers /api/trips and a
Photon search server answers /api/, so those two cannot share a host.
Every address — the API and its per-service overrides — can be typed into Settings → Servers & sync at runtime, on Android and iOS both. The sign-in realm is not typed: the API server announces it, and the app only switches to a newly announced realm once that realm answers, so a misconfigured server cannot sign you out of a working one. Nothing has to be baked into a build for sign-in to work; changing the realm signs the device out immediately, because tokens issued by one realm mean nothing to another.
Settings → Servers & sync takes one server address covering routing, search, sync and live. Show advanced splits it per service for a deployment spread across hostnames; anything left blank falls back to the single address.
| What it points at | Without it |
|---|---|
| The Detour API (sync) | No account, no sync, no friends, circles or convoys. Everything else still works. |
| Your Keycloak realm (sign-in) | Sign-in cannot start. |
| Your GraphHopper (routing) | “Navigate in app” is absent, and spin candidates show straight-line distance. |
| Your Photon (search) | Search silently uses the public photon.komoot.io. |
One address cannot serve both sync and search: the API answers /api/trips and
a Photon server answers /api/, so those two need separate hosts. The advanced
fields exist for exactly that.
The APKs published on the releases page are built by CI with no server configuration baked in, deliberately: a public release should not ship someone else’s server address. That does not disable sign-in, only preconfigure it.
Standing those services up is ../README.md and ../backend/INSTALL.md.
Android Auto gets a car-sized spin: pick a radius, spin a destination, and drive it turn by turn on the head unit, with the same map, speed readout and camera warnings as the phone. Search works there too.
One catch, and it is Google’s rather than the app’s: a real head unit only lists apps built on the Android for Cars App Library when they were installed from Google Play. The Desktop Head Unit accepts a sideloaded APK, a car never does. ANDROID_AUTO.md covers the Internal App Sharing route and how to debug the car screen.
A SwiftUI app in iosApp/ runs on the same core as the Android one: map and
spin, trip recording in the background, history with GPX export, badges, saved
places and in-app turn-by-turn with spoken directions.
Sign-in works on iPhone. It moved to the identity provider’s own page in a
browser, and iOS supplies its half of that — ASWebAuthenticationSession plus
SecRandomCopyBytes — the same way Android supplies a Custom Tab plus
SecureRandom; the authorization-code-with-PKCE flow itself is shared
(shared/.../data/Oidc.kt). That unblocks everything that was gated on an
account: sync, friends and the leaderboard, convoys, circles and the group
spin.
Be as honest here as IOS_PORT.md is: what has actually been
exercised is narrower than “works” sounds. The shared flow has unit tests, the
Android side has been driven on a real device, and the iOS side is verified only
as far as CI’s ios.yml reaches — it compiles, boots the simulator and gets
screenshotted. CI cannot reach a private Keycloak, so nobody has yet watched an
iPhone finish the browser leg against a real realm. Treat that leg as untested,
not working, until someone has.
Two things are Android-only and are not coming to iOS: Android Auto (CarPlay navigation needs an entitlement Apple grants on application, and routinely refuses for hobby apps) and now-playing media on an external display (iOS exposes no equivalent).
Where the platforms behave differently — trip auto-detection, how lean and g are measured, guidance audio ducking — the reasoning for each is in IOS_PORT.md.
There is no download. CI builds a simulator app and an unsigned .ipa on
every change, but signing one for a real phone needs a certificate from a paid
Apple Developer account, which no CI trick removes.
Even without a sync server, a few features talk to the network by design:
your configured Detour server sees the spin center and radius you choose,
OpenFreeMap’s tiles see your current map viewport, and address/place search sends your query (and an
approximate location, to rank nearby results first) to Photon — your own
instance if you’ve set one in Settings, otherwise the public
photon.komoot.io. To give a spin destination or a route stop with no name
(tapped onto the map or imported from GPX) a name instead of coordinates,
Detour also sends that point’s exact coordinates to the same Photon. If you self-host Photon, search and naming fall back to the public
instance only when yours is unreachable, and only if you leave “Fall back to
public search” turned on; turn it off to keep search on your own hardware even
when your instance is down.
Android backup copies your trips to Google Drive. Detour opts in to Android’s automatic backup, so if backup is on for your phone, your trips and their full GPS traces (which show where rides start and end — usually home and work), explored area, badges, saved places and routes go to your Google account’s backup storage. Sign-in tokens, server credentials and recent searches are left out. There is no in-app switch; turn off backup for the phone, or for Detour where your phone allows it, to keep trips out of Google Drive.
A dashboard key in a URL is readable by whoever sees that URL. A dashboard
key (Home Assistant) is read-only, but it
reads your rides and full traces. The server accepts it as ?key= because an
iframe cannot send a header, and a query string lands in proxy and CDN logs,
browser history and the dashboard’s config. Send it as the X-Api-Key header
wherever you can, and revoke a key that has been in a URL you no longer control.
Circles are the one feature where the server keeps a position. Everything else is either never uploaded or uploaded as a record only you can read — a convoy’s live feed is relayed between open sockets and never written down. A circle stores one row per member: your latest fix, overwritten in place, no history and no trail. It exists only for circles you joined, only while that circle’s sharing switch is on, and pausing is enforced by the server rather than trusted to the app. Leaving a circle deletes it.
The published privacy policy is privacy.html.