Merge branch 'main' into feat/login-code
This commit is contained in:
commit
a056ec286b
8 changed files with 228 additions and 2 deletions
|
|
@ -65,7 +65,7 @@ function RouteMapInner({
|
|||
onWaypointPress,
|
||||
}: RouteMapProps) {
|
||||
const ML = MapLibreGL!;
|
||||
const cameraRef = useRef<MapLibreRN.CameraRef>(null);
|
||||
const cameraRef = useRef<MapLibreGL.CameraRef>(null);
|
||||
|
||||
const handleLongPress = useCallback(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import tseslint from "typescript-eslint";
|
|||
import prettier from "eslint-config-prettier";
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ["**/build/", "**/dist/", "**/.react-router/", "**/.expo/", "**/node_modules/"] },
|
||||
{ ignores: ["**/build/", "**/dist/", "**/.react-router/", "**/.expo/", "**/node_modules/", "**/metro.config.js"] },
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
|
|
|
|||
2
openspec/changes/legal-disclaimers/.openspec.yaml
Normal file
2
openspec/changes/legal-disclaimers/.openspec.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
schema: spec-driven
|
||||
created: 2026-04-17
|
||||
52
openspec/changes/legal-disclaimers/design.md
Normal file
52
openspec/changes/legal-disclaimers/design.md
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
## Context
|
||||
|
||||
The operator is based in Germany. German law requires an Impressum for any online service (TMG §5 / MStV §18) and a GDPR-compliant privacy policy (DSGVO Art. 13/14). Missing Impressum is routinely exploited by Abmahnanwälte — fines and legal costs can reach several thousand euros. The current privacy page is written as a human-readable manifest, not a formal Datenschutzerklärung.
|
||||
|
||||
The service is also in early alpha. The operator may reset the database, change core behavior, or break APIs at any time. Users signing up now have no way to know this.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- Meet the minimum legal requirements to operate from Germany (Impressum, Datenschutzerklärung, ToS)
|
||||
- Set clear user expectations about the alpha state — database resets, breaking changes
|
||||
- Require explicit acknowledgement at signup so there is a record of informed consent
|
||||
- Keep the existing privacy manifest style (readable, honest) while adding the formal GDPR sections
|
||||
|
||||
**Non-Goals:**
|
||||
- Professional legal review — the operator should still have a German IT lawyer review the final text before publishing
|
||||
- Cookie banner / consent management — trails.cool uses only essential cookies (no tracking, no analytics), so no banner is required under §25 TDDDG
|
||||
- Age verification or KYC
|
||||
- ToS-level commitments that would constitute a contract of service (we explicitly disclaim warranties)
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. Routes under `/legal/`
|
||||
**Choice**: `/legal/imprint`, `/legal/privacy`, `/legal/terms` — move the existing `/privacy` route to `/legal/privacy` with a redirect.
|
||||
**Rationale**: Grouped namespace makes the legal pages easy to find and link from the footer. Redirect preserves existing privacy links.
|
||||
**Alternative considered**: Keep `/privacy` at the top level — less organized as more legal pages are added.
|
||||
|
||||
### 2. Impressum as a plain page with placeholders
|
||||
**Choice**: The Impressum page is server-rendered from a small config object (operator name, address, email, responsible person). Not i18n'd — Impressum is normally in the language of the operator's country (German), but we'll include an English section below.
|
||||
**Rationale**: The operator's legal details rarely change. Keeping them in a config file (TypeScript) rather than the database avoids needing a CMS for a static page.
|
||||
**Alternative considered**: Database-backed operator config — overkill for data that changes once a year at most.
|
||||
|
||||
### 3. Signup acknowledgement via checkbox + timestamp
|
||||
**Choice**: Add a required checkbox on the registration form. On successful registration, store `terms_accepted_at` on the user row. If the Terms are later updated in a material way, we can compare the accepted timestamp and prompt re-acknowledgement.
|
||||
**Rationale**: A timestamped record of acknowledgement is sufficient for the experimental-status disclaimer. Re-acknowledgement is only needed for material changes.
|
||||
**Alternative considered**: Store the full ToS version hash — more precise but overkill while the operator can reset everyone's data anyway.
|
||||
|
||||
### 4. Alpha banner
|
||||
**Choice**: A thin banner at the top of the Journal layout, showing "trails.cool is in early development — your data may be reset. See Terms." The banner is dismissible for the browser session (sessionStorage, not cookie). Not shown on the Planner because Planner sessions are already anonymous and ephemeral.
|
||||
**Rationale**: Persistent, visible signal that users can dismiss. Session-scoped dismissal means returning users still see it occasionally.
|
||||
**Alternative considered**: One-time dismissal stored in the DB — adds server state for a trivial UX feature.
|
||||
|
||||
### 5. Keep Datenschutzerklärung readable
|
||||
**Choice**: The existing privacy manifest structure (Planner, Journal, Sentry, Email, Third Parties, Security) stays. We add new sections at the top for the GDPR-required info: data controller contact, legal basis for processing each category, data subject rights (Art. 15–22 DSGVO), and right to complain to the Berlin Beauftragte für Datenschutz.
|
||||
**Rationale**: Keeping the plain-language summary is valuable. The formal sections are additive.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Text is not legally reviewed**: The drafted text is a starting point — the operator should have a German IT lawyer review before publishing. → Mitigation: Note this explicitly in the tasks; don't treat the first version as final.
|
||||
- **Impressum requires real address**: The operator's private address is publicly exposed unless they rent a business address or use a service. → Mitigation: Call this out in the tasks so the operator decides before deploying.
|
||||
- **Re-acknowledgement flow not built now**: If the Terms change materially, users should re-acknowledge. The initial version doesn't include this. → Mitigation: The `terms_accepted_at` timestamp is captured so the flow can be added later.
|
||||
- **Banner fatigue**: A persistent banner can be annoying. → Mitigation: Session-scoped dismissal keeps it low-friction.
|
||||
36
openspec/changes/legal-disclaimers/proposal.md
Normal file
36
openspec/changes/legal-disclaimers/proposal.md
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
## Why
|
||||
|
||||
trails.cool is operated from Germany, which means German law (TMG, TDDDG, MStV, and GDPR via DSGVO) applies. There is currently no Impressum, no Terms of Service, and the privacy manifest isn't a full GDPR-compliant Datenschutzerklärung — this exposes the operator to legal risk (Abmahnungen are common for missing Impressum in Germany).
|
||||
|
||||
Separately, the service is in active early development. Users signing up today may find their accounts wiped if the operator decides to reset the database, or they may encounter breaking changes to the UI, URLs, or federation behavior. Current users have no signal that this is experimental, which sets wrong expectations and creates another liability surface.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Add an **Impressum** page at `/legal/imprint` with operator name, address, email, and responsible person per §5 TMG / §18 MStV
|
||||
- Expand the existing privacy manifest into a full **Datenschutzerklärung** at `/legal/privacy` — keep the human-readable summary but add the GDPR-required sections (legal basis, data subject rights, controller contact, right to complain)
|
||||
- Add a **Terms of Service** page at `/legal/terms` with:
|
||||
- Alpha / experimental status disclaimer (service is untested, under active development)
|
||||
- No warranty / no SLA / limitation of liability (within German consumer-protection limits)
|
||||
- Explicit statement that the database may be wiped without notice
|
||||
- User responsibility to export their own data (linking to existing export)
|
||||
- Acceptable use (no illegal content, no abuse)
|
||||
- Require **acknowledgement at signup**: a checkbox on the registration form confirming the user has read the Terms and understands the alpha status. Form cannot submit without it.
|
||||
- Add a persistent **alpha banner** on the Journal (thin, dismissible for the session) linking to the Terms with the line "trails.cool is in early development — data may be reset"
|
||||
- Add **footer links** to Impressum / Privacy / Terms on both apps (Journal and Planner)
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `legal-disclaimers`: Impressum page, Terms of Service with alpha disclaimer, full Datenschutzerklärung, signup acknowledgement flow, alpha banner, and footer links
|
||||
|
||||
### Modified Capabilities
|
||||
- `journal-auth`: Registration SHALL require explicit acknowledgement of the Terms of Service before account creation
|
||||
|
||||
## Impact
|
||||
|
||||
- **Legal risk reduction**: Closes the Impressum gap that triggers Abmahnungen in Germany
|
||||
- **User expectations**: Clear alpha signal reduces complaints if the DB is reset
|
||||
- **Code**: New routes in the Journal (`/legal/imprint`, `/legal/terms`, expanded `/legal/privacy`), footer component updates, registration form change
|
||||
- **i18n**: All legal copy needs EN + DE translations (DE is the authoritative version for German consumers)
|
||||
- **Database**: Add `terms_accepted_at` timestamp to the `users` table to record acknowledgement
|
||||
- **Operator info**: The operator's real name and address will be in the Impressum — this is a legal requirement and cannot be omitted
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Terms acknowledgement at signup
|
||||
The registration form SHALL require explicit acknowledgement of the Terms of Service before an account can be created.
|
||||
|
||||
#### Scenario: Checkbox required
|
||||
- **WHEN** a user views the registration form
|
||||
- **THEN** they see a required checkbox labeled "I have read and agree to the Terms of Service, including that trails.cool is in alpha and my data may be reset"
|
||||
- **AND** the checkbox label links to the Terms page
|
||||
|
||||
#### Scenario: Cannot submit without acknowledgement
|
||||
- **WHEN** a user attempts to register without checking the acknowledgement box
|
||||
- **THEN** the form blocks submission and shows a validation message
|
||||
|
||||
#### Scenario: Acknowledgement recorded
|
||||
- **WHEN** a user successfully registers
|
||||
- **THEN** the current timestamp is stored in `users.terms_accepted_at`
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
## ADDED Requirements
|
||||
|
||||
### Requirement: Impressum page
|
||||
The Journal SHALL serve an Impressum page at `/legal/imprint` containing the operator's legal information per §5 TMG and §18 MStV.
|
||||
|
||||
#### Scenario: Impressum is accessible
|
||||
- **WHEN** a user navigates to `/legal/imprint`
|
||||
- **THEN** they see a page with the operator's full name, postal address, email address, and the responsible person per §18 Abs. 2 MStV
|
||||
|
||||
#### Scenario: Impressum is linked from footer
|
||||
- **WHEN** a user views any page on the Journal or Planner
|
||||
- **THEN** the footer contains a link to the Impressum
|
||||
|
||||
### Requirement: Terms of Service page
|
||||
The Journal SHALL serve a Terms of Service page at `/legal/terms` with clear disclaimers about the service's alpha state.
|
||||
|
||||
#### Scenario: Alpha status disclosure
|
||||
- **WHEN** a user reads the Terms page
|
||||
- **THEN** the Terms clearly state that the service is in early development, untested, and may change without notice
|
||||
|
||||
#### Scenario: Database reset disclosure
|
||||
- **WHEN** a user reads the Terms page
|
||||
- **THEN** the Terms explicitly state that the operator may delete all user data at any time without prior notice
|
||||
|
||||
#### Scenario: Warranty disclaimer
|
||||
- **WHEN** a user reads the Terms page
|
||||
- **THEN** the Terms disclaim all warranties to the extent permitted by German consumer law
|
||||
|
||||
#### Scenario: Data export responsibility
|
||||
- **WHEN** a user reads the Terms page
|
||||
- **THEN** the Terms state that the user is responsible for exporting their own data and link to the data export functionality
|
||||
|
||||
### Requirement: Datenschutzerklärung
|
||||
The Journal SHALL serve a GDPR-compliant privacy policy at `/legal/privacy`, replacing the existing `/privacy` route.
|
||||
|
||||
#### Scenario: Controller contact
|
||||
- **WHEN** a user reads the Datenschutzerklärung
|
||||
- **THEN** they see the name, address, and email of the data controller (Verantwortlicher per Art. 4 Nr. 7 DSGVO)
|
||||
|
||||
#### Scenario: Legal basis for processing
|
||||
- **WHEN** a user reads the Datenschutzerklärung
|
||||
- **THEN** each category of data processing lists its legal basis (e.g., Art. 6 Abs. 1 lit. b for contract, lit. f for legitimate interest)
|
||||
|
||||
#### Scenario: Data subject rights
|
||||
- **WHEN** a user reads the Datenschutzerklärung
|
||||
- **THEN** they see their rights under GDPR: access (Art. 15), rectification (Art. 16), erasure (Art. 17), data portability (Art. 20), objection (Art. 21), and right to lodge a complaint with a supervisory authority
|
||||
|
||||
#### Scenario: Redirect from old privacy URL
|
||||
- **WHEN** a user navigates to `/privacy`
|
||||
- **THEN** they are redirected to `/legal/privacy` with a 301 status
|
||||
|
||||
### Requirement: Alpha banner
|
||||
The Journal SHALL display a persistent banner notifying users that the service is in alpha, dismissible for the browser session.
|
||||
|
||||
#### Scenario: Banner visible on first visit
|
||||
- **WHEN** a user visits any Journal page for the first time in a session
|
||||
- **THEN** a banner is shown with text stating trails.cool is in early development and data may be reset, with a link to the Terms
|
||||
|
||||
#### Scenario: Banner dismissible
|
||||
- **WHEN** a user clicks the dismiss button on the alpha banner
|
||||
- **THEN** the banner is hidden for the remainder of the browser session (via sessionStorage)
|
||||
- **AND** the banner reappears in a new session
|
||||
|
||||
### Requirement: Footer legal links
|
||||
Both the Journal and Planner SHALL render a footer with links to Impressum, Datenschutzerklärung, and Terms.
|
||||
|
||||
#### Scenario: Footer on Journal
|
||||
- **WHEN** a user views any Journal page
|
||||
- **THEN** the footer contains links labeled "Impressum", "Privacy", and "Terms" pointing to the respective legal pages
|
||||
|
||||
#### Scenario: Footer on Planner
|
||||
- **WHEN** a user views any Planner page
|
||||
- **THEN** the footer contains the same three legal links, pointing to the Journal instance (since the Planner is stateless and legal pages live on the Journal)
|
||||
46
openspec/changes/legal-disclaimers/tasks.md
Normal file
46
openspec/changes/legal-disclaimers/tasks.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
## 1. Operator Information
|
||||
|
||||
- [ ] 1.1 Decide what operator address to use on the Impressum (private address vs. business address service vs. lawyer's address) — legal requirement, must be reachable
|
||||
- [ ] 1.2 Create `apps/journal/app/lib/operator.ts` exporting the operator details (name, address, email, responsible person)
|
||||
|
||||
## 2. Legal Pages
|
||||
|
||||
- [ ] 2.1 Create `apps/journal/app/routes/legal.imprint.tsx` rendering the Impressum from the operator config
|
||||
- [ ] 2.2 Create `apps/journal/app/routes/legal.terms.tsx` with Terms of Service covering alpha status, warranty disclaimer, DB reset disclosure, and data export responsibility
|
||||
- [ ] 2.3 Move existing `apps/journal/app/routes/privacy.tsx` to `apps/journal/app/routes/legal.privacy.tsx` and expand with GDPR-required sections (controller, legal basis, data subject rights, complaint right)
|
||||
- [ ] 2.4 Add a redirect from `/privacy` to `/legal/privacy` with 301 status
|
||||
- [ ] 2.5 Register the new routes in `apps/journal/app/routes.ts`
|
||||
|
||||
## 3. i18n
|
||||
|
||||
- [ ] 3.1 Add German translations for all legal page copy (Impressum, Terms, Datenschutzerklärung) — DE is the authoritative version for German consumers
|
||||
- [ ] 3.2 Add English translations for the same pages
|
||||
- [ ] 3.3 Add translation keys for the alpha banner and signup acknowledgement checkbox
|
||||
|
||||
## 4. Signup Acknowledgement
|
||||
|
||||
- [ ] 4.1 Add `terms_accepted_at` column (timestamp, nullable initially for existing users) to the `users` table in `packages/db/src/schema/journal.ts`
|
||||
- [ ] 4.2 Run `pnpm db:push` to apply the schema
|
||||
- [ ] 4.3 Add a required checkbox to `apps/journal/app/routes/auth.register.tsx` linking to the Terms
|
||||
- [ ] 4.4 Client-side: block form submission if checkbox is unchecked; show validation message
|
||||
- [ ] 4.5 Server-side: reject registration requests in `api.auth.register.ts` that don't include `termsAccepted: true`
|
||||
- [ ] 4.6 On successful registration, set `users.terms_accepted_at = NOW()`
|
||||
|
||||
## 5. Alpha Banner
|
||||
|
||||
- [ ] 5.1 Create `apps/journal/app/components/AlphaBanner.tsx` — thin banner with message and dismiss button, reads `sessionStorage.alphaBannerDismissed`
|
||||
- [ ] 5.2 Render `<AlphaBanner />` in the Journal root layout
|
||||
- [ ] 5.3 Add corresponding i18n strings
|
||||
|
||||
## 6. Footer Links
|
||||
|
||||
- [ ] 6.1 Create `apps/journal/app/components/Footer.tsx` with links to Impressum, Privacy, Terms
|
||||
- [ ] 6.2 Add the footer to the Journal root layout
|
||||
- [ ] 6.3 Add a footer to the Planner layout linking to the Journal's legal pages (absolute URLs, since Planner is stateless)
|
||||
|
||||
## 7. Review & Publish
|
||||
|
||||
- [ ] 7.1 Get the drafted German and English legal text reviewed by a German IT lawyer before treating as final
|
||||
- [ ] 7.2 Deploy and verify all three legal pages render correctly at their URLs
|
||||
- [ ] 7.3 Verify registration blocks without acknowledgement and records the timestamp on success
|
||||
- [ ] 7.4 Verify the alpha banner appears and dismisses correctly
|
||||
Loading…
Add table
Add a link
Reference in a new issue