4.5 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Instruktionen
Lies die .ai/*.md Files. Diese liefern den Context für die App, die hier entstehen soll.
Maßgeblich ist aktuell .ai/sultan-system-architecture.md (Netzwerk-, Sicherheits-, Daten- und Anwendungsmodell inkl. SQL-Schema, Event-Format und Referenz-Compose-Files). Bei Widersprüchen zwischen diesem File und der Spec gilt die Spec.
Projektstatus
Frühe Phase: Go-Modul sultan (Go 1.25), bisher nur pkg/traefik – übersetzt flache traefik.*-Labels (FromLabels) in Traefiks dynamische Konfiguration, führt Knoten-Configs zusammen (Merge) und rendert JSON/YAML (Marshal) für /v1/traefik-config. Noch kein main/Subcommand.
go vet ./...
go test -race ./... # alle Tests
go test -run TestFromLabels_Errors ./pkg/traefik # einzelner Test
go test -cover ./...
Middleware-Optionen werden bewusst als unveränderte Label-Strings in verschachtelten Maps durchgereicht (Traefik parst sie beim Einlesen wie Labels); nur Router- und Service-Felder sind typisiert. Nicht unterstützte Labels (TCP/UDP, unbekannte Attribute) liefern ErrUnsupportedLabel statt still ignoriert zu werden.
Big Picture
Sultan ersetzt manuell gepflegtes Ingress-Routing (z. B. Nginx Proxy Manager): Backends deklarieren ihr Routing selbst (Traefik-Labels an Docker-Containern oder Drop-ins in /etc/sultan.d/*.conf für Bare-Metal/LAMP), ein Agent meldet das an einen zentralen Server, und Traefik auf dem Ingress-Knoten pollt daraus seine Konfiguration.
Single Binary in Go mit drei Subcommands:
sultan server– Registry, Stream-Hub (hält Agent-Verbindungen im RAM, Dead-Node-Timeout 90s), Policy-Engine, Traefik-HTTP-Providersultan agent– Watcher (Docker Events API auftraefik.*-Labels bzw. inotify auf/etc/sultan.d), State-Serializer (injiziert LAN- oder NetBird-IP100.64.x.yals Backend-Ziel), Transport mit Reconnect + exponentiellem Backoffsultan token– CLI für Schlüssel, Enrollment-Tokens, Lizenzausstellung
Ports / Endpunkte des Servers:
:8443(TLS) – Control Plane, ausschließlich ausgehend vom Agent (NAT/CGNAT/DS-Lite-tauglich). Persistenter Stream (HTTP/2 oder SSE), 45s-Keepalive, Events werden sofort gepusht. Enrollment:POST /api/v1/enroll.127.0.0.1:8080–GET /v1/traefik-config(Traefik pollt alle 2–5s; flache Labels aller aktiven Nodes → verschachteltes Traefik-JSON) undGET /metrics/checkmk(Checkmk Local-Check-Format).
Sicherheitsmodell (Kerninvarianten):
- Server-TLS-Zertifikat self-signed (ECDSA P-256), Agent pinnt den SHA256-Fingerprint per TOFU in
known_servers. - Agent-Keys (Ed25519) werden nur lokal erzeugt und verlassen den Host nie; Enrollment tauscht Public Key + einmaligen Token gegen Mandanten-Zuordnung und signierten Capability-Lease. Danach ist jeder Heartbeat mit
node.keysigniert. - Jeder Agent-Payload wird serverseitig gegen die Mandanten-Policy geprüft: Hosts in
Host(...)-Regeln müssen inallowed_domainsstehen (sonst ablehnen + Audit-Log), Routen übermax_routeswerden verworfen. - Lizenzen (
license.json) sind mit dem Ed25519-Server-Master-Key signiert.
Stream-Events (JSON: event_id, timestamp, type, node_id, payload): service_state (Agent→Server, vollständiger Label-Snapshot, nicht Delta), node_command (re_enroll/drain/maintenance), lease_renew, error.
Persistenz: SQLite unter <data-dir>/sultan.db (Default /var/lib/sultan), Tabellen tenants, nodes, enrollment_tokens (Tokens nur als Hash gespeichert).
Policy/Lizenzablauf: Mandanten-Status steuert das generierte Routing – Zahlungsverzug → Router auf Bezahlseite (402), Backend down/service_down → Statusseite 503 mit Retry-After: 60. Checkmk-Schwellen: WARN bei 30, CRIT bei 14 Tagen Restlaufzeit, bei Ablauf Umleitung auf Landingpage.
Bekannte Unstimmigkeiten in der Spec
Vor der Implementierung mit dem User klären statt raten:
- Token-Präfix:
sltn_token_...(Abschnitt 3.2) vs.sltn_enroll_...(Compose-Beispiel 7.2). - Tenant-Status: Schema kennt
active/suspended_payment/quarantined, Abschnitt 6.2 setztexpired. - Stream-Transport ist offen: HTTP/2 oder SSE (SSE wäre nur Server→Agent; bidirektionale Events brauchen dann einen Rückkanal).
- Einige URLs in JSON/YAML-Blöcken der Spec sind durch Copy-Paste als Markdown-Links verstümmelt (
"[http://...](http://...)") – die nackte URL ist gemeint.