Seed docs/legal-archive with Terms / Privacy / Imprint snapshots
Adds a simple legal-archive folder that preserves the full text of the
Terms, Privacy Policy, and Impressum at each version, plus a small
renderer script so future snapshots are mechanical:
python3 scripts/render-legal.py terms > docs/legal-archive/terms-YYYY-MM-DD.md
Why:
- Terms are per-user contract. users.terms_version maps each user to a
file in this folder so we can always tell what text they agreed to.
- Privacy is informational but GDPR Art. 13/14 still expects us to be
able to say what the policy disclosed on a given date.
- Impressum archived for symmetry; cheap.
How it works:
- scripts/render-legal.py reads the TSX source, resolves operator.*
placeholders from operator.ts and TERMS_VERSION / PRIVACY_LAST_UPDATED
from legal.ts, strips JSX tags, and prints clean markdown to stdout.
- A README in the folder explains when to bump and when to add a
snapshot (material change only, not typo fixes).
Seeded entries:
- terms-2026-04-19.md
- privacy-2026-04-19.md
- imprint-2026-04-19.md
No automation / CI check yet — trust-based process documented in the
folder's README. Revisit if discipline slips.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c6c5ad3765
commit
d52828543a
5 changed files with 773 additions and 0 deletions
53
docs/legal-archive/README.md
Normal file
53
docs/legal-archive/README.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Legal archive
|
||||||
|
|
||||||
|
Frozen snapshots of the Terms of Service, Privacy Policy, and Impressum at
|
||||||
|
each version. Kept so we can always answer "what did the Terms / Privacy
|
||||||
|
text say on a given date" without digging through git blame.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
- **Terms**: users accept a specific version at registration
|
||||||
|
(`users.terms_accepted_at` + `users.terms_version`). The version string
|
||||||
|
is the date in this folder — so there is always a file here matching
|
||||||
|
every value that exists in that column.
|
||||||
|
- **Privacy**: users don't "accept" a privacy policy, but GDPR Art. 13/14
|
||||||
|
requires us to tell users how their data is processed *at the time* it's
|
||||||
|
processed. If a regulator or user asks what the policy said on
|
||||||
|
YYYY-MM-DD, this folder answers.
|
||||||
|
- **Impressum**: less legally critical, but free to snapshot for symmetry.
|
||||||
|
|
||||||
|
## When to add a file
|
||||||
|
|
||||||
|
Whenever any of these three texts change materially, run the snapshot step
|
||||||
|
as part of the change:
|
||||||
|
|
||||||
|
1. Bump `TERMS_VERSION` / `PRIVACY_LAST_UPDATED` in
|
||||||
|
[`apps/journal/app/lib/legal.ts`](../../apps/journal/app/lib/legal.ts)
|
||||||
|
to today's date.
|
||||||
|
2. Re-render the affected page(s) into this folder as
|
||||||
|
`<doc>-YYYY-MM-DD.md`.
|
||||||
|
3. Commit the bump **and** the new snapshot in the same PR.
|
||||||
|
|
||||||
|
A trivial wording tweak or typo fix does not need a new snapshot; only
|
||||||
|
changes that affect meaning / behaviour / purposes / third parties / legal
|
||||||
|
basis / retention / etc.
|
||||||
|
|
||||||
|
## File naming
|
||||||
|
|
||||||
|
`<doc>-YYYY-MM-DD.md` where `<doc>` is one of `terms`, `privacy`,
|
||||||
|
`imprint`, and the date matches the `Last updated` line in the
|
||||||
|
corresponding legal page on the day the snapshot was taken.
|
||||||
|
|
||||||
|
## How to render
|
||||||
|
|
||||||
|
The snapshots are plain markdown extracted from the TSX source by
|
||||||
|
stripping JSX tags / attributes and resolving the `operator.*`
|
||||||
|
placeholders. Same approach as the pbcopy export used during legal
|
||||||
|
reviews. One-liner (from repo root):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/render-legal.py <doc> > docs/legal-archive/<doc>-YYYY-MM-DD.md
|
||||||
|
```
|
||||||
|
|
||||||
|
(If `scripts/render-legal.py` doesn't exist yet, the extraction logic is
|
||||||
|
in the commit that seeded this directory — see PR history.)
|
||||||
83
docs/legal-archive/imprint-2026-04-19.md
Normal file
83
docs/legal-archive/imprint-2026-04-19.md
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
# Impressum — trails.cool
|
||||||
|
|
||||||
|
Impressum / Legal Notice
|
||||||
|
|
||||||
|
Die deutsche Fassung ist maßgeblich. / The German version is authoritative.
|
||||||
|
|
||||||
|
Anbieter nach § 5 TMG
|
||||||
|
|
||||||
|
Ullrich Schäfer
|
||||||
|
|
||||||
|
Mehringdamm 87
|
||||||
|
|
||||||
|
10965 Berlin
|
||||||
|
|
||||||
|
Germany
|
||||||
|
|
||||||
|
Kontakt
|
||||||
|
|
||||||
|
E-Mail:
|
||||||
|
|
||||||
|
legal@trails.cool
|
||||||
|
|
||||||
|
Inhaltlich verantwortlich nach § 18 Abs. 2 MStV
|
||||||
|
|
||||||
|
Ullrich Schäfer
|
||||||
|
|
||||||
|
Mehringdamm 87
|
||||||
|
|
||||||
|
10965 Berlin
|
||||||
|
|
||||||
|
Streitbeilegung
|
||||||
|
|
||||||
|
Die Europäische Kommission stellt eine Plattform zur
|
||||||
|
Online-Streitbeilegung (OS) bereit:
|
||||||
|
|
||||||
|
https://ec.europa.eu/consumers/odr
|
||||||
|
|
||||||
|
.
|
||||||
|
|
||||||
|
Wir sind nicht bereit oder verpflichtet, an
|
||||||
|
Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle
|
||||||
|
teilzunehmen.
|
||||||
|
|
||||||
|
Alpha-Status
|
||||||
|
|
||||||
|
trails.cool befindet sich in aktiver Entwicklung (Alpha). Inhalte,
|
||||||
|
Funktionen und Daten können sich jederzeit ändern oder entfallen.
|
||||||
|
|
||||||
|
English version
|
||||||
|
|
||||||
|
Service provider (§ 5 TMG)
|
||||||
|
|
||||||
|
Ullrich Schäfer
|
||||||
|
|
||||||
|
Mehringdamm 87
|
||||||
|
|
||||||
|
10965 Berlin
|
||||||
|
|
||||||
|
Germany
|
||||||
|
|
||||||
|
Contact
|
||||||
|
|
||||||
|
Email:
|
||||||
|
|
||||||
|
legal@trails.cool
|
||||||
|
|
||||||
|
Content responsibility (§ 18 (2) MStV)
|
||||||
|
|
||||||
|
Ullrich Schäfer, address as above.
|
||||||
|
|
||||||
|
Dispute resolution
|
||||||
|
|
||||||
|
The European Commission provides an online dispute resolution platform at
|
||||||
|
|
||||||
|
https://ec.europa.eu/consumers/odr
|
||||||
|
|
||||||
|
. We are not willing or obliged to participate in dispute resolution
|
||||||
|
proceedings before a consumer arbitration board.
|
||||||
|
|
||||||
|
Alpha status
|
||||||
|
|
||||||
|
trails.cool is under active development (alpha). Features and data
|
||||||
|
may change or be removed at any time.
|
||||||
358
docs/legal-archive/privacy-2026-04-19.md
Normal file
358
docs/legal-archive/privacy-2026-04-19.md
Normal file
|
|
@ -0,0 +1,358 @@
|
||||||
|
# Privacy Policy — trails.cool
|
||||||
|
|
||||||
|
Datenschutzerklärung / Privacy Policy
|
||||||
|
|
||||||
|
Stand / Last updated: 2026-04-19. Die deutsche Fassung ist maßgeblich.
|
||||||
|
The German version is authoritative; English summaries follow each
|
||||||
|
section.
|
||||||
|
|
||||||
|
1. Verantwortlicher
|
||||||
|
|
||||||
|
Verantwortlich für die Datenverarbeitung im Sinne der DSGVO ist:
|
||||||
|
|
||||||
|
Ullrich Schäfer
|
||||||
|
|
||||||
|
Mehringdamm 87
|
||||||
|
|
||||||
|
10965 Berlin
|
||||||
|
|
||||||
|
Germany
|
||||||
|
|
||||||
|
E-Mail:
|
||||||
|
|
||||||
|
legal@trails.cool
|
||||||
|
|
||||||
|
English.
|
||||||
|
Data controller under GDPR is the party named
|
||||||
|
above. Contact for any data-protection matter: legal@trails.cool.
|
||||||
|
|
||||||
|
2. Erhobene Daten und Zwecke
|
||||||
|
|
||||||
|
Wir verarbeiten nur die Daten, die für den Betrieb der jeweiligen
|
||||||
|
Funktion erforderlich sind. trails.cool besteht aus zwei Teilen:
|
||||||
|
dem
|
||||||
|
Journal
|
||||||
|
(trails.cool, mit Konto) und dem
|
||||||
|
|
||||||
|
Planner
|
||||||
|
(planner.trails.cool, anonym).
|
||||||
|
|
||||||
|
Kontodaten (Journal):
|
||||||
|
E-Mail-Adresse,
|
||||||
|
Benutzername, Anzeigename, Passkey-Public-Key. Zweck:
|
||||||
|
Kontoverwaltung und Anmeldung.
|
||||||
|
|
||||||
|
Nutzerinhalte (Journal):
|
||||||
|
Routen (GPX-Daten,
|
||||||
|
Geometrie, Titel, Beschreibung) und Aktivitäten (Titel,
|
||||||
|
Beschreibung, Datum, Verknüpfung zu Routen). Zweck: Speicherung
|
||||||
|
und Anzeige innerhalb des Dienstes.
|
||||||
|
|
||||||
|
Anmeldedaten (Journal):
|
||||||
|
kurzlebige Magic-Link-Token
|
||||||
|
(zur E-Mail-basierten Anmeldung). Zweck: Authentifizierung.
|
||||||
|
|
||||||
|
Sitzungscookie (Journal):
|
||||||
|
eine zufällige
|
||||||
|
Sitzungs-ID nach dem Einloggen. Zweck: Authentifizierung während
|
||||||
|
der Sitzung.
|
||||||
|
|
||||||
|
Planner-Sitzungsdaten:
|
||||||
|
anonyme Sitzungs-ID,
|
||||||
|
kollaborativer Zustand (Wegpunkte, Notizen). Keine Zuordnung zu
|
||||||
|
einer Person. Zweck: gemeinsames Planen von Routen.
|
||||||
|
|
||||||
|
Server-Logfiles:
|
||||||
|
IP-Adresse, Zeitstempel,
|
||||||
|
HTTP-Methode, Pfad, Statuscode, User-Agent. Zweck: Sicherheit,
|
||||||
|
Betrieb, Fehlersuche. Details siehe Abschnitt 4.
|
||||||
|
|
||||||
|
Fehlerdaten (Sentry):
|
||||||
|
Stacktraces,
|
||||||
|
Fehlermeldungen, Browser-/OS-Information aus dem User-Agent,
|
||||||
|
Performance-Metriken. Bei eingeloggten Journal-Nutzer:innen
|
||||||
|
zusätzlich die Nutzer-ID (keine E-Mail, kein Benutzername).
|
||||||
|
IP-Adressen werden nicht aktiv gespeichert, ebenso keine Cookies
|
||||||
|
und keine Formulareingaben.
|
||||||
|
|
||||||
|
English.
|
||||||
|
The Journal stores only what you provide (account
|
||||||
|
details and your own routes/activities) plus short-lived auth
|
||||||
|
artefacts. The Planner is anonymous and holds only ephemeral
|
||||||
|
session state. Server logs and Sentry error data are covered
|
||||||
|
separately below.
|
||||||
|
|
||||||
|
3. Rechtsgrundlagen
|
||||||
|
|
||||||
|
Art. 6 Abs. 1 lit. b DSGVO (Vertragserfüllung):
|
||||||
|
|
||||||
|
Kontoführung, Anmeldung per Passkey oder Magic Link, Speicherung
|
||||||
|
und Anzeige der von Ihnen erstellten Routen und Aktivitäten,
|
||||||
|
Versand notwendiger Transaktions-E-Mails, Bereitstellung anonymer
|
||||||
|
Planner-Sitzungen. Ein Konto sowie die Bestätigung der
|
||||||
|
Nutzungsbedingungen sind Bestandteil dieses Nutzungsvertrags –
|
||||||
|
sie sind
|
||||||
|
keine
|
||||||
|
Einwilligung im Sinne von Art. 6 Abs. 1
|
||||||
|
lit. a DSGVO.
|
||||||
|
|
||||||
|
Art. 6 Abs. 1 lit. f DSGVO (berechtigte Interessen):
|
||||||
|
|
||||||
|
kurzzeitige Server-Logfiles zur Sicherung des Betriebs und zur
|
||||||
|
Missbrauchsabwehr, Rate-Limiting, Fehlermonitoring über Sentry
|
||||||
|
zur Sicherstellung der Funktionsfähigkeit des Dienstes.
|
||||||
|
|
||||||
|
English.
|
||||||
|
Contract (Art. 6(1)(b)) covers everything
|
||||||
|
account- and content-related; legitimate interests (Art. 6(1)(f))
|
||||||
|
cover short-lived server logs, rate-limiting, and error monitoring.
|
||||||
|
We do
|
||||||
|
not
|
||||||
|
rely on consent for any of this.
|
||||||
|
|
||||||
|
4. Server-Logfiles
|
||||||
|
|
||||||
|
Beim Aufruf der Dienste werden automatisch technische Informationen
|
||||||
|
protokolliert:
|
||||||
|
|
||||||
|
IP-Adresse
|
||||||
|
|
||||||
|
Zeitstempel
|
||||||
|
|
||||||
|
HTTP-Methode, Pfad, Statuscode
|
||||||
|
|
||||||
|
User-Agent (Browser, Betriebssystem)
|
||||||
|
|
||||||
|
Zweck:
|
||||||
|
Sicherheit, Betrieb und Fehlersuche.
|
||||||
|
|
||||||
|
Rechtsgrundlage:
|
||||||
|
Art. 6 Abs. 1 lit. f DSGVO.
|
||||||
|
|
||||||
|
Speicherdauer:
|
||||||
|
maximal 14 Tage, danach automatische
|
||||||
|
Löschung. Eine Zusammenführung dieser Daten mit anderen Datenquellen
|
||||||
|
findet nicht statt.
|
||||||
|
|
||||||
|
English.
|
||||||
|
HTTP requests to our servers are logged (IP,
|
||||||
|
timestamp, method, path, status, user-agent) for up to 14 days for
|
||||||
|
operational and security purposes under Art. 6(1)(f), then deleted.
|
||||||
|
|
||||||
|
5. Speicherdauer
|
||||||
|
|
||||||
|
Konto und zugehörige Inhalte: bis zur Löschung durch Sie
|
||||||
|
|
||||||
|
Planner-Sitzungen: automatische Löschung nach 7 Tagen Inaktivität
|
||||||
|
|
||||||
|
Magic-Link-Token: 15 Minuten
|
||||||
|
|
||||||
|
Server-Logfiles: maximal 14 Tage
|
||||||
|
|
||||||
|
Fehlerdaten (Sentry): 90 Tage
|
||||||
|
|
||||||
|
Hinweis Alpha: Während der Alpha-Phase behält sich der Betreiber
|
||||||
|
ausdrücklich vor, die Datenbank zurückzusetzen oder einzelne
|
||||||
|
Datensätze zu löschen. Dies kann zu Datenverlust führen, bevor Sie
|
||||||
|
eine Löschung veranlassen. Details dazu in den Nutzungsbedingungen.
|
||||||
|
|
||||||
|
English.
|
||||||
|
Account and content kept until you delete them.
|
||||||
|
Ephemeral data (sessions, magic-link tokens, logs, Sentry events)
|
||||||
|
deleted automatically on the schedules above. Alpha caveat: the
|
||||||
|
operator may reset the database or delete individual records
|
||||||
|
during alpha, which can cause data loss before you request
|
||||||
|
deletion. See the Terms of Service.
|
||||||
|
|
||||||
|
6. Empfänger und Drittanbieter
|
||||||
|
|
||||||
|
Wir geben personenbezogene Daten nur an die unten genannten
|
||||||
|
Auftragsverarbeiter und Dritten weiter, und auch dort nur im
|
||||||
|
jeweils notwendigen Umfang.
|
||||||
|
|
||||||
|
Sentry
|
||||||
|
(Functional Software Inc.) – Fehler- und
|
||||||
|
Performance-Monitoring. Was übermittelt wird: Stacktraces,
|
||||||
|
Fehlertext, Browser-/OS-Informationen aus dem User-Agent,
|
||||||
|
Performance-Daten; bei eingeloggten Journal-Nutzer:innen
|
||||||
|
zusätzlich die Nutzer-ID. IP-Adressen werden nicht aktiv
|
||||||
|
gespeichert (
|
||||||
|
sendDefaultPii
|
||||||
|
ist deaktiviert), ebenso
|
||||||
|
keine Cookies und keine vollständigen HTTP-Header. Keine
|
||||||
|
Session-Replays. Sentry agiert als externer Dienstleister
|
||||||
|
(Auftragsverarbeiter) für die Fehlerbehandlung. Da Sentry ein
|
||||||
|
Anbieter mit Sitz in den USA ist, kann im Einzelfall eine
|
||||||
|
Übermittlung personenbezogener Daten in ein Drittland im Sinne
|
||||||
|
der Art. 44 ff. DSGVO stattfinden; wir stützen uns hierbei auf
|
||||||
|
die von Sentry bereitgestellten
|
||||||
|
Standardvertragsklauseln.
|
||||||
|
|
||||||
|
OpenStreetMap
|
||||||
|
– Kartenkacheln werden beim Anzeigen
|
||||||
|
der Karten direkt vom Browser von OSM-Tile-Servern geladen. Dabei
|
||||||
|
werden
|
||||||
|
IP-Adresse und User-Agent
|
||||||
|
an OSM übertragen.
|
||||||
|
Dies ist notwendig, um überhaupt eine Karte darzustellen; es gilt
|
||||||
|
die
|
||||||
|
|
||||||
|
OSM Foundation Privacy Policy
|
||||||
|
|
||||||
|
.
|
||||||
|
|
||||||
|
Overpass API
|
||||||
|
(POI-Daten) – POI-Abfragen laufen
|
||||||
|
serverseitig über unsere eigene Route
|
||||||
|
/api/overpass
|
||||||
|
.
|
||||||
|
Der Upstream-Dienst sieht nur unsere Server-IP, nicht die Ihrer
|
||||||
|
Nutzer:innen. Aktueller Upstream:
|
||||||
|
overpass.private.coffee
|
||||||
|
|
||||||
|
, der ohne Query-Logs arbeitet. Eine selbst gehostete Instanz ist
|
||||||
|
geplant.
|
||||||
|
|
||||||
|
BRouter
|
||||||
|
– Routenberechnung läuft auf einer von uns
|
||||||
|
selbst gehosteten Instanz. Keine Weitergabe an Dritte.
|
||||||
|
|
||||||
|
E-Mail-Versand
|
||||||
|
– Transaktions-E-Mails (Magic
|
||||||
|
Link, Willkommensnachricht) werden über einen konfigurierten
|
||||||
|
externen SMTP-Dienst versendet. Dabei wird die E-Mail-Adresse
|
||||||
|
der Empfänger:in an diesen Dienst übergeben. Selbst-betriebene
|
||||||
|
Instanzen konfigurieren ihren eigenen Mailserver.
|
||||||
|
|
||||||
|
Hosting
|
||||||
|
– Die Dienste werden in Rechenzentren
|
||||||
|
innerhalb der EU betrieben. Ein Auftragsverarbeitungsvertrag mit
|
||||||
|
dem Hoster besteht.
|
||||||
|
|
||||||
|
English.
|
||||||
|
Third parties and what they receive: Sentry (error
|
||||||
|
details, no IPs/cookies); OpenStreetMap tile servers
|
||||||
|
(your IP and user-agent, directly from your browser, to load map
|
||||||
|
tiles); Overpass (via our server-side proxy, so upstream only sees
|
||||||
|
our server); BRouter (self-hosted, no third party involved); SMTP
|
||||||
|
provider (your email address for magic link / welcome mail);
|
||||||
|
hosting provider in the EU under a DPA.
|
||||||
|
|
||||||
|
7. Ihre Rechte
|
||||||
|
|
||||||
|
Als betroffene Person stehen Ihnen die folgenden Rechte zu:
|
||||||
|
|
||||||
|
Auskunft (Art. 15 DSGVO)
|
||||||
|
|
||||||
|
Berichtigung (Art. 16 DSGVO)
|
||||||
|
|
||||||
|
Löschung (Art. 17 DSGVO)
|
||||||
|
|
||||||
|
Einschränkung der Verarbeitung (Art. 18 DSGVO)
|
||||||
|
|
||||||
|
Datenübertragbarkeit (Art. 20 DSGVO)
|
||||||
|
|
||||||
|
Widerspruch gegen Verarbeitung auf Basis berechtigter Interessen (Art. 21 DSGVO)
|
||||||
|
|
||||||
|
Zur Ausübung dieser Rechte genügt eine formlose E-Mail an
|
||||||
|
|
||||||
|
legal@trails.cool
|
||||||
|
|
||||||
|
. Einen vollständigen Export Ihrer Routen und Aktivitäten können Sie
|
||||||
|
jederzeit direkt in den Kontoeinstellungen herunterladen (GPX bzw.
|
||||||
|
JSON). Ihr Konto samt Inhalten können Sie dort ebenfalls selbst
|
||||||
|
löschen.
|
||||||
|
|
||||||
|
English.
|
||||||
|
You have the standard GDPR rights (access,
|
||||||
|
rectification, erasure, restriction, portability, objection). Email
|
||||||
|
us to exercise them. Data exports and account deletion are also
|
||||||
|
available directly in your account settings.
|
||||||
|
|
||||||
|
8. Beschwerderecht
|
||||||
|
|
||||||
|
Sie haben das Recht, sich bei einer Datenschutzaufsichtsbehörde zu
|
||||||
|
beschweren. Für uns zuständig ist:
|
||||||
|
|
||||||
|
Berliner Beauftragte für Datenschutz und Informationsfreiheit
|
||||||
|
|
||||||
|
Friedrichstr. 219
|
||||||
|
|
||||||
|
10969 Berlin
|
||||||
|
|
||||||
|
www.datenschutz-berlin.de
|
||||||
|
|
||||||
|
English.
|
||||||
|
You have the right to lodge a complaint with a
|
||||||
|
supervisory authority; ours is the Berlin data-protection
|
||||||
|
commissioner (address above).
|
||||||
|
|
||||||
|
Privacy Manifest
|
||||||
|
|
||||||
|
A plain-language summary of what we do, for readers who prefer
|
||||||
|
behaviour over paragraphs. Binding text is above.
|
||||||
|
|
||||||
|
Planner (planner.trails.cool)
|
||||||
|
|
||||||
|
Anonymous by design. No account, no identifier, nothing persisted
|
||||||
|
past the session.
|
||||||
|
|
||||||
|
No cookies, no localStorage, no sessionStorage
|
||||||
|
|
||||||
|
No browser-side error tracking (Sentry is not loaded)
|
||||||
|
|
||||||
|
Session data is deleted automatically after 7 days of inactivity
|
||||||
|
|
||||||
|
Journal (trails.cool)
|
||||||
|
|
||||||
|
Holds only what you put in. Exportable any time, deletable any time.
|
||||||
|
|
||||||
|
Account: email, username, display name, passkey public key
|
||||||
|
|
||||||
|
Routes: GPX, geometry, title, description
|
||||||
|
|
||||||
|
Activities: title, description, date, linked route
|
||||||
|
|
||||||
|
GPX / JSON export available per object and overall
|
||||||
|
|
||||||
|
Sentry
|
||||||
|
|
||||||
|
Journal server: always on
|
||||||
|
|
||||||
|
Journal browser: only after login, torn down on logout
|
||||||
|
|
||||||
|
Planner: server only; the browser never loads Sentry
|
||||||
|
|
||||||
|
IPs not actively stored, no cookies, no full headers (
|
||||||
|
sendDefaultPii: false
|
||||||
|
)
|
||||||
|
|
||||||
|
No replays (replay integration not installed, sample rates 0)
|
||||||
|
|
||||||
|
User ID only (no email/username) on Journal-logged-in events
|
||||||
|
|
||||||
|
What we don't do
|
||||||
|
|
||||||
|
Sell data
|
||||||
|
|
||||||
|
Show ads
|
||||||
|
|
||||||
|
Build user profiles
|
||||||
|
|
||||||
|
Run tracking pixels, analytics, or A/B tests
|
||||||
|
|
||||||
|
Security
|
||||||
|
|
||||||
|
Auth via passkey (WebAuthn) or magic link — no passwords stored
|
||||||
|
|
||||||
|
HTTPS + HSTS preload on all origins; session cookies httpOnly + Secure
|
||||||
|
|
||||||
|
CSP, X-Frame-Options, X-Content-Type-Options on every response
|
||||||
|
|
||||||
|
Containers run non-root; host firewall restricts to HTTP/HTTPS/SSH
|
||||||
|
|
||||||
|
Gitleaks + dependency audit on every PR
|
||||||
|
|
||||||
|
Vulnerability reports:
|
||||||
|
|
||||||
|
SECURITY.md
|
||||||
157
docs/legal-archive/terms-2026-04-19.md
Normal file
157
docs/legal-archive/terms-2026-04-19.md
Normal file
|
|
@ -0,0 +1,157 @@
|
||||||
|
# Terms of Service — trails.cool
|
||||||
|
|
||||||
|
Nutzungsbedingungen / Terms of Service
|
||||||
|
|
||||||
|
Stand / Last updated: 2026-04-19 • Alpha — subject to change. Die
|
||||||
|
deutsche Fassung ist maßgeblich. The German version is authoritative;
|
||||||
|
English summaries follow each section.
|
||||||
|
|
||||||
|
1. Gegenstand und Alpha-Status
|
||||||
|
|
||||||
|
trails.cool ist ein experimenteller, derzeit kostenloser Dienst zur Planung
|
||||||
|
und Dokumentation von Outdoor-Aktivitäten. Der Dienst befindet sich
|
||||||
|
in aktiver Entwicklung (Alpha). Funktionen, Schnittstellen und
|
||||||
|
gespeicherte Daten können jederzeit ohne Vorankündigung geändert,
|
||||||
|
unterbrochen, gelöscht oder eingestellt werden. Eine
|
||||||
|
Verfügbarkeitsgarantie besteht nicht.
|
||||||
|
|
||||||
|
English.
|
||||||
|
trails.cool is an experimental service,
|
||||||
|
currently free of charge, in
|
||||||
|
active alpha development. Features, data, and availability can
|
||||||
|
change at any time without notice. No availability is guaranteed.
|
||||||
|
|
||||||
|
2. Mindestalter
|
||||||
|
|
||||||
|
Die Nutzung eines Journal-Kontos ist erst ab einem Alter von 16
|
||||||
|
Jahren zulässig. Anonyme Nutzung des Planners unterliegt keiner
|
||||||
|
Altersbeschränkung.
|
||||||
|
|
||||||
|
English.
|
||||||
|
A Journal account is available only to users
|
||||||
|
aged 16 or older. Anonymous use of the Planner has no age
|
||||||
|
requirement.
|
||||||
|
|
||||||
|
3. Dienstverfügbarkeit
|
||||||
|
|
||||||
|
Der Betreiber kann den Dienst jederzeit ändern, einschränken,
|
||||||
|
unterbrechen oder einstellen, auch einzelne Funktionen oder
|
||||||
|
einzelne Konten. Wartungs- und Ausfallzeiten sind möglich.
|
||||||
|
|
||||||
|
English.
|
||||||
|
The operator may modify, limit, interrupt, or
|
||||||
|
discontinue the service (or parts of it, or individual accounts)
|
||||||
|
at any time. Downtime and maintenance windows may occur.
|
||||||
|
|
||||||
|
4. Zurücksetzen der Datenbank
|
||||||
|
|
||||||
|
Während der Alpha-Phase behält sich der Betreiber ausdrücklich das
|
||||||
|
Recht vor, die gesamte Datenbank zurückzusetzen oder einzelne
|
||||||
|
Datensätze ohne vorherige Benachrichtigung zu löschen.
|
||||||
|
Nutzer:innen sollten wichtige Routen und Aktivitäten selbstständig
|
||||||
|
über die Export-Funktion (GPX bzw. JSON) sichern.
|
||||||
|
|
||||||
|
English.
|
||||||
|
During alpha, the operator may reset the entire
|
||||||
|
database or delete individual records without notice. Back up
|
||||||
|
anything important via the GPX / JSON export.
|
||||||
|
|
||||||
|
5. Eigenverantwortung für Daten
|
||||||
|
|
||||||
|
Nutzer:innen sind für die Sicherung ihrer Inhalte selbst
|
||||||
|
verantwortlich. GPX- und JSON-Exporte sind im Journal jederzeit
|
||||||
|
für jede Route und Aktivität verfügbar. Der Betreiber schuldet
|
||||||
|
keine Datenwiederherstellung.
|
||||||
|
|
||||||
|
English.
|
||||||
|
You are responsible for backing up your own
|
||||||
|
content. Exports are always available in the Journal; the
|
||||||
|
operator owes no data recovery.
|
||||||
|
|
||||||
|
6. Inhalte und Nutzungsrechte
|
||||||
|
|
||||||
|
Die von Ihnen eingestellten Inhalte (Routen, Aktivitäten,
|
||||||
|
Beschreibungen, GPX-Dateien) bleiben Ihr Eigentum. Sie räumen dem
|
||||||
|
Betreiber lediglich das einfache, nicht übertragbare Recht ein,
|
||||||
|
diese Inhalte zum Zweck des Betriebs des Dienstes zu speichern,
|
||||||
|
zu verarbeiten und Ihnen wieder anzuzeigen. Eine darüber
|
||||||
|
hinausgehende Nutzung, Veröffentlichung oder Weitergabe findet
|
||||||
|
nicht statt.
|
||||||
|
|
||||||
|
English.
|
||||||
|
You retain ownership of everything you upload.
|
||||||
|
You grant the operator a limited, non-transferable licence to
|
||||||
|
store, process, and display your content solely for operating the
|
||||||
|
service; no other use, publication, or sharing.
|
||||||
|
|
||||||
|
7. Nutzungsregeln
|
||||||
|
|
||||||
|
Keine Verbreitung rechtswidriger Inhalte
|
||||||
|
|
||||||
|
Keine missbräuchliche Nutzung (Spam, hochfrequente automatisierte
|
||||||
|
Abfragen, Denial-of-Service)
|
||||||
|
|
||||||
|
Kein Umgehen technischer Schutzmaßnahmen
|
||||||
|
|
||||||
|
Kein massenhaftes automatisiertes Auslesen von Daten
|
||||||
|
|
||||||
|
English.
|
||||||
|
No illegal content, no abuse (spam, flooding,
|
||||||
|
DoS), no circumvention of technical protections, no bulk
|
||||||
|
automated data extraction.
|
||||||
|
|
||||||
|
8. Kontobeendigung
|
||||||
|
|
||||||
|
Sie können Ihr Konto jederzeit über die Einstellungen löschen;
|
||||||
|
damit werden auch die zugehörigen Inhalte gelöscht. Der Betreiber
|
||||||
|
kann Konten bei Verstößen gegen diese Bedingungen nach
|
||||||
|
angemessener Interessenabwägung sperren oder löschen.
|
||||||
|
|
||||||
|
English.
|
||||||
|
Delete your account (and its content) any time
|
||||||
|
via settings. The operator may suspend or delete accounts that
|
||||||
|
violate these terms, balancing interests reasonably.
|
||||||
|
|
||||||
|
9. Haftung
|
||||||
|
|
||||||
|
Der Dienst wird ohne Gewährleistung bereitgestellt. Der Betreiber
|
||||||
|
haftet uneingeschränkt bei Vorsatz und grober Fahrlässigkeit sowie
|
||||||
|
bei Verletzung von Leben, Körper oder Gesundheit. Im Übrigen ist
|
||||||
|
die Haftung für leicht fahrlässige Pflichtverletzungen auf den
|
||||||
|
vertragstypischen, vorhersehbaren Schaden bei Verletzung
|
||||||
|
wesentlicher Vertragspflichten (Kardinalpflichten) begrenzt. Eine
|
||||||
|
darüber hinausgehende Haftung – insbesondere für Datenverlust –
|
||||||
|
ist ausgeschlossen, soweit gesetzlich zulässig.
|
||||||
|
|
||||||
|
English.
|
||||||
|
No warranty. Unlimited liability for intent,
|
||||||
|
gross negligence, and injury to life, body, or health. For
|
||||||
|
slight negligence, liability is limited to foreseeable damages
|
||||||
|
arising from breach of material contractual duties. Liability
|
||||||
|
beyond that — in particular for data loss — is excluded to the
|
||||||
|
extent permitted by law.
|
||||||
|
|
||||||
|
10. Änderungen der Bedingungen
|
||||||
|
|
||||||
|
Diese Nutzungsbedingungen können sich während der Alpha-Phase
|
||||||
|
ändern. Bei wesentlichen Änderungen werden registrierte
|
||||||
|
Nutzer:innen per E-Mail informiert und müssen die neuen
|
||||||
|
Bedingungen erneut bestätigen.
|
||||||
|
|
||||||
|
English.
|
||||||
|
These terms may change during alpha. Registered
|
||||||
|
users will be emailed about material changes and prompted to
|
||||||
|
re-accept.
|
||||||
|
|
||||||
|
11. Anwendbares Recht
|
||||||
|
|
||||||
|
Es gilt deutsches Recht unter Ausschluss des UN-Kaufrechts. Für
|
||||||
|
Verbraucher:innen mit gewöhnlichem Aufenthalt in einem Mitgliedstaat
|
||||||
|
der Europäischen Union bleibt der zwingende Schutz des Rechts ihres
|
||||||
|
Aufenthaltslandes unberührt.
|
||||||
|
|
||||||
|
English.
|
||||||
|
German law applies, excluding the UN Convention
|
||||||
|
on Contracts for the International Sale of Goods. Consumers
|
||||||
|
habitually residing in an EU member state retain the mandatory
|
||||||
|
protection of their national law.
|
||||||
122
scripts/render-legal.py
Executable file
122
scripts/render-legal.py
Executable file
|
|
@ -0,0 +1,122 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Render a trails.cool legal page (Terms / Privacy / Impressum) from its TSX
|
||||||
|
source into plain markdown suitable for `docs/legal-archive/`.
|
||||||
|
|
||||||
|
Usage (from repo root):
|
||||||
|
python3 scripts/render-legal.py terms > docs/legal-archive/terms-YYYY-MM-DD.md
|
||||||
|
python3 scripts/render-legal.py privacy > docs/legal-archive/privacy-YYYY-MM-DD.md
|
||||||
|
python3 scripts/render-legal.py imprint > docs/legal-archive/imprint-YYYY-MM-DD.md
|
||||||
|
|
||||||
|
Strips JSX tags + attributes and resolves the `operator.*` placeholders
|
||||||
|
from `apps/journal/app/lib/operator.ts` so the output is a clean
|
||||||
|
human-readable snapshot.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
OPERATOR_FILE = REPO_ROOT / "apps/journal/app/lib/operator.ts"
|
||||||
|
LEGAL_FILE = REPO_ROOT / "apps/journal/app/lib/legal.ts"
|
||||||
|
|
||||||
|
PAGES = {
|
||||||
|
"terms": REPO_ROOT / "apps/journal/app/routes/legal.terms.tsx",
|
||||||
|
"privacy": REPO_ROOT / "apps/journal/app/routes/legal.privacy.tsx",
|
||||||
|
"imprint": REPO_ROOT / "apps/journal/app/routes/legal.imprint.tsx",
|
||||||
|
}
|
||||||
|
|
||||||
|
TITLES = {
|
||||||
|
"terms": "Terms of Service — trails.cool",
|
||||||
|
"privacy": "Privacy Policy — trails.cool",
|
||||||
|
"imprint": "Impressum — trails.cool",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def load_operator() -> dict[str, str]:
|
||||||
|
src = OPERATOR_FILE.read_text()
|
||||||
|
fields = ("name", "street", "postalCode", "city", "country", "email", "responsiblePerson")
|
||||||
|
return {
|
||||||
|
f: m.group(1) if (m := re.search(rf'{f}:\s*"([^"]+)"', src)) else ""
|
||||||
|
for f in fields
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def load_legal_constants() -> dict[str, str]:
|
||||||
|
"""Exported top-level `export const NAME = "value";` from legal.ts."""
|
||||||
|
src = LEGAL_FILE.read_text()
|
||||||
|
return dict(re.findall(r'export const (\w+)\s*=\s*"([^"]+)"', src))
|
||||||
|
|
||||||
|
|
||||||
|
def render(src: str, operator: dict[str, str], legal: dict[str, str]) -> str:
|
||||||
|
# Resolve operator placeholders
|
||||||
|
src = re.sub(r'\{operator\.address\.(\w+)\}', lambda m: operator.get(m.group(1), ""), src)
|
||||||
|
src = re.sub(r'\{operator\.(\w+)\}', lambda m: operator.get(m.group(1), ""), src)
|
||||||
|
|
||||||
|
# Resolve legal.ts constants (TERMS_VERSION, PRIVACY_LAST_UPDATED, …)
|
||||||
|
for name, value in legal.items():
|
||||||
|
src = re.sub(r'\{' + re.escape(name) + r'\}', value, src)
|
||||||
|
|
||||||
|
# Unwrap template literals and explicit single-space expressions
|
||||||
|
src = re.sub(r'\{`(.*?)`\}', r'\1', src)
|
||||||
|
src = re.sub(r'\{"\s*"\}', ' ', src)
|
||||||
|
|
||||||
|
# Strip common JSX attributes we don't want in prose
|
||||||
|
src = re.sub(r'\s(?:className|href|target|rel|id|name|content|htmlFor)="[^"]*"', '', src)
|
||||||
|
|
||||||
|
# Drop any remaining {expr} blocks (unresolved expressions)
|
||||||
|
src = re.sub(r'\{[^{}]*\}', '', src)
|
||||||
|
|
||||||
|
# Replace tags with newlines to preserve paragraph structure
|
||||||
|
src = re.sub(r'<[^>]+>', '\n', src)
|
||||||
|
|
||||||
|
# HTML entity decode (minimal)
|
||||||
|
for entity, replacement in (
|
||||||
|
("'", "'"),
|
||||||
|
("&", "&"),
|
||||||
|
("<", "<"),
|
||||||
|
(">", ">"),
|
||||||
|
(""", '"'),
|
||||||
|
(" ", " "),
|
||||||
|
):
|
||||||
|
src = src.replace(entity, replacement)
|
||||||
|
|
||||||
|
# Collapse blank lines
|
||||||
|
lines = [ln.rstrip() for ln in src.splitlines()]
|
||||||
|
out: list[str] = []
|
||||||
|
prev_blank = False
|
||||||
|
for ln in lines:
|
||||||
|
if ln.strip() == "":
|
||||||
|
if not prev_blank and out:
|
||||||
|
out.append("")
|
||||||
|
prev_blank = True
|
||||||
|
else:
|
||||||
|
out.append(ln.strip())
|
||||||
|
prev_blank = False
|
||||||
|
return "\n".join(out).strip()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
if len(sys.argv) != 2 or sys.argv[1] not in PAGES:
|
||||||
|
sys.stderr.write(f"Usage: {sys.argv[0]} {{{'|'.join(PAGES)}}}\n")
|
||||||
|
return 2
|
||||||
|
|
||||||
|
page = sys.argv[1]
|
||||||
|
operator = load_operator()
|
||||||
|
legal = load_legal_constants()
|
||||||
|
src = PAGES[page].read_text()
|
||||||
|
|
||||||
|
# Slice out the JSX tree: everything between "return (" and the closing ");"
|
||||||
|
src = re.split(r'return \(\s*\n', src, maxsplit=1)[-1]
|
||||||
|
src = re.sub(r'\);\s*\}\s*$', '', src.rstrip())
|
||||||
|
|
||||||
|
body = render(src, operator, legal)
|
||||||
|
print(f"# {TITLES[page]}\n\n{body}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Loading…
Add table
Add a link
Reference in a new issue