Handschlag: --managed hebelt die Lizenzpruefung nicht mehr aus (#535)

Mit E-M3 wanderte die Lizenzpruefung in den Manager: Eine gefuehrte Instanz prueft
nicht mehr selbst, weil der Manager es zentral getan hat. Woran sie "gefuehrt"
erkannte, war der Schalter --managed - und ein Schalter ist keine Erkennung.
ClawdDotNet --managed startete damit eine Instanz ganz ohne Lizenzpruefung. Vor
E-M3 gab es die Luecke nicht; sie ist mit der Zentralisierung entstanden.

ManagerHandshake unterschreibt Instanzverzeichnis und Ablauf mit HMAC-SHA256. An
die Signierstelle kommt der Manager nur, wenn seine Lizenzpruefung bestanden hat -
StartInstanceAsync weist vorher mit StartRefusal.NoLicense ab. Dass die
Unterschrift existiert, ist der Beweis.

- Gebunden an das Verzeichnis, und geprueft wird gegen das tatsaechliche, nicht
  gegen das behauptete: Sonst unterschriebe man das Argument statt der Sache.
- Fuenf Minuten gueltig, der Ablauf geht in die Unterschrift ein.
- Ueber die Umgebung statt ueber die Befehlszeile: Eine Befehlszeile steht in
  jeder Prozessliste.
- Ungueltig heisst "nicht gefuehrt", nicht Abbruch. Dann prueft die Instanz ihre
  Lizenz selbst - der Weg von vor E-M3, also kein neuer Fehlerfall.

AppHost.IsManaged ist jetzt der gepruefte Befund. CanSwitchInstance und der
Wechseldialog haengen daran statt am Schalter.

Die Grenze steht ausdruecklich im Konzept (9.9): Der Schluessel liegt in jeder
ausgelieferten Programmdatei. Es geht um den gewoehnlichen Weg an der Lizenz
vorbei, nicht um jemanden mit Werkzeugen und Willen.

Abgenommen an echten Prozessen, Unterschrift fuer den Positivfall unabhaengig in
Python gerechnet: ohne Unterschrift "Lizenz erforderlich"; mit gueltiger kommt die
Instanz hoch; Unterschrift fuer ein fremdes Verzeichnis und hochgedrehter Ablauf
scheitern beide.

820 Tests gruen, 0 Warnungen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-10 15:11:49 +02:00
co-authored by Claude Opus 5
parent dcffcdcff5
commit e3403087c2
6 changed files with 346 additions and 10 deletions
+47 -4
View File
@@ -281,10 +281,9 @@ Dateisystem um. Reihenfolge, wenn es kommt: anhalten, zurückspielen, starten. U
nie ohne Rückfrage. Der Manager kann das inzwischen: Er kennt den Zustand jeder
Instanz über ihren Socket und hält sie geordnet an.
**Wie stellt eine Instanz fest, dass ein Manager da ist?** Vorschlag: Der Manager
startet sie und gibt ihr den Socketpfad mit; die Instanz verbindet sich einmal
zurück und beendet sich, wenn das nicht gelingt. Kein Selbstzweck-Handschlag,
sondern die Prüfung aus E-M3.
**Wie stellt eine Instanz fest, dass ein Manager da ist? — Entschieden**
(2026-09-10, #535): über eine Unterschrift, die der Manager beim Start mitgibt.
Siehe Abschnitt 9.9.
**Startet der Manager eine abgestürzte Instanz wieder? — Entschieden** (2026-09-08):
ja, aber nur, wenn die Instanz es verlangt. Der Schalter heißt „Autostart durch
@@ -914,3 +913,47 @@ Die Instanz gibt sich zwei Minuten zum Auslaufen, der Manager wartet bis zu fün
kürzer, schösse er mitten in das Aufräumen hinein, um das er gerade gebeten hat. Und
trotzdem endlich: Eine Instanz, die dauerhaft „ich räume noch auf" sagt, hängt genauso wie
eine, die schweigt.
### 9.9 Der Handschlag: woran eine Instanz einen Manager erkennt
Behoben am 2026-09-10 (#535).
**Das Loch.** Mit E-M3 wanderte die Lizenzprüfung in den Manager: Eine geführte Instanz
prüft nicht mehr selbst, weil der Manager es zentral getan hat. Woran sie „geführt"
erkannte, war der Schalter `--managed` — und ein Schalter ist keine Erkennung.
`ClawdDotNet --managed` startete eine Instanz ganz ohne Lizenzprüfung. Vor E-M3 gab es die
Lücke nicht, weil jede Instanz selbst geprüft hat; sie ist mit der Zentralisierung
entstanden.
**Die Lösung.** `ManagerHandshake` in `Core/Instances`. Der Manager unterschreibt
Instanzverzeichnis und Ablaufzeitpunkt mit HMAC-SHA256; die Instanz rechnet dieselbe
Unterschrift nach. An diese Stelle kommt der Manager nur, wenn seine Lizenzprüfung
bestanden hat — `ManagerCore.StartInstanceAsync` weist vorher mit
`StartRefusal.NoLicense` ab. **Dass die Unterschrift existiert, ist der Beweis.**
**Vier Eigenschaften, und jede hat einen Grund:**
| | Warum |
|---|---|
| **Gebunden an das Verzeichnis** — und geprüft wird gegen das, in dem die Instanz *tatsächlich* läuft | Sonst unterschriebe man das Argument statt der Sache: Eine Unterschrift für Alpha ließe Beta starten, indem man beim Prüfen Alpha vorzeigt |
| **Fünf Minuten gültig**, Ablauf geht in die Unterschrift ein | Sonst höbe jemand eine Unterschrift auf und startete damit nächste Woche eine Instanz ohne Lizenz. Am Ablauf zu drehen bricht die Unterschrift |
| **Über die Umgebung, nicht die Befehlszeile** | Eine Befehlszeile steht in jeder Prozessliste — die Unterschrift wäre abzulesen und abzuschreiben |
| **Ungültig heißt nicht geführt**, nicht Abbruch | Dann prüft die Instanz ihre Lizenz eben selbst — der Weg von vor E-M3. Kein neuer Fehlerfall, sondern der alte. Wer eine Lizenz hat, merkt nichts |
**Was das aushält und was nicht — ausdrücklich.** Der Schlüssel steht in der Quelldatei und
liegt damit in jeder ausgelieferten Programmdatei; wer ihn sucht, findet ihn. Das ist die
bewusste Abwägung und keine Nachlässigkeit: Es geht nicht darum, jemanden abzuwehren, der
Werkzeuge und Willen mitbringt, sondern darum, dass der **gewöhnliche** Weg um die Lizenz
herum verschlossen ist. Ein Schalter, den man in der Hilfe liest, ist etwas anderes als ein
HMAC, den man nachbauen muss.
**Abgenommen an echten Prozessen**, nicht nur im Test — und die Unterschrift für den
Positivfall wurde unabhängig in Python gerechnet, damit nicht der C#-Code sich selbst
bestätigt:
| Lauf | Ergebnis |
|---|---|
| `--managed`, keine Unterschrift | „Lizenz erforderlich" — kein Start |
| `--managed` + gültige Unterschrift | Instanz kommt hoch, meldet ihren Named Pipe |
| Unterschrift für ein anderes Verzeichnis | „Lizenz erforderlich" |
| Gültige Unterschrift, Ablauf hochgedreht | „Lizenz erforderlich" |