# 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](.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 Greenfield: Es existiert noch kein Go-Code, kein `go.mod`, keine Build-/Test-Toolchain. Build-, Lint- und Test-Befehle hier ergänzen, sobald sie existieren. ## 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-Provider - `sultan agent` – Watcher (Docker Events API auf `traefik.*`-Labels bzw. inotify auf `/etc/sultan.d`), State-Serializer (injiziert LAN- oder NetBird-IP `100.64.x.y` als Backend-Ziel), Transport mit Reconnect + exponentiellem Backoff - `sultan 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) und `GET /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.key` signiert. - Jeder Agent-Payload wird serverseitig gegen die Mandanten-Policy geprüft: Hosts in `Host(...)`-Regeln müssen in `allowed_domains` stehen (sonst ablehnen + Audit-Log), Routen über `max_routes` werden 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 `/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 setzt `expired`. - 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.