# CLAUDE.md — stats.wise-relations.com

## Identity

**Name:** wise-relations Webanalyse
**Domain:** stats.wise-relations.com
**Operator:** Helm & Nagel GmbH (Aßlar, HRB 7706)
**Upstream:** https://github.com/arp242/goatcounter (GoatCounter by Martin Tournoij)
**License:** EUPL-1.2

Privater, deutschsprachiger B2B-Fork von GoatCounter. Wird als kostenlose,
DSGVO-konforme Webanalyse ohne Cookies / ohne personenbezogenes Tracking für
den deutschen Mittelstand betrieben.

## Repository

- **GitHub:** `atraining/stats.wise-relations.com` — **PRIVATE**
- **Default dev branch:** `de-fork` (alle Anpassungen laufen hier)
- **Upstream tracking branch:** `main` (bleibt clean auf Upstream für Rebases)
- Remote setup:
  ```
  origin   → github.com/atraining/stats.wise-relations.com (private)
  upstream → github.com/arp242/goatcounter (public)
  ```

## Branch-Strategie

```
main (upstream/main Spiegel)
  └─ de-fork (unsere Anpassungen, wird deployed)
```

**Upstream-Update ziehen:**
```bash
git fetch upstream
git checkout main && git merge --ff-only upstream/main
git checkout de-fork && git rebase main
# Konflikte in tpl/*.gohtml, tpl.go, serve.go nachziehen
```

## EUPL-1.2 Compliance

Die Lizenz erlaubt zwei Wege zur Quellcode-Bereitstellung (Artikel 5):
**Wir haben uns für Option A entschieden:**

- **Option A — Maschinenlesbarer Download** ✅ (gewählt)
  - `deploy/build-source-zip.sh` erzeugt nach jedem Deploy `/opt/stats/public/source.zip`
  - Caddy liefert die ZIP unter `https://stats.wise-relations.com/source.zip` aus
  - Impressum verlinkt nur diese URL (kein GitHub-Link mehr)
- Option B — Repository-Link (nicht gewählt; würde Fork öffentlich zeigen)

**Pflicht-Artefakte im Repo:**
- `LICENSE` — EUPL-1.2 Volltext (unverändert vom Upstream)
- `NOTICE` — Modifikationsliste (EUPL Art. 5 Attribution-Pflicht)
- `deploy/legal/impressum.md` — verlinkt `/source.zip`

**Wichtig:** Die SaaS-Bereitstellung (Artikel 1 "providing access to its
essential functionalities") zählt als Distribution und triggert die Pflicht.
Die ZIP muss gepflegt werden, solange die Plattform läuft.

## Branding / UX-Entscheidungen

- **Logo:** 3-Balken-Chart SVG (monochrom, `currentColor`) ersetzt die
  GoatCounter-Ziege. Alle Favicons neu gerendert aus dem SVG.
- **Tonalität:** B2B, Sie-Form, nüchtern. **Kein** Open-Source-Marketing
  ("quelloffen", "Matomo breath of fresh air" etc. sind raus — EUPL-Pflicht
  ist allein durch `/source.zip` + Impressum erfüllt).
- **Features-Grid:** CSS erwartet `<div>` = Zeile mit 2× `<p>` à 49%.
  9 Features in 5 Zeilen (Datensparsam, Schlank, Besucherzählung ohne
  Cookies, Sinnvolle Kennzahlen, Klare Bedienung, Barrierefreiheit, Eigene
  Daten, Server in Deutschland, Script-Tag-Einbindung).
- **Preise:** "Kostenlos im üblichen Rahmen. Bei höherem Volumen kurz
  melden." Bewusst **keine 100.000-Pageviews-Schwelle** in der Werbung
  (klingt nach Marketingschwelle).
- **Footer:** nur Startseite, Kontakt, Hilfe, Impressum, Datenschutz, AGB +
  wise-relations.com + Helm & Nagel. **Kein GitHub-Link** im Footer.
- **Kontakt:** `info@wise-relations.com` + `+49 6441 8994005` — Formular,
  kein "GitHub issue" mehr.
- **`<hr>`-Dekoration:** Upstream rendert einen Goat-Logo via Base64 in CSS.
  Komplett entfernt aus `public/shared.css` und `home.gohtml`.

## Inhaltliche Anpassungen (übersetzt)

- **Templates:** `home`, `why`, `_contact`, `signup`, `_bottom_links`,
  `_top`, `help` — alle auf Deutsch Sie-Form
- **Sidebar nav** (`tpl.go` Zeilen 186–220): Grundlagen, Weitere
  Datenquellen, So machen Sie…, Weitere Themen, Rechtliches
- **Help-Artikel** (`tpl/help/*.md`): 24 Artikel übersetzt.
  - Code-Blöcke **verbatim** (keine Übersetzung)
  - Beispiel-URLs: `yoursite.goatcounter.com` → `meinkonto.stats.wise-relations.com`
  - Upstream-persönliche Referenzen (`martin@arp242.net`, `arp242.net`) raus
- **Legal:** `deploy/legal/impressum.md`, `datenschutzerklaerung.md`, `agb.md`
  nach deutschem DDG / MStV / BGB / DSGVO
- **Reserved Subdomains:** `wise-relations-stats-reserved.yaml` verhindert
  Registrierung gefährlicher Tenant-Namen (admin, stats, static, www, mail)

## Technische Patches

**`cmd/goatcounter/serve.go`** (Zeilen ~298–305):
Apex/www-Swap — Apex serviert die Website, `www.` redirected zur Apex-Domain.
Upstream-Default ist umgekehrt ("www is canonical"), das passt für
wise-relations nicht.

```go
if saas {
    d := znet.RemovePort(domain.String())
    hosts[d] = handlers.NewWebsite(db, dev.Bool())
    hosts["www."+d] = zhttp.RedirectHost("https://" + domain.String())
    ...
}
```

**`tpl.go`** — Sidebar-Labels für Help in Deutsch (betrifft auch die
Article-H1-Überschriften via `help_hdr`).

## SaaS-Mechanik (wichtig für lokale Tests)

- **`serve` (single-tenant)** — Apex zeigt `serve_newsite.gohtml` (upstream,
  Englisch). Nur für einzelne Websites ohne Tenant-Modell.
- **`saas` (multi-tenant)** — Apex zeigt Marketing-Site (`home.gohtml`, also
  unsere übersetzte Version). Das ist der **Produktions-Modus**.
  - Row-level multi-tenant in **einer** SQLite-Datei (`sites`-Tabelle,
    `site_id` als FK auf User)
  - Tenant-Zugriff per Subdomain: `meinkonto.stats.wise-relations.com`
  - Caddy on-demand TLS holt automatisch Let's-Encrypt-Zertifikat pro
    Subdomain (HTTP-01)

**Tenant-Domain-Validierung:** GoatCounter lehnt Hostnames mit weniger als 2
Labels ab. Für Tests in `-dev`-Mode daher `stats.localhost:8100` verwenden,
nicht bare `localhost:8100`.

## Lokaler Dev-Betrieb

**Build (CGO erforderlich für SQLite):**
```bash
cd /c/Users/train/dev/stats.wise-relations.com
PATH="$HOME/sdk/mingw64/bin:$HOME/sdk/go/bin:$PATH" \
    CGO_ENABLED=1 go build -o goatcounter.exe ./cmd/goatcounter
```

- **Go:** `~/sdk/go/bin/go.exe` (1.23+)
- **MinGW:** `~/sdk/mingw64/bin/` (portable WinLibs, GCC 13.2.0) — liefert
  `gcc` für `mattn/go-sqlite3` und `zgo.at/zdb-drivers/go-sqlite3`.
  Kein systemweites Install, kein Admin.

**Run (saas mode, dev):**
```bash
./goatcounter.exe saas -dev -listen :8100 -tls http \
    -domain stats.localhost:8100,static.stats.localhost:8100 \
    -db sqlite+./data/db.sqlite
```

- Browser (Chrome/Edge/Firefox) lösen `*.localhost` automatisch auf 127.0.0.1 auf
- `-dev` mode lädt `tpl/*.gohtml` bei jedem Request neu — kein Rebuild nötig
  für Template-Änderungen, nur für Go-Code-Änderungen

**Docker-Preview:** `deploy/Dockerfile.local` (Alpine + CGO). Nicht für
Produktion. Produktion läuft direkt auf dem Host per systemd.

## Produktion

**Infrastruktur (Option 2 — Caddy-on-Host):**
- Hetzner VPS — derselbe Host, aber **keine Abhängigkeit** zu lobout.
  Caddy läuft als System-Service auf dem Host (nicht im Docker-Stack).
  Server-Bauanleitung: separates Repo `atraining/infra` (privat).
- Jedes Projekt deployt sein eigenes Caddy-Fragment nach
  `/etc/caddy/conf.d/<name>.caddy`. Host-Caddy importiert alle Fragmente.
  stats liefert `deploy/caddy/stats.caddy`.
- `deploy-on-host.sh` baut Binary, erzeugt `source.zip`, kopiert Snippet,
  reloadet Caddy, startet Services neu. Idempotent.
- GoatCounter lauscht auf `127.0.0.1:8100` (systemd, `deploy/stats.service`).
- Ask-Sidecar (`deploy/ask-server.py`, `stats-ask.service`) auf
  `127.0.0.1:8101` — validiert Tenant-Domains vor TLS-Issue.
  Caddy-Ask-Endpoint `http://127.0.0.1:8101/ask` sitzt im globalen Block
  des Host-Caddyfiles (siehe infra-Repo `base-config/Caddyfile`).

**DNS:**
```
stats.wise-relations.com         A  <vps-ip>
*.stats.wise-relations.com       A  <vps-ip>
static.stats.wise-relations.com  A  <vps-ip>
```

**Deploy-Ablauf:**
```bash
cd /opt/stats/src
git pull origin de-fork
sudo bash deploy/deploy-on-host.sh
```

`deploy-on-host.sh` übernimmt Build, source.zip, Caddy-Snippet-Install,
Caddy-Validate+Reload, Service-Restart, Health-Check. Idempotent.

**Erstmalige Einrichtung:** Einmalig `sudo bash deploy/install.sh ...`
(legt User, Verzeichnisse, systemd-Units, erste Admin-Site an). Danach
immer `deploy-on-host.sh`.

**Pfade auf VPS:**
- `/opt/stats/src` — Git-Checkout (de-fork Branch)
- `/opt/stats/goatcounter` — kompilierte Binary
- `/opt/stats/data/db.sqlite` — einzige SQLite-DB (alle Tenants)
- `/opt/stats/backups/` — rotierende DB-Snapshots (cron)
- `/opt/stats/public/source.zip` — EUPL-Quellcode-Download

## Projektstruktur (relevante Ergänzungen zum Upstream)

```
stats.wise-relations.com/
├── CLAUDE.md                  # diese Datei
├── NOTICE                     # EUPL Modification-Liste
├── LICENSE                    # EUPL-1.2 (unverändert)
│
├── deploy/                    # Deployment-Artefakte
│   ├── README.md
│   ├── install.sh             # One-shot-Installer für den VPS
│   ├── stats.service          # systemd unit für goatcounter
│   ├── stats-ask.service      # systemd unit für Caddy-Ask-Sidecar
│   ├── ask-server.py          # Tenant-Domain-Validator für on-demand TLS
│   ├── deploy-on-host.sh      # Kontinuierliches Deploy (idempotent)
│   ├── caddy/stats.caddy      # Apex + www + static + *.tenant + /source.zip
│   │                            (wird nach /etc/caddy/conf.d/stats.caddy kopiert)
│   ├── Dockerfile.local       # nur für lokale Preview (Alpine + CGO)
│   ├── build-source-zip.sh    # EUPL-Quellcode-Archiv-Generator
│   ├── backup.sh              # SQLite-Backup-Skript (cron)
│   ├── env.example            # Umgebungsvariablen für systemd-Unit
│   └── legal/
│       ├── impressum.md
│       ├── datenschutzerklaerung.md
│       └── agb.md
│
├── tpl/                       # Templates (überwiegend upstream,
│   │                            übersetzt/angepasst)
│   ├── _top.gohtml            # lang="de", Title
│   ├── _bottom_links.gohtml   # Footer (kein GitHub-Link)
│   ├── _contact.gohtml        # Deutsches Kontakt-Formular
│   ├── home.gohtml            # Marketing, 9 Features in 5 Zeilen
│   ├── why.gohtml             # "Warum diese Webanalyse?"
│   ├── signup.gohtml          # Sie-Form, AGB-Checkbox-Text
│   ├── help.gohtml            # Deutsche Sidebar-Labels
│   └── help/*.md              # 24 Artikel auf Deutsch
│
├── public/                    # Static Assets
│   ├── logo.svg               # 3-Balken-Chart (currentColor)
│   ├── logo.png               # 200×200 aus SVG
│   ├── favicon/               # 16, 32, 180, safari-pinned, aus SVG
│   └── shared.css             # <hr>-Goat-Dekoration entfernt
│
├── cmd/goatcounter/
│   └── serve.go               # Apex/www-Swap-Patch
│
└── tpl.go                     # Deutsche Sidebar-Nav-Labels
```

## Contact-Flow

Upstream hatte nur Mailto. Unser `_contact.gohtml` hat:
- E-Mail: `info@wise-relations.com`
- Telefon: `+49 6441 8994005` (Mo–Fr, 9–17 Uhr)
- HTML-Formular → `POST {{.Base}}/contact` (Backend-Handler: `handlers.contact`)
- Honeypot-Feld `turing` (Upstream-Default, "9 eintragen")

## Offene Punkte

1. **Produktions-Deploy** — `install.sh` auf VPS ausführen, TLS holen, erste
   Admin-Site anlegen. Build-Skript für `source.zip` in systemd-Service oder
   deploy-Skript integrieren.
2. **`de-fork` als Default-Branch** auf GitHub setzen (derzeit ist es
   wahrscheinlich noch `main`) — damit `git clone` standardmäßig den richtigen
   Branch zieht.
3. **Admin-UI-Audit** — `tpl/i18n_*.gohtml`, `tpl/bosmang_geoip.gohtml`,
   Signup-Flow intern. Niedrige Priorität (nur Admins sehen es).
4. **Favicon.ico** — existiert noch nicht, optional nachreichen.
5. **Monitoring** — keine Alarme, keine Uptime-Checks. Für Pilot-Phase OK.

## Design-Entscheidungen (festgehalten)

- **Kein Docker in Produktion** — GoatCounter braucht CGO für SQLite; ein
  schlankes systemd-Setup hat weniger Overhead und direkteren Zugriff auf
  die SQLite-Datei (kein Volume-Mount-Drama).
- **Kein getrennter DB-Server** — SQLite in WAL-Mode reicht für die Last.
  Multi-Tenancy ist Row-Level, alle Tenants teilen sich eine DB-Datei.
- **Kein Cloudflare / kein DDoS-Schutz** — Hetzner stellt Basis-Schutz. Für
  unsere Traffic-Größe (kein Consumer-Produkt) genügt das.
- **Keine Anmeldung / kein Passwort-Recovery für die Plattform** — Upstream
  hat Magic-Link + Passwort. Beibehalten, keine eigene Auth-Implementation.
- **Keine Zahlungsabwicklung** — Plattform ist kostenlos (bis ~100k PV),
  höheres Volumen via E-Mail-Absprache. Keine Stripe/SEPA-Integration.
