Privacy-First Scenic Route Planner
Astray
A scenic-route planner built on a self-owned routing engine. The driver gives a destination and a time budget ("I'll spend up to 20 extra minutes"), and Astray returns the most scenic route that fits, then hands off to their usual navigation app. No accounts, no tracking, and no third-party routing or geocoding service anywhere in the product.
The Challenge
Every off-the-shelf router treats preferences as penalties: "avoid highways" taxes a road class globally, per edge, with no awareness of alternatives or remaining detour budget -- which is why it sends you on a four-hour back-roads odyssey to dodge a bridge you cannot avoid. Modeling what a driver actually wants ("make my spare 20 minutes count") meant building a routing engine from scratch around a problem no shippable engine implements, and doing it without collecting a single byte of user data.
Our Approach
- 1Inverted the routing model as the Arc Orienteering Problem: maximize accumulated scenic reward subject to a travel-time budget. Scenic roads earn reward while the unavoidable bridge costs only its honest time, so the "is there an alternative here?" intelligence emerges from the economics instead of being hand-coded.
- 2A pure-Rust routing core (no I/O, no network, no threads) running a Lagrangian price search over a non-negativity-safe cost function, hardened with adaptive hull tracing and scenic-attractor via-routing after measuring that the textbook relaxation returned identical routes at +25%, +50%, and +75% budgets.
- 3Scenery baked as a vector: 14 independent quantized signals per road segment (bends, water, forest, remoteness, gradient, and so on) collapsed at query time by signed weight vectors. "Wander," "cruise," and gentle-passenger moods ship as data files, and personalization never requires re-processing the map.
- 4Privacy as architecture: a trust ladder from an auditable public instance (stateless, logs nothing, pinned to a released tag) down to client-only WASM routing in the browser, plus a self-baked geocoder, because autocomplete leaks the most sensitive strings a user types.
AI & Technical Highlights
Budget-Constrained Optimization
An Arc Orienteering Problem solver: Lagrangian relaxation with adaptive convex-hull tracing, via-routing to fill the duality gap, and edge-set diversity selection over candidate routes.
Self-Owned Data Pipeline
OpenStreetMap plus elevation data baked into a zero-copy graph blob: a columnar feature store compiled to a packed runtime artifact, with 14 quantized signal columns riding on every edge.
Global Tiling
A fixed-origin quadtree that keeps subdividing until every tile holds roughly the same number of road segments (~100k), so a dense metro and open desert alike produce evenly sized tiles, and independently baked regions stitch cleanly at their shared edges.
On-Device Personalization
A familiarity overlay computed on the phone from explicitly recorded trips only. Deterministic, never uploaded, and rejected by the server tier in code, so the privacy boundary lives in the type system.
Technologies
Technical deep dive
Astray runs a budgeted reward-maximization search over a self-baked road graph. The same pure Rust core compiles into a stateless server and into WebAssembly running entirely in the browser, so the most private tier is a recompile rather than a rewrite. The interesting engineering lives in three places: making a time budget actually buy scenery (the textbook relaxation quietly returns the same route for every budget), teaching a destination-seeking router to plan loops that end where they started, and baking taste into map data without deciding what "scenic" means for everyone.
Deep Dives
The full write-ups live on Astray's engineering wiki. Each one starts in plain language and works down to the algorithms and the measured numbers.
How a time budget becomes a route ↗
The Arc Orienteering framing, the Lagrangian price search, and the duality-gap failure that made the budget knob a no-op until hull tracing and via-routing closed it.
The loop problem: ending where you started ↗
"Take me out for an hour and bring me home" has no destination, and the fastest answer is to never leave. Out-and-back via attractors, anti-hairpin penalties, and overlap rejection.
What counts as scenic, and who decides ↗
The 14 measured ingredients of a good road, why bendiness and sharpness are separate signals, and why the system measures ingredients instead of issuing verdicts.
Tuning the drive: how custom moods work ↗
Moods as editable weight sets over the signal basis, the 15-band equalizer they surface as, and the threshold veto that keeps the gentle mood from trading comfort away.
Teaching a computer what a road feels like ↗
The bake: OpenStreetMap and elevation data scored, quantized, and compiled into stitchable quadtree tiles, with a columnar feature store so new signals join without a rebuild.
Private by architecture, not by promise ↗
The trust ladder from a stateless public instance to routing fully on-device, the no_std core that makes it cheap, and the measured numbers behind the design.
Deliberate Tradeoffs
Astray routes on free-flow speeds because live-traffic APIs are a location beacon; the fix is an optional field where the user types the ETA their maps app already shows, and the budget anchors to that number. The textbook Lagrangian search shipped first and failed on real corridors -- measured on one SF Bay Area trip, no route existed between 80 and 116 minutes, so every budget in that band returned the byte-identical route. Hull tracing plus via-routing closed the gap at a cost the latency budget could afford, where an exact resource-constrained search could not.