# FuelBoard Relay Keyless proxy for the **UK Fuel Finder API**. Holds the GOV.UK OAuth 2.0 client credentials server-side so the FuelBoard iOS app and widget never embed a secret (secrets inside an IPA are extractable by anyone). ## Architecture ``` FuelBoard app / widget │ GET http://192.168.1.131:8788/api/v1/stations?fuel=e10&lat=..&lng=.. ▼ FuelBoard Relay (this repo, runs on the Mac Mini) │ OAuth 2.0 client_credentials grant (token cached ~1h) │ GET https://api.fuelfinder.service.gov.uk/v1/prices (snapshot, cached 5 min) ▼ UK Fuel Finder API (official gov.uk open data) ``` - Token refreshed automatically before expiry (60s buffer). - Station snapshot cached `FUEL_CACHE_TTL` (default 300s); stale cache is served on upstream failure so the app never sees a hard outage. - No credentials are exposed to clients — the app calls the relay keyless. ## Setup ```bash python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt cp .env.example .env # then fill in FUEL_API_CLIENT_ID / FUEL_API_CLIENT_SECRET ./scripts/run.sh ``` Without credentials the relay runs in **demo mode**: `/health` reports `demo_mode: true` and `/api/v1/stations` returns a clean 503 with a hint — useful for testing the app→relay path before credentials exist. ## Endpoints | Endpoint | Description | |---|---| | `GET /health` | Cache status, last sync, failure counters | | `GET /api/v1/stations` | All stations with prices (keyless) | | `GET /api/v1/stations?fuel=e10&lat=53.72&lng=-1.85&radius=10&limit=10` | Filter by fuel, radius around a point, sorted nearest-first | | `GET /api/v1/stations?fuel=diesel` (no lat/lng) | Sorted cheapest-first | Fuel grades: `e10`, `e5`, `diesel`. ## Getting credentials 1. Sign in at with GOV.UK One Login. 2. Register an application → get `client_id` + `client_secret`. 3. Put them in `.env` (never in git, never in the app). ## Fair use notes (from the Fuel Finder developer guideline) - Refresh at least every 5 minutes — we do (cron-style refresh on request + TTL cache). - Serve data unmodified — this relay only filters/sorts, never alters prices. - Keep timestamps — `price_updated` is passed through untouched. ## Integration point (FuelBoard app) `Shared/FuelPriceProvider.swift` has `FuelFinderProvider` stubbed with a `TODO`. When the relay is live, point it at: ``` http://192.168.1.131:8788/api/v1/stations?fuel=&lat=..&lng=.. ``` and map `stations[].price` / `stations[].distance_km` into `FuelStation`.