Files
deklarix/CLAUDE.md
noroot 31caa8f66a feat: add web layer (internal/web) and wire it into main.go
Routing, html/template layout/content pattern, and the Pre-Publish
check flow: POST /pruefen runs extraction (Stufe 1) then rules.Evaluate
(Stufe 2) and renders the result as an htmx fragment. Nothing is
persisted yet — that's the next step (wiring internal/store in).

The needsClarification case is rendered explicitly as a request for
more information rather than "no findings", matching the core
principle. Every result carries the legal-advice disclaimer required by
CLAUDE.md's guardrails.

Server depends on a narrow Extractor interface rather than *extract.
Client directly, so tests inject a fake instead of calling the real API
— internal/web's test suite never touches the network. htmx is vendored
locally (internal/web/static/htmx.min.js) instead of loaded from a CDN,
keeping the UI usable without runtime internet access.

cmd/deklarix/main.go now wires all of this together: reads
ANTHROPIC_API_KEY (required) and RULES_DIR (default "rules"), builds
the extract client and loads the rule set, and serves web.Server instead
of the old inline health-only mux.

This exposed the same crash-loop risk fixed earlier for DATABASE_URL:
postinst's start guard only checked DATABASE_URL, so a fresh install
would now crash-loop on a missing ANTHROPIC_API_KEY instead. The guard
checks both. scripts/build.sh also now ships rules/*.yaml into the .deb
under /usr/share/deklarix/rules (not a conffile — rules are updated via
the release pipeline, never hand-edited on a server), and
deklarix.env.example points RULES_DIR there by default.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 14:23:45 +02:00

389 lines
14 KiB
Markdown

# Deklarix
> Prüfung und Nachweis für Werbe-Content. Pre-Publish-Kennzeichnungsprüfung
> und revisionssichere Archivierung für Creator, Agenturen, Marken und
> Kanzleien. `deklarix.de` / `deklarix.com`
---
## Was das Projekt ist
Drei Funktionen, mehr nicht:
1. **Pre-Publish-Prüfung** — Caption, Standbild und Vertragslage rein,
Risikobewertung mit konkreten Korrekturvorschlägen raus.
2. **Beweissicherung** — jeder veröffentlichte Beitrag wird mit Hash und
Zeitstempel archiviert: Screenshot, Caption, sichtbare Kennzeichnung,
verknüpfter Vertrag, Briefing.
3. **Verantwortungsmatrix** — wer hat was vorgegeben, wer hat freigegeben,
wer schuldet welche Prüfung (Creator / Agentur / Marke).
**Wer nutzt es, wer zahlt:** Creator nutzen es täglich, zahlen wenig
(Reichweite/Referenz). Agenturen und Marken zahlen richtig — sie haften
über § 8 Abs. 2 UWG mit und brauchen Freigabe-Workflow plus
Nachweisarchiv. Kanzleien lizenzieren Deklarix als Whitelabel für ihre
Mandanten (zahlen für Software, nicht für Mandantenzugang); das
Kanzlei-Verzeichnis im Produkt bleibt kostenlos.
**Rollen von Anfang an.** Eine Submission gehört nicht einem Nutzer,
sondern hat Beteiligte mit Rollen (`creator`, `agentur`, `marke`,
`kanzlei`). Alle vier existieren im Datenmodell ab dem ersten Commit.
**Kein Demo-Shortcut.** Es wird direkt produktionsreif entwickelt —
keine reduzierte Zwischenstufe mit manuell eingetragenen Daten oder ohne
Login als Selbstzweck.
---
## Kernprinzip: das Modell entscheidet nicht
**Das LLM extrahiert Fakten. Das Regelwerk in Go fällt das Urteil.**
- **Stufe 1 — Extraktion (Claude API):** Eingabe Caption plus Bild/Video,
Ausgabe striktes JSON (Plattform, Gegenleistung, Kennzeichnung
vorhanden/Kanal/Wortlaut/Position, Produktkategorie, Verlinkungen, ...).
Ist die Extraktion unsicher, liefert sie `unklar` — nie eine geratene
Bewertung.
- **Stufe 2 — Bewertung (Go, deterministisch):** Regelwerk als
versionierte YAML-Dateien (`rules/`). Jedes Finding referenziert
Regel-ID und Version (z. B. `WK-004 v3`). Regeln sind
plattformunabhängig — UWG/MStV gelten überall gleich, nur
Darstellungsdetails unterscheiden sich; die gehören in
Regelbedingungen/Extraktion, nie in separate Regelsätze pro Plattform.
**Absolute Regel:** Das Modell nennt niemals selbst Normen, Urteile oder
Randnummern. Fundstellen stehen ausschließlich in den kuratierten
YAML-Regeln. Eine halluzinierte Fundstelle ist in diesem Markt ein
Totalschaden.
---
## Stack
Bewusst langweilig, ein Binary, lokal lauffähig:
| Bereich | Wahl |
|---|---|
| Sprache | Go 1.26, Standard-Library wo möglich |
| Web | `net/http` + `html/template` + htmx, kein Frontend-Build, kein React |
| Datenbank | PostgreSQL über `pgx`, Migrationen über `golang-migrate` |
| PDF | serverseitig erzeugt |
| Zeitstempel | RFC-3161 von einer öffentlichen TSA |
| Extraktion | Claude API (nur Fakten-Extraktion, siehe oben) |
| Design | `design/enterprise.css` (Enterprise Light Theme, enconf-Pattern, Primärfarbe `#1677ff`) |
Kein Docker für die App selbst, kein React, keine Queue. Neue
Abhängigkeiten nur nach Rückfrage.
**Warum Postgres statt SQLite:** Das Produkt läuft von Anfang an für
hunderte gleichzeitige Nutzer über mehrere Mandanten (Creator, Agentur,
Marke, Kanzlei). Ein revisionssicheres Beweisarchiv braucht robuste
Concurrent-Writes sowie Point-in-Time-Recovery/Replikation — das ist mit
SQLites Single-Writer-Modell nicht sauber zu bauen. Gehostet wird
Managed Postgres in der EU (DSGVO).
**Pfad:** `/var/www/deklarix` | **Git:** `https://git.netcell-it.de/projekte/deklarix` | **Branch:** `main`
---
## Projektstruktur
```
/var/www/deklarix/
├── cmd/
│ └── deklarix/
│ └── main.go # Entry Point, HTTP-Server
├── internal/
│ ├── extract/ # Claude-API-Aufruf, JSON-Schema, Validierung
│ ├── rules/ # YAML-Loader, Auswertung, Versionierung
│ ├── evidence/ # Hashing, Zeitstempel, Append-only-Log
│ ├── dossier/ # PDF-Erzeugung
│ ├── store/ # Postgres, Migrationen
│ └── web/ # Handler, Templates
├── rules/ # YAML-Regeln, versioniert im Git
├── testdata/
│ └── golden/ # Golden-Tests: Beispielbeiträge + erwartetes Ergebnis
├── design/
│ └── enterprise.css # Design-System (enconf-Basis)
├── packaging/
│ └── DEBIAN/
│ └── control.tmpl # .deb Package-Control-Template
├── scripts/
│ ├── build.sh # Cross-Compile amd64 + arm64
│ ├── test.sh # Tests + vet + build-check
│ └── release.sh # Vollständiger Release-Prozess
├── go.mod
├── go.sum
└── CLAUDE.md
```
---
## Datenmodell
Fünf Tabellen, mehr braucht der MVP nicht:
- `submission` — ein eingereichter Beitrag, Status, Zeitpunkte
- `asset` — Bild oder Datei, Pfad, SHA-256
- `extraction` — das JSON aus Stufe 1, Modellversion, Prompt-Version
- `finding` — Ergebnis pro Regel: Regel-ID, Regel-Version, Schwere, Text
- `evidence_package` — Dossier, Hash, Zeitstempel-Token, erzeugt am
- `participant` — Beteiligter an einer Submission mit Rolle
(`creator`, `agentur`, `marke`, `kanzlei`) und Beitrag zur
Verantwortungsmatrix (wer hat vorgegeben, wer freigegeben)
**Append-only.** Kein UPDATE auf `finding`, `extraction` oder
`evidence_package`. Korrekturen sind neue Zeilen mit Verweis auf die alte.
Ein Beweisarchiv, in dem man Zeilen ändern kann, ist kein Beweisarchiv.
**Beweiskette:** SHA-256 über jedes Asset und über die kanonisierte
JSON-Repräsentation der Metadaten, RFC-3161-Zeitstempel über diesen Hash.
Dossier-PDF enthält Beitrag, Caption, Kennzeichnung, Findings mit
Fundstellen, Vertrags- und Briefing-Bezug, Verantwortungsmatrix, alle
Hashes und das Zeitstempel-Token. Vollständiger Export muss für den
Nutzer jederzeit möglich sein — wer kündigt, bekommt sein Archiv.
---
## Go Commands
```bash
export PATH=$PATH:/usr/local/go/bin # Immer setzen!
# Entwickeln
go run ./cmd/deklarix/
# Tests
./scripts/test.sh
# oder direkt:
go test -race ./...
go vet ./...
# Build (amd64 + arm64)
./scripts/build.sh 1.0.0
# Build (nur amd64)
./scripts/build.sh 1.0.0 amd64
```
---
## Build & Release-Prozess
### Versioning (Semantic Versioning: MAJOR.MINOR.PATCH)
- **MAJOR** — Breaking changes, API-Inkompatibilitäten
- **MINOR** — Neue Features, rückwärtskompatibel
- **PATCH** — Bugfixes
### Release-Schritte
```bash
# 1. Alle Änderungen committen
git add -p && git commit -m "feat: ..."
# 2. Release-Skript (macht Tests → .deb-Build → Gitea-Upload → Tag → Push)
./scripts/release.sh 1.2.0
# Danach liegt in dist/:
# deklarix_1.2.0_amd64.deb
# deklarix_1.2.0_arm64.deb
```
### Was das Release-Skript tut
1. Prüft: sauberer Git-Status (keine uncommitted changes)
2. Führt `./scripts/test.sh` aus (vet + race tests + build-check, braucht `DATABASE_URL`)
3. Baut `.deb`-Pakete für `linux/amd64` und `linux/arm64` (`scripts/build.sh`)
4. Lädt die Pakete in Giteas Debian-Paketregistrierung hoch (Kanal `testing`,
Suites `bookworm` + `trixie`) — siehe „Deployment“ unten
5. Setzt Git-Tag `v<version>` mit Annotierung
6. Pusht `main` + Tag nach `origin`
### Deployment (apt, Infrastruktur-Standard wie bei enconf)
Updates laufen ausschließlich über ein apt-Paket, nie über manuelles
Kopieren eines Binaries. `.deb`-Pakete landen in Giteas eingebauter
Debian-Paketregistrierung unter der Gitea-Org `projekte` (gleicher
Mechanismus wie beim Schwesterprojekt enconf-webpanel). Zielserver
richten sich einmalig ein:
```bash
sudo mkdir -p /etc/apt/keyrings
sudo curl -fsSL https://git.netcell-it.de/api/packages/projekte/debian/repository.key \
-o /etc/apt/keyrings/deklarix-gitea.asc
echo "deb [signed-by=/etc/apt/keyrings/deklarix-gitea.asc] https://git.netcell-it.de/api/packages/projekte/debian $(lsb_release -cs) testing" \
| sudo tee /etc/apt/sources.list.d/deklarix.list
sudo apt-get update && sudo apt-get install deklarix
```
Danach: `apt upgrade` für Updates, `systemctl {start,stop,status} deklarix`
für den Dienst. Konfiguration liegt in `/etc/deklarix/deklarix.env`
(aus `deklarix.env.example` beim Erstinstall kopiert, `DATABASE_URL` ist
dort absichtlich auskommentiert — der Dienst startet erst, wenn sie
gesetzt ist; `postinst` startet ihn sonst bewusst nicht, um keine
Crash-Restart-Schleife gegen einen Platzhalter-Host zu erzeugen).
Aktuell gibt es nur den Kanal `testing` (kein `stable` — es gibt noch
keine Kunden, für die eine Trennung nötig wäre).
---
## Testing-Pattern & Qualitätssicherung
```go
// Datei: internal/web/health_test.go
package web_test
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestHealth(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/health", nil)
w := httptest.NewRecorder()
HealthHandler(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w.Code)
}
}
```
- Tests liegen neben dem Code: `paket/foo_test.go`
- Package: `package foo_test` (Black-Box-Test) oder `package foo` (White-Box)
- Race-Detector immer an: `go test -race ./...`
- Tabellenbasierte Tests für mehrere Inputs
**DATABASE_URL und Testsystem.** `internal/store` hat Integrationstests
gegen echtes Postgres (u. a. die Append-only-Garantie). Ohne
`DATABASE_URL` überspringt `go test` diese Tests einfach — praktisch für
schnelle lokale Iteration ohne lokales Postgres. `./scripts/test.sh`
(und damit `release.sh`) ist strenger: es bricht ohne `DATABASE_URL` mit
Fehler ab, statt die Append-only-Garantie still zu überspringen. Es
zieht dafür aber **kein** eigenes Postgres hoch (kein Docker-Zwang auf
dem lokalen Dev-Rechner) — das ist Aufgabe des dedizierten Testsystems,
dort muss `DATABASE_URL` vorkonfiguriert sein.
**Golden-Test-Suite ist das eigentliche Asset des Projekts, nicht die
UI.** 30 bis 50 Beispielbeiträge in `testdata/golden/`, abgeleitet aus
veröffentlichten Urteilen und Beanstandungen der Medienanstalten, jeweils
mit erwarteten Findings. Jede Regeländerung läuft gegen die komplette
Suite. Eine Regeländerung, die ein Golden-Ergebnis kippt, wird nicht
gemerged, ohne dass der erwartete Wert bewusst und dokumentiert angepasst
wird. Extraktion wird separat getestet: fixierte Eingaben, geprüfte
JSON-Ausgabe.
---
## Leitplanken, die dauerhaft gelten
- **Keine Rechtsdienstleistung.** Deklarix ist ein Werkzeug, keine
Rechtsberatung. Keine Formulierung darf eine individuelle rechtliche
Bewertung suggerieren. Auf jedem Screen mit einem Ergebnis steht ein
entsprechender Hinweis. Keine Erfolgsaussichten-Aussagen.
- **Keine Versicherungssprache.** Nie „Abmahnschutz“, „abgesichert“,
„geschützt“, „Kostenübernahme“ — sonst erlaubnispflichtiges
Versicherungsgeschäft. Immer „Prüfung“, „Hinweis“, „Dokumentation“,
„Nachweis“.
- **Keine erfundenen Fundstellen.** Jede Norm und jedes Urteil steht in
einer Regel-YAML und wurde dort von Hand geprüft.
- **Nur eigene Inhalte.** Kein Scraping fremder Accounts, keine fremden
Posts als Demo-Material. Demo-Material ist synthetisch. Nutzerinhalte
kommen über eigenen OAuth-Zugang oder Datenexport.
- **DSGVO und Hosting in der EU.** Löschkonzept von Anfang an mitdenken,
aber Beweisarchiv und Löschpflicht sauber gegeneinander abgrenzen
(offener Punkt, siehe unten).
- **Berufsrecht der Anwälte (§ 49b Abs. 3 S. 1 BRAO).** Kein Routing
(kein Button, der einen Fall an eine Kanzlei übergibt — Nutzer wählt
selbst aus dem Verzeichnis), kein ergebnisabhängiger Preis, keine
Sachvorteile an gelistete Kanzleien, Verzeichnis bleibt kostenlos
(BGH IX ZR 89/23: entgeltliche Mandatsvermittlung ist nichtig). Vor dem
ersten Kanzlei-Vertrag: formlose Auskunft der zuständigen
Rechtsanwaltskammer einholen.
---
## Domains
| Domain | Verwendung |
|--------|-----------|
| deklarix.de | Primär |
| deklarix.com | Redirect / International |
---
## Wichtige Hinweise
### Go PATH
```bash
# Immer setzen — ist nicht im Standard-PATH des Servers
export PATH=$PATH:/usr/local/go/bin
```
### Git Push
```bash
git push origin main
# Remote: https://git.netcell-it.de/projekte/deklarix.git
```
### Server-Prozess
```bash
# Dienst läuft über systemd (installiert per apt, siehe Deployment oben)
sudo systemctl start deklarix
sudo systemctl status deklarix
# Config: /etc/deklarix/deklarix.env (DATABASE_URL, PORT, ANTHROPIC_API_KEY, RULES_DIR)
# Logs prüfen
journalctl -u deklarix -f
```
### Bekannte Server
| Rolle | Host | Zugang |
|---|---|---|
| Testsystem (DB-Integrationstests, apt-Verifikation) | 89.163.205.4 | SSH `noroot`, sudo passwortlos |
| Installer-Endpoint (`get.deklarix.com`) | 89.163.205.110 | noch nicht eingerichtet |
---
## Arbeitsweise für Claude Code
- Kleine Commits, ein Thema pro Commit
- Erst Test oder Golden-Fall, dann Implementierung
- Keine neue Abhängigkeit ohne Rückfrage
- Keine Umbenennung von Regel-IDs; Regeln werden versioniert, nicht
ersetzt
- Fehler nicht verschlucken, keine stillen Fallbacks bei der Extraktion
- Bei Unsicherheit über eine Rechtsfrage: nicht raten, sondern als offene
Frage in `rules/OPEN.md` notieren
- Der Code-RAG dient der Orientierung im Bestand, nicht als Nachweis von
Korrektheit. Verifiziert wird durch Compiler, Tests und die
Golden-Suite.
## Vor Änderungen
1. `go vet ./...` — keine Fehler
2. `./scripts/test.sh` — alle Tests grün
3. Bestehenden Code lesen — nicht raten
## Nach Änderungen
1. `./scripts/test.sh` → 0 Fehler
2. `./scripts/build.sh <version>` → erfolgreich
3. Commit mit semantischer Message: `feat:`, `fix:`, `refactor:`, `docs:`
4. Bei Release: `./scripts/release.sh <version>`
---
## Offene Punkte
- Umfang des Regelsatzes für den ersten Pilotkunden festlegen
- **TSA für die Zeitstempel:** interimsweise FreeTSA.org (frei, RFC-3161-
konform, siehe `internal/evidence.DefaultTSAURL`) — funktioniert
technisch, ist aber **nicht eIDAS-qualifiziert**. Vor echtem
Kundeneinsatz auf einen eIDAS-qualifizierten Zeitstempeldienst (z. B.
D-Trust, Bundesdruckerei) umstellen, der die gesetzliche
Vermutungswirkung nach eIDAS Art. 41 hat — dafür ist ein Vertrag/Account
nötig, kein anonymer HTTP-Call.
- Löschkonzept versus Beweisarchiv sauber ausformulieren
- Markenanmeldung Deklarix, sobald erster Kunde zahlt