Three operator decisions change v1's premises, so the design is now two tiers with separate flags: Tier A (THERMOGRAPH_EVENTS) is v1 unchanged -- anonymous hourly aggregates, no identifier, nothing stored on the device, no consent needed, runs for everyone. It stays the primary signal precisely so a poor consent rate cannot take the numbers down with it, and so Tier B's rates can be calibrated against it to measure the consent bias rather than ignoring it. Tier B (THERMOGRAPH_EVENT_SESSIONS) adds an ephemeral per-tab id -- 16 random bytes in sessionStorage, rotating on a 30-minute idle and a 2-hour absolute cap, capped at 200 events, never linked to another session, device, or to the account (api_event does not read the auth cookie and there is no user column). Rows land in a 30-day hypertable with minute-granularity timestamps and an in-session sequence number instead of precise clock times. Storing an identifier engages ePrivacy Art. 5(3) and analytics is not strictly necessary, so it is gated on opt-in consent: an equal-weight banner that mints nothing before "Allow", one-click withdrawal in every footer that drops the live id immediately, and GPC/DNT treated as a refusal already given. THERMOGRAPH_SEARCH_MISS captures zero-result search text server-side in api_suggest only -- normalised, rejected outright on any personal-data smell, stored as a per-day count, and pruned below a three-person floor after a week. The privacy page is rewritten rather than deferred: "no per-visitor identifier" becomes false the moment Tier B ships. Tests assert the load-bearing promises so the copy and the code cannot drift apart silently. Raw-IP truncation is NOT implemented here (the logging pipeline owns it) but UI-EVENTS.md states what the app side must do, and records that Caddy's default JSON log already stores full request URIs -- so every search query is in Loki with the client IP today, which the search-miss mitigations depend on fixing. UI-EVENTS.md carries the v1-to-v2 diff, the consent reasoning including why legitimate interest is not available, and an explicit argument that the session identifier is the wrong trade at this traffic volume.
89 lines
5.4 KiB
Django/Jinja
89 lines
5.4 KiB
Django/Jinja
{% extends "base.html.j2" %}
|
||
{% block title %}{{ page_title }}{% endblock %}
|
||
{% block description %}{{ page_description }}{% endblock %}
|
||
{% block canonical_path %}{{ canonical_path }}{% endblock %}
|
||
{% block content %}
|
||
<nav class="breadcrumb" aria-label="Breadcrumb">
|
||
{% for label, href in breadcrumb %}{% if href %}<a href="{{ href }}">{{ label }}</a>{% else %}<span>{{ label }}</span>{% endif %}{% if not loop.last %} <span class="sep">›</span> {% endif %}{% endfor %}
|
||
</nav>
|
||
<article class="climate-page">
|
||
<h1>Privacy</h1>
|
||
<p class="lede">Thermograph is free, has no ads, and needs no account. It is built so that
|
||
there is very little about you to collect in the first place.</p>
|
||
|
||
<h2>Your location</h2>
|
||
<p>Thermograph never looks up your location from your IP address. The only way the site
|
||
learns where you are is if you press <b>Use my location</b>, which asks your browser for
|
||
permission. You can decline, and picking a place on the map works just as well.</p>
|
||
<p>Whichever way you choose a place, the coordinates are sent to the server only as part of
|
||
the ordinary request for that grid square's climate data. The place you picked is
|
||
remembered in your own browser's local storage, not on the server, so clearing your
|
||
browser data forgets it. <b>The coordinates are never recorded in the usage statistics
|
||
below</b> — those record only <i>how</i> you picked a place (map, search, or your
|
||
browser's location), never <i>where</i>.</p>
|
||
|
||
<h2>Usage statistics</h2>
|
||
<p>There is no third-party analytics service, no advertising tag, and nothing that follows
|
||
you to other sites. What the site does keep is in two parts, and only the second one
|
||
asks your permission.</p>
|
||
|
||
<h3>Anonymous counts — always on</h3>
|
||
<p>The server keeps simple counts of things like "the calendar was opened 40 times this
|
||
hour" or "a search found nothing 12 times today", along with which site referred the
|
||
visit (the domain only, never the full link). No identifier of any kind is attached,
|
||
nothing is stored on your device, and there is no way to tell one visitor's counts from
|
||
another's — so there is nothing here that could be traced back to you. These counts are
|
||
kept for up to 400 days.</p>
|
||
|
||
<h3>Session statistics — only if you say yes</h3>
|
||
<p>To answer questions like "do people who open the location picker actually get a
|
||
forecast, or give up half way?", the site has to see several actions as belonging to the
|
||
same visit. That means putting a random identifier in your browser, so
|
||
<b>we ask first, and it stays off unless you agree</b>.</p>
|
||
<p>If you agree:</p>
|
||
<ul>
|
||
<li>a random number is generated and kept in your browser's <b>session storage</b>, for
|
||
the current tab only. It is not a cookie, and it is not sent to any other site;</li>
|
||
<li>it changes after 30 minutes of inactivity, and after 2 hours regardless, so it cannot
|
||
follow you across a day, a device, or a return visit;</li>
|
||
<li>it disappears when you close the tab;</li>
|
||
<li>it records which views and controls you used, from a short fixed list — never what you
|
||
typed, never where you are, never which articles you read;</li>
|
||
<li>it is <b>never linked to your account</b>, even when you are signed in, and never
|
||
linked to your email address or your IP address;</li>
|
||
<li>the records are deleted after <b>30 days</b>.</li>
|
||
</ul>
|
||
<p>You can change your mind at any time with the <b>Analytics</b> control at the bottom of
|
||
every page. Turning it off deletes the identifier from your browser immediately and stops
|
||
any further recording. Because the records are deliberately not linked to you, we cannot
|
||
go back and find your past ones to delete — which is part of why they are kept for such a
|
||
short time.</p>
|
||
<p>If your browser sends a <b>Global Privacy Control</b> or <b>Do Not Track</b> signal, the
|
||
site treats that as a no, does not ask, and records nothing extra.</p>
|
||
|
||
<h3>Searches that found nothing</h3>
|
||
<p>When you search for a place and the site finds no match, the text you typed may be kept
|
||
so that missing places can be added. It is stored on its own, with nothing else about you
|
||
attached, and only if it looks like a place name — anything containing an email address,
|
||
a web address or a long run of digits is discarded rather than stored. Anything only one
|
||
or two people ever searched for is deleted within a week; the rest is deleted after
|
||
90 days. Searches that <i>did</i> find a place are never stored as text.</p>
|
||
|
||
<h2>Server logs</h2>
|
||
<p>As on every website, the server records that a request happened — the page, the time and
|
||
the result. Network addresses are shortened before they are written down, so an individual
|
||
device cannot be picked out of the logs, and the logs are deleted after 30 days.</p>
|
||
|
||
<h2>Email</h2>
|
||
<p>If you sign up for the monthly digest, your address is used for that digest and nothing
|
||
else. It is never sold or shared, and every message can unsubscribe you.</p>
|
||
|
||
<h2>Accounts</h2>
|
||
<p>An account is optional and only exists to hold weather alerts you have asked for. It
|
||
stores your email address and the places you are watching. None of the usage statistics
|
||
above are connected to it.</p>
|
||
|
||
<p class="muted">Questions: see <a href="{{ base }}/about">About & methodology</a>.</p>
|
||
<p class="muted consent-control" data-consent-control></p>
|
||
</article>
|
||
{% endblock %}
|