# bike-app A self-hosted cycling app: syncs rides from a **Bryton Rider 650**, tracks mileage like Strava, and adds a **spare-parts inventory** and a **maintenance record** with mileage-milestone reminders. **Status: planning complete, no code written yet.** ## Why Today the Rider 650 syncs over Bluetooth to the Bryton Active phone app, which forwards to Strava — but Active has no background sync, so you have to remember to open the app. Research found a better path that removes the phone entirely: ``` ride ends -> Rider 650 joins home Wi-Fi (Main Menu -> Data Sync) -> uploads to Bryton cloud -> this app's poller fetches the ORIGINAL FIT file -> rides, wear tracking, and push reminders ``` That's also *higher fidelity* than the current route — Strava's API can only ever return smoothed streams, never the original file. ## Docs | File | What's in it | |---|---| | [`docs/PLAN.md`](docs/PLAN.md) | The full implementation plan: stack, schema, ingestion pipeline, auth, notifications, phased roadmap, CI/CD, risks, verification | | [`docs/RESEARCH.md`](docs/RESEARCH.md) | Raw findings: the Bryton cloud protocol (endpoints, headers, auth), FIT library comparisons, maintenance interval tables, self-hostable geo services, Gitea Actions gotchas | | [`docs/DECISIONS.md`](docs/DECISIONS.md) | Every decision taken, what was rejected, and why | ## Planned stack Python 3.12 / FastAPI / SQLAlchemy async / PostgreSQL 16 + PostGIS, `procrastinate` for jobs, SvelteKit static SPA as an installable PWA, MapLibre GL, all behind Caddy in Docker Compose. Source control and CI in self-hosted Gitea with an act_runner on the same box. Four containers in v1, under 2GB RAM. ## Next steps 1. **Verify on the Rider 650:** does `Main Menu -> Data Sync` upload *automatically* on joining Wi-Fi, or only on manual trigger? This determines how completely the phone leaves the loop. 2. **Plug the 650 in over USB** and `ls -R` the mounted volume to confirm the real `.fit` path (documented as `Bryton/Activities/`, but worth confirming). 3. **Grab a real `.fit` file** from it and run it through `fitdecode` — Bryton's encoder is not Garmin's, and the schema should be checked against reality before it's written. 4. Then Phase 0: scaffolding and CI (see the roadmap in `docs/PLAN.md`).