thermograph/frontend/templates/privacy.html.j2
Emi Griffith 35b58300cb UI events v2: consented session tier, failed-search capture, privacy rewrite
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.
2026-07-23 16:11:06 -07:00

89 lines
5.4 KiB
Django/Jinja
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% 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 &amp; methodology</a>.</p>
<p class="muted consent-control" data-consent-control></p>
</article>
{% endblock %}