Defer V3-08/V3-09, add V3-17 to investigate self-hosted OSRM

Per request: V3-08's routing-engine direction is decided (self-hosted OSRM over a hosted API or the public demo instance) but standing one up is split into its own ticket, V3-17, since provisioning a server is different work from the app code that calls it. V3-08 and V3-09 are marked Deferred pending V3-17.

V3-17 also names a real tension worth flagging rather than quietly ignoring: this backlog's own organizing principle is 'v3 is everything buildable with no server,' and a self-hosted OSRM instance is a server -- so V3-08/V3-09 sit oddly under docs/v3/ once V3-17 exists. Left unresolved by design (a documentation call for later), noted in both V3-17 and BACKLOG.md's v4 section rather than silently renumbering tickets that are already cross-referenced throughout the docs.
This commit is contained in:
2026-08-17 19:56:44 -05:00
parent 1bcc6f1c5a
commit e2ccd4b250
5 changed files with 117 additions and 6 deletions

View File

@@ -252,6 +252,14 @@ Split out from v3 deliberately. These three are **one programme, not three items
needs a server, an identity system, and a privacy stance, and none of them is worth needs a server, an identity system, and a privacy stance, and none of them is worth
building alone. Nothing in v3 depends on any of them. building alone. Nothing in v3 depends on any of them.
**A fourth item that doesn't fit that programme but shares its precondition:**
self-hosted road-snapped routing (v3's V3-08/V3-09, gated on
[V3-17](v3/V3-17-osrm-hosting.md)). It needs a server the same way this section's three
items do, but nothing about identity, privacy, or the group-ride use case — it's routing
infrastructure, not a rider-facing programme. Filed under `docs/v3/` for now since that's
where the tickets it unblocks already live; flagged here because "v3 is everything
buildable with no server" stops being strictly true the moment V3-17 is picked up.
## Live group rides ## Live group rides
Invite someone to a ride and see both of you on the map, live. Invite someone to a ride and see both of you on the map, live.

View File

@@ -24,8 +24,8 @@ backup are v4 — see [../BACKLOG.md](../BACKLOG.md).
| [V3-05](V3-05-mounted-mode.md) | Mounted (handlebar) mode | M | V3-04 | Done | | [V3-05](V3-05-mounted-mode.md) | Mounted (handlebar) mode | M | V3-04 | Done |
| [V3-06](V3-06-notification-stats.md) | Live stats in the notification | S | — | Done | | [V3-06](V3-06-notification-stats.md) | Live stats in the notification | S | — | Done |
| [V3-07](V3-07-route-drawing.md) | Route drawing (pins, straight lines) | M | — | Done | | [V3-07](V3-07-route-drawing.md) | Route drawing (pins, straight lines) | M | — | Done |
| [V3-08](V3-08-road-routing.md) | Road-snapped routing and ETA | L | V3-07, V3-01 | Not started | | [V3-08](V3-08-road-routing.md) | Road-snapped routing and ETA | L | V3-07, V3-01, **V3-17** | Deferred (needs V3-17) |
| [V3-09](V3-09-route-following.md) | Follow a planned route | M | V3-04, V3-08 | Not started | | [V3-09](V3-09-route-following.md) | Follow a planned route | M | V3-04, V3-08 | Deferred (needs V3-08) |
| [V3-10](V3-10-trip-splitting.md) | Trip splitting | S | — | Done | | [V3-10](V3-10-trip-splitting.md) | Trip splitting | S | — | Done |
| [V3-11](V3-11-offline-tiles.md) | Offline tile pre-download | M | V3-04 | Partially done (pipeline shipped; needs aeroplane-mode device verification) | | [V3-11](V3-11-offline-tiles.md) | Offline tile pre-download | M | V3-04 | Partially done (pipeline shipped; needs aeroplane-mode device verification) |
| [V3-12](V3-12-crash-reporting.md) | Crash reporting | S | — | Partially done (code only; needs a real Sentry DSN + release build) | | [V3-12](V3-12-crash-reporting.md) | Crash reporting | S | — | Partially done (code only; needs a real Sentry DSN + release build) |
@@ -33,6 +33,7 @@ backup are v4 — see [../BACKLOG.md](../BACKLOG.md).
| [V3-14](V3-14-gpx-interop.md) | GPX interoperability | S | V3-01 | Partially done (code only; needs real-device verification) | | [V3-14](V3-14-gpx-interop.md) | GPX interoperability | S | V3-01 | Partially done (code only; needs real-device verification) |
| [V3-15](V3-15-auto-pause.md) | Auto-pause | M | V3-13 *(gated)* | Not started | | [V3-15](V3-15-auto-pause.md) | Auto-pause | M | V3-13 *(gated)* | Not started |
| [V3-16](V3-16-visual-identity.md) | Visual identity | M | V3-04, V3-05 | Partially done (token-level identity shipped; needs outdoor device verification) | | [V3-16](V3-16-visual-identity.md) | Visual identity | M | V3-04, V3-05 | Partially done (token-level identity shipped; needs outdoor device verification) |
| [V3-17](V3-17-osrm-hosting.md) | Self-hosted OSRM: investigate and stand one up | M | — *(needs a server — see the ticket's own note on the v3/v4 boundary)* | Not started |
## Dependencies ## Dependencies
@@ -40,15 +41,20 @@ backup are v4 — see [../BACKLOG.md](../BACKLOG.md).
V3-01 ──┬────────────► V3-08 ──► V3-09 V3-01 ──┬────────────► V3-08 ──► V3-09
└──► V3-14 ▲ └──► V3-14 ▲
V3-07 ──────► V3-08 │ V3-07 ──────► V3-08 │
V3-17 ──────► V3-08 │
V3-02 ──► V3-03 │ V3-02 ──► V3-03 │
V3-04 ──┬──► V3-05 ──┬───────┘ V3-04 ──┬──► V3-05 ──┬───────┘
├──► V3-11 └──► V3-16 ├──► V3-11 └──► V3-16
└──► V3-09 └──► V3-09
V3-13 ──► V3-15 (gate: may close unbuilt) V3-13 ──► V3-15 (gate: may close unbuilt)
no dependencies: V3-01 · V3-02 · V3-04 · V3-06 · V3-07 · V3-10 · V3-12 no dependencies: V3-01 · V3-02 · V3-04 · V3-06 · V3-07 · V3-10 · V3-12 · V3-17
``` ```
**V3-08/V3-09 are deferred, on request**, pending V3-17 (self-hosted OSRM). See V3-17's
own note on why that also puts them in tension with this document's v3/v4 boundary —
unresolved by design, not an oversight.
## Three that carry more weight than their size suggests ## Three that carry more weight than their size suggests
**V3-01** ships the port's **first real migration**. The destructive fallback is gone, so **V3-01** ships the port's **first real migration**. The destructive fallback is gone, so
@@ -56,7 +62,9 @@ getting `addColumn` plus a v1-database test right matters more than the feature
V3-07 and V3-09 both add migrations behind it. V3-07 and V3-09 both add migrations behind it.
**V3-08** forces a routing-engine decision with ongoing cost and vendor implications. **V3-08** forces a routing-engine decision with ongoing cost and vendor implications.
Behind a `RoutingService` interface, mirroring what `LocationSource` did for GPS. Behind a `RoutingService` interface, mirroring what `LocationSource` did for GPS. The
direction is decided (self-hosted OSRM); V3-17 does the actual standing-up, and V3-08 is
deferred until it exists.
**V3-13** is not code. It answers the three questions that have been open since v2, and **V3-13** is not code. It answers the three questions that have been open since v2, and
**V3-15 may close unbuilt** as a result — a legitimate and probably likely outcome. **V3-15 may close unbuilt** as a result — a legitimate and probably likely outcome.

View File

@@ -1,6 +1,7 @@
# V3-08 — Road-snapped routing and ETA # V3-08 — Road-snapped routing and ETA
**Phase** Route planning · **Depends on** V3-07, benefits from V3-01 · **Size** L · **Status** Not started **Phase** Route planning · **Depends on** V3-07, benefits from V3-01, **blocked on V3-17**
· **Size** L · **Status** Deferred
## Goal ## Goal
Resolve the actual shortest path along roads between pins, and estimate how long the ride Resolve the actual shortest path along roads between pins, and estimate how long the ride
@@ -11,6 +12,12 @@ This is what Dylan asked for. It is also the ticket with a **decision that canno
deferred**: road-snapped routing needs a routing engine over OpenStreetMap data, and the deferred**: road-snapped routing needs a routing engine over OpenStreetMap data, and the
choice has ongoing consequences. choice has ongoing consequences.
**Decided, not deferred:** self-hosted OSRM (see the table below). What's actually
deferred is standing one up — that's [V3-17](V3-17-osrm-hosting.md), split out on
request because provisioning a server is a different kind of work from building the app
code that calls it, and this ticket cannot start until V3-17 has something to point
`RoutingService` at.
## Design ## Design
### Choosing an engine — decide before writing code ### Choosing an engine — decide before writing code

View File

@@ -1,6 +1,6 @@
# V3-09 — Follow a planned route while riding # V3-09 — Follow a planned route while riding
**Phase** Route planning · **Depends on** V3-04, V3-08 · **Size** M · **Status** Not started **Phase** Route planning · **Depends on** V3-04, V3-08 (itself blocked on V3-17) · **Size** M · **Status** Deferred
## Goal ## Goal
Pick a saved route before starting, see it on the live map underneath your actual track, Pick a saved route before starting, see it on the live map underneath your actual track,

View File

@@ -0,0 +1,88 @@
# V3-17 — Self-hosted OSRM: investigate and stand one up
**Phase** Infrastructure · **Depends on** nothing · **Size** M · **Blocks** V3-08, V3-09 ·
**Status** Not started
## Goal
Get a self-hosted OSRM instance running somewhere, so V3-08 (road-snapped routing and
ETA) has a real backend to build against instead of a deferred decision.
## Context
V3-08 named the choice of routing engine as "a decision that cannot be deferred," and
[the conversation that spawned this ticket](../v3/README.md) picked a direction without
picking a provider: **self-hosted OSRM**, over a hosted API (GraphHopper, Mapbox
Directions) or the public OSRM demo (explicitly not for production use).
**This ticket exists because that decision itself has a wrinkle worth naming up front:**
this whole v3 backlog's organizing principle, stated in `docs/BACKLOG.md`, is *"v3 is
everything that can be built with no server. v4 is everything that cannot."* A
self-hosted OSRM instance is a server. Strictly, that makes V3-08 and V3-09 — anything
that depends on this ticket — v4 work by the project's own definition, not v3, even
though they're filed under `docs/v3/` today and the routing itself has nothing to do
with the group-rides/accounts/backup programme that currently defines v4. Whether to
formally renumber them is a documentation decision for whoever picks this up next; this
ticket does not resolve it, only flags it so it isn't silently glossed over.
## Design
Two separable questions:
1. **Where does it run?** A small VPS (the same shape of box that would eventually host
the v4 group-ride server, so this could double as an early step toward that) versus
something serverless/managed. OSRM's own Docker image is the standard path either way.
2. **What data does it need?** A regional OSM extract, not the planet — start with
whatever region actually gets ridden (per `docs/LAUNCH.md`, this is presently a
friends-and-family app, so the region is small and known). [Geofabrik](https://download.geofabrik.de/)
publishes regional `.osm.pbf` extracts sized for exactly this.
Profiles matter for this app specifically (see V3-08's Design section): a motorcycle
route and a bicycle route between the same two pins should genuinely differ. OSRM ships
car/bike/foot profiles out of the box; a motorcycle profile is closer to car (mostly
avoids the walk-only restrictions bike profiles impose) but might want the twisty-road
preference a stock car profile doesn't have reason to express. Confirming that is part of
this ticket's investigation, not something to guess at now.
## Implementation
1. Pick and provision a host (see Design's first question)
2. Download and preprocess a regional extract with OSRM's own toolchain
(`osrm-extract` → `osrm-partition` → `osrm-customize`, or the older
`osrm-contract` pipeline depending on the OSRM version chosen)
3. Run `osrm-routed` behind whatever the host offers for TLS termination — the app will
be calling this over the public internet from riders' phones, so plain HTTP is not
an option
4. Confirm at least a car-equivalent and a bike profile both return sane routes for a
handful of real local pin pairs, by hand, before writing any app code against it
5. Write the resulting base URL and auth (if any) down for V3-08 to consume — as
configuration, never a hardcoded value, matching V3-08's own "no API key committed to
the repository" acceptance criterion, which applies here too even though there's no
third-party vendor to protect a key from — an open, unauthenticated routing endpoint
is still worth not publishing in a public repo
6. A basic uptime check of some kind — this becomes a real dependency the app relies on,
not a fire-and-forget script
## Acceptance criteria
- [ ] An OSRM instance is reachable over HTTPS from outside the host network
- [ ] Returns a road-following route for a real pin pair in the region actually ridden
- [ ] At least two distinct profiles (car-equivalent, bike) both work
- [ ] The endpoint and any credentials live in configuration, not source
- [ ] Documented: what's running, where, how to update the extract when it goes stale,
and what it costs (if anything) to keep running
## Tests
Infrastructure, not app code — no `flutter test` coverage belongs to this ticket
directly. V3-08's own `FakeRoutingService`-driven tests are what verify the app's
behavior; this ticket's job is only to make the real thing exist for that fake to stand
in for.
## Risks
- **Ongoing hosting cost and maintenance**, however small — this is the first piece of
always-on infrastructure this project has taken on. Worth being honest that "no
server" stopped being true the moment this ticket is picked up, regardless of which
numbering bucket it ends up filed under.
- **OSM extracts go stale.** A road that didn't exist at extract time won't route.
Needs a refresh cadence, not a one-time setup.
- Regional extracts are cheap; do not reach for a planet-wide extract preemptively.
## Out of scope
Actually building V3-08/V3-09 against this once it exists — that's their ticket, not
this one. Turn-by-turn navigation, traffic-aware routing, anything beyond what stock OSRM
gives you.