Files
deklarix/CLAUDE.md
noroot 5156fe62da feat: wire auth into the web layer (Schritt 2, part 2/2)
Registration creates a new account plus its first user; login
authenticates an existing one; both set a deklarix_session cookie
(HttpOnly, SameSite=Strict, Secure only when the request itself came
over TLS — hardcoding Secure=true would break local http://localhost
development, since browsers won't store a Secure cookie over plaintext).

requirePage protects full-page GETs (redirects to /login); requireAPI
protects the htmx/download endpoints (401, since those are only ever
called from an already-authenticated page — an unauthenticated hit
there is the exception, e.g. a session expiring mid-use).

handleCheck now creates submissions under the current account.
handleArchive and handleDossierDownload compare the submission's
account against the caller's and return 404 on mismatch — not 403,
which would confirm the ID exists to a different tenant. Login failure
uses the same message for "no such email" and "wrong password" to avoid
account enumeration.

Restructured templates along the way: layout.html now only holds
reusable fragments ("head", "nav"); each full page (index/login/register)
is its own top-level named template. The previous layout+content nesting
would have broken the moment a second page defined "content" — Go's
html/template keys blocks by name across the whole parsed set, not per
file, so two pages both defining "content" would silently overwrite each
other.

Verified against a real running instance (not just Go's test recorder):
started the compiled binary against a fresh Postgres and drove the whole
flow with curl — anonymous redirect, registration setting a real cookie,
authenticated page load, logout clearing both the cookie and the
server-side session row, and being locked out again afterward.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 15:56:11 +02:00

16 KiB

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

Plus drei Tabellen für Auth/Mandantentrennung (account, app_user, session), mehr braucht der MVP nicht:

  • account — ein Mandant (Creator, Agentur, Marke oder Kanzlei als eigene Organisation); jede Submission gehört genau einem Account
  • app_user — ein Login innerhalb eines Accounts (E-Mail, Passwort- Hash, Rolle)
  • session — eine angemeldete Sitzung (Token, Ablaufzeit); bewusst eine echte Tabelle statt zustandsloser signierter Tokens, damit Logout eine Sitzung wirklich beendet
  • 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, Titel, Korrektur, Fundstellen (zum Zeitpunkt des Findings fixiert, kein Verweis auf die aktuelle Regel-YAML — die kann sich inzwischen geändert haben)
  • 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.

Auth/Mandantentrennung: POST /register legt einen neuen Account plus den ersten Nutzer darin an, POST /login meldet einen bestehenden Nutzer an — beides setzt ein deklarix_session-Cookie (HttpOnly, SameSite=Strict, Secure sobald über TLS erreicht). GET / verlangt eine gültige Sitzung (sonst Redirect zu /login); POST /pruefen, POST /veroeffentlichen und GET /dossier/{id} verlangen sie ebenfalls (sonst 401). Ein Beitrag eines fremden Accounts wird wie ein nicht existierender behandelt (404), nie mit einer expliziten 403 bestätigt — sonst würde die Antwort selbst verraten, dass die ID existiert.


Go Commands

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

# 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:

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

// 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

# Immer setzen — ist nicht im Standard-PATH des Servers
export PATH=$PATH:/usr/local/go/bin

Git Push

git push origin main
# Remote: https://git.netcell-it.de/projekte/deklarix.git

Server-Prozess

# 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, DOSSIER_DIR, TSA_URL)

# 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