Add monorepo scaffolding and project documentation
Sets up the Nuvolari monorepo layout (app/, backend/, docs/, tool/) with the groundwork that does not depend on the Flutter toolchain: gitignore covering Flutter, Python and every secret file shape; env.example.json as the template for --dart-define-from-file; a verification script that skips stages whose target does not exist yet so it is runnable from day one; and a CI workflow in GitHub Actions syntax so it runs unchanged on Gitea or GitHub. The documentation records facts verified against the live services rather than restated from the brief. Two of them change the design: - DPC VMI rasters are 1200x1400 Float32 on a 1 km grid in a custom projection centred on Italy, not EPSG:4326 or EPSG:3857, and their GeoKeys are internally inconsistent. Reprojection is mandatory and the source CRS must be read from each file rather than hardcoded. - The ARPA CAP feed carries six level values, not four: BIANCO for the avalanche scale out of season and "-" for no data. Collapsing either into VERDE would report "no alert" where the bulletin reports "not assessed". Also documents why the frames we publish inherit CC BY-SA from the DPC source, and why rain notifications subscribe to cell topics from the device so no user location ever reaches a server. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
# Privacy design
|
||||
|
||||
This is the engineering note. The user-facing privacy policy is drafted in M9 and must
|
||||
stay consistent with what is written here.
|
||||
|
||||
## Principle
|
||||
|
||||
The backend holds no user data of any kind. There is no account, no device registry,
|
||||
no user table. This is not a policy promise — it is a property of the architecture,
|
||||
and it is what makes the Data safety declaration simple and honest.
|
||||
|
||||
## Location
|
||||
|
||||
The device may ask for location permission to centre the map and to pick a forecast
|
||||
point. When it does:
|
||||
|
||||
- **Prominent disclosure** is shown before the system permission dialog, stating what
|
||||
the location is used for, as Google Play requires.
|
||||
- The permission is optional. Declining leaves the app fully usable: the map opens on
|
||||
the region centre from the region config and the forecast point is chosen manually.
|
||||
- The coordinates stay on the device. They are used to render the map and to build the
|
||||
MET Norway request, and are never sent to our backend.
|
||||
|
||||
MET Norway does receive coordinates — it cannot return a forecast otherwise. This is
|
||||
disclosed on the Sources screen, and the coordinates are rounded before being sent.
|
||||
|
||||
## Rain notifications without a server-side location
|
||||
|
||||
The worker computes rain per geographic cell and publishes per-cell state. The device
|
||||
decides which cells it cares about and subscribes to the matching FCM topics **itself**.
|
||||
|
||||
The consequence is that the server never learns which cell any user is in — it
|
||||
publishes to topics, not to devices. There is nothing to correlate, nothing to
|
||||
subpoena, and nothing to breach. Cell size is coarse enough that a cell identifies an
|
||||
area, not a household.
|
||||
|
||||
**Never** replace this with device tokens registered against coordinates. It would be
|
||||
simpler and it would destroy the property.
|
||||
|
||||
## Advertising and consent
|
||||
|
||||
AdMob is initialised only after the UMP consent flow completes:
|
||||
|
||||
- The consent form is shown before **any** ad request.
|
||||
- Declining consent yields non-personalised ads. It never yields no app.
|
||||
- The consent choice is revocable from the settings screen.
|
||||
- A Google-certified CMP (UMP) is used, IAB TCF 2.3.
|
||||
|
||||
Only test ad unit IDs are used in development. Real IDs arrive through `env.json`,
|
||||
which is git-ignored.
|
||||
|
||||
## Caching on the device
|
||||
|
||||
Radar frames and forecast responses are cached on disk under the app's private
|
||||
directory. The cache holds published weather data only — no personal data — and is
|
||||
cleared with the app.
|
||||
|
||||
## Third parties that receive data
|
||||
|
||||
| Party | What it receives | Why |
|
||||
|---|---|---|
|
||||
| MET Norway | rounded coordinates, User-Agent | to return a forecast |
|
||||
| Google AdMob | ad request data per the consent choice | monetisation |
|
||||
| Firebase Cloud Messaging | topic subscriptions (no coordinates) | rain notifications |
|
||||
| Our CDN | frame and manifest requests | radar imagery |
|
||||
|
||||
Our own backend receives nothing that identifies a user, by construction.
|
||||
Reference in New Issue
Block a user