Eine Roadmap statt neun verteilter Listen
Der Stand lag ueber eine Bestandsaufnahme, drei Konzeptpapiere, vier Umsetzungsplaene und zwei Deploymentcenter-Dokumente verteilt - jedes mit eigener Reihenfolge, teils widersprueglich. Alle offenen Punkte daraus sind in docs/Roadmap.md zusammengefuehrt. Aufbau der neuen Roadmap - Statusvokabular: erledigt / beschlossen-offen / Entscheidung noetig / zurueckgestellt / Idee. Damit steht das Geparkte sichtbar drin, statt in einem Konzeptpapier zu verschwinden. - Herkunft-Spalte traegt das alte Kuerzel (S4, K2, B9, T4, F-A1, C1, DC1), damit die archivierten Papiere auffindbar bleiben, ohne sie zu lesen. - Abschnitt 1 Reihenfolge, 2 offene Entscheidungen, 3 die Vorhaben nach Bereich, 4 Ideenspeicher, 5 Chronik des Erledigten, 6 Modell-Einstufung, 7 Herkunftskarte. Was dabei sichtbar wurde - Neun Entscheidungen blockieren Arbeit, ohne dass sie Aufwand kosten - allen voran Matrix oder Rocket.Chat. Sie stehen jetzt gesammelt in Abschnitt 2 statt verstreut in den Diskussionsteilen der Konzepte. - B8 (keine Tiefenbegrenzung bei AgentComm) ist unveraendert offen. Das ist keine Theorie: A haelt sein Gate, waehrend es auf B wartet - ruft B nun A, warten beide bis zum Timeout. Der Testfall A13 dafuer fehlt bis heute. - Die vier Umsetzungsplaene vom 2026-08-05 sind alle unumgesetzt und waren in keiner Roadmap verzeichnet. Archiv Sechs Dokumente ziehen nach docs/archiv/: Bestandsaufnahme, Konzepte Backup/Finanz/Analyse, Linux-Portierung-Analyse, Lizenz-HardwareId-v2 (gegenstandslos - LicenseLabrador ist ersetzt), Deploymentcenter-Review und der 2.4-Integrationsplan. Sie bleiben als Begruendung lesbar, werden aber nicht mehr fortgeschrieben; das README ordnet jedes einzeln ein und warnt, dass ihre Quelltext-Verweise ins Leere gehen koennen. Bauplan bleibt Bauplan Taskboard, Audit, Staging, Memory, Agentenkommunikation, RocketChat und Deploymentcenter-Integration bleiben in docs/ - sie sind die Detailvorgabe fuer die Umsetzung, nicht Vorhabenlisten. Jedes bekommt oben eine Zeile, die seine Rolle und den Umsetzungsstand nennt und auf die Roadmap zeigt. Dasselbe fuer die vier Umsetzungsplaene: die Reihenfolge gilt in der Roadmap, nicht im Plan. Nebenbei repariert: Taskboard-Konzept und Teststrategie verwiesen auf AgentScheduler und ToolJobScheduler, die seit der Scanner-Konsolidierung geloescht sind. Alle Dokument-Verweise in docs/ sind geprueft. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,299 @@
|
||||
# Deploymentcenter 2.2 – 2.4: Was noch zu tun ist
|
||||
|
||||
Stand: 2026-08-13. Ergänzt [Deploymentcenter-Integration](../Deploymentcenter-Integration.md)
|
||||
(dort steht der Stand nach 2.1) um die drei neuen Ausbaustufen.
|
||||
|
||||
| Fassung | Was dazukam | Betrifft uns |
|
||||
|---|---|---|
|
||||
| **2.2** | Plattform-Dimension, signierte Releases, Anwenden mit Rollback, `preservePatterns` | Release-Strecke, Update-Anwendung |
|
||||
| **2.3** | Erstinstallation über `update-agent --action install`, `setup.json`, Installationskonto | Neu, siehe §4 |
|
||||
| **2.4** | Release-Ablage hinter HTTP-Basic-Auth, Zugang über den Lizenzschlüssel | **Erledigt**, siehe §1 |
|
||||
|
||||
---
|
||||
|
||||
## 1. Zugangsschutz (2.4) — erledigt
|
||||
|
||||
`CheckForUpdateAsync` übergibt jetzt `ReleaseCredentials.FromLicenseKey(...)`, und
|
||||
`UpdateCheckResult.Unauthorized` wird getrennt von einem Netzfehler behandelt.
|
||||
|
||||
**Warum das nicht warten konnte:** UPGRADE §16.1 empfiehlt „erst ausliefern, dann
|
||||
scharfschalten". Für ein Produkt, das noch nie veröffentlicht hat, geht diese Reihenfolge
|
||||
nicht auf. `ReleaseGuard::regenerateForProject` überspringt Verzeichnisse, die es nicht
|
||||
gibt — `/releases/clawddotnet/` liefert derzeit 404, es ist also nichts geschützt. Sobald
|
||||
wir das **erste** Release hochladen, entsteht das Verzeichnis, und der nächste
|
||||
`tick.php`-Lauf legt den Schutz an. Der erste ausgelieferte Build muss die Zugangsdaten
|
||||
also bereits mitbringen, sonst schließt sich die Tür hinter dem ersten Release.
|
||||
|
||||
---
|
||||
|
||||
## 2. Plattform (2.2) — Release-Strecke steht
|
||||
|
||||
Erstes Paket veröffentlicht: **0.1.0, Kanal `dev`, Plattform `win-x64`**, 116 Dateien,
|
||||
25 MB, Rückgabewert 0. Die Update-Prüfung antwortet korrekt (`0.0.9` → Update, `0.1.0` →
|
||||
keins).
|
||||
|
||||
```bash
|
||||
pack-and-deploy --config deploy/packager.config.json \
|
||||
--project clawddotnet --version 0.1.0 \
|
||||
--channel dev --platform win-x64 \
|
||||
--publish-dir <dotnet-publish-Ausgabe>
|
||||
```
|
||||
|
||||
- Zugangsdaten in `deploy/packager.config.json` (per `.gitignore` ausgeschlossen),
|
||||
Vorlage ohne Werte in [`packager.config.example.json`](../../deploy/packager.config.example.json).
|
||||
- **`ftpRemoteBaseDir` ist `/releases`**, nicht `/public_html/releases` wie in der
|
||||
Packager-Vorlage: Auf diesem Server liegt die Release-Ablage auf der FTP-Wurzel.
|
||||
- Der Packager veröffentlicht mit einem **Sub-Token**, das nur `updateservice:publish`
|
||||
trägt — gezogen über `/api/tokens/v1/provision`. Das Master-Token gehört nicht in eine
|
||||
Konfigurationsdatei.
|
||||
- **`deploy.py` ist dafür das falsche Werkzeug.** Es spiegelt den
|
||||
Deploymentcenter-Projektbaum in die FTP-Wurzel und hat mit dem Veröffentlichen eines
|
||||
Anwendungspakets nichts zu tun.
|
||||
- Die Versionsgegenprobe des Packagers greift und passt: `<Version>` aus
|
||||
[Directory.Build.props](../../Directory.Build.props) stimmt mit `clawddotnet.dll` überein.
|
||||
|
||||
Offen: `linux-x64` (erst nach der Avalonia-Portierung) und `prod`.
|
||||
|
||||
Clientseitig ist nichts zu tun: Das SDK schickt die Kennung des laufenden Systems von
|
||||
selbst.
|
||||
|
||||
### `preservePatterns` betrifft uns kaum
|
||||
|
||||
Unsere Konfiguration liegt seit der Linux-Portierung in `AppPaths.ConfigDirectory`
|
||||
(`%APPDATA%` bzw. XDG), **nicht** neben der Programmdatei. Ein Update kann sie also gar
|
||||
nicht überschreiben. Zu prüfen bleibt nur, dass keine leeren Arbeitsordner ins Paket
|
||||
wandern — die `CreateWorkingDirectories`-Targets in
|
||||
[ClawdDotNet.csproj](../../ClawdDotNet.csproj) legen `tools/`, `Logs/` und `Instances/`
|
||||
unter `OutputPath` an, und die sind mit AppPaths ohnehin überholt.
|
||||
|
||||
---
|
||||
|
||||
## 3. Update anwenden — erledigt
|
||||
|
||||
Aus dem Hinweis ist eine Rückfrage geworden („Jetzt installieren" / „Später"), die den
|
||||
`update-agent` startet. Umgesetzt in
|
||||
[`DeploymentcenterService.StartUpdate`](../../src/ClawdDotNet.App/Services/DeploymentcenterService.cs)
|
||||
und `App.StartUpdateAsync`.
|
||||
|
||||
### Der Agent wird mitgeliefert — er muss es
|
||||
|
||||
Die Erstinstallation legt den Agenten **nicht** ins Zielverzeichnis: Sie läuft von dort,
|
||||
wo der Benutzer sie hingelegt hat. `ResolveAgentPath()` sucht ihn aber neben der
|
||||
Anwendung. Ohne Mitliefern fände die Anwendung nie einen Agenten und könnte sich nicht
|
||||
aktualisieren.
|
||||
|
||||
[`deploy/publish.py`](../../deploy/publish.py) holt das ausgelieferte Binary von
|
||||
`/installer/`, **prüft die SHA256 gegen `installer.json`** und legt es plattformrichtig
|
||||
ab (`update-agent.exe` bzw. `update-agent`). Bewusst das offizielle statt eines selbst
|
||||
gebauten: Es ist dasselbe, das die Erstinstallation verwendet, und wird zentral gepflegt.
|
||||
Ein ungeprüfter Download wäre ausgerechnet auf dem Pfad, der später fremden Code
|
||||
ausführt, die falsche Sparsamkeit.
|
||||
|
||||
Kosten: rund 28 MB im gepackten Paket (25 → 53 MB).
|
||||
|
||||
### Die Reihenfolge ist der eigentliche Inhalt
|
||||
|
||||
```
|
||||
1. AnnounceUpdate(version) → Watchdog meldet beim Beenden "maintenance"
|
||||
2. AppHost.DisposeAsync() → Datenbank, Scanner, Telegram, Abmeldung
|
||||
3. StartUpdate(...) → Agent starten, exitCurrentApp: false
|
||||
4. desktop.Shutdown() → wir beenden uns selbst
|
||||
```
|
||||
|
||||
Die Verlockung wäre, `LaunchUpdateAgent` das Beenden zu überlassen. Das tut es aber über
|
||||
`Environment.Exit` und übergeht damit Schritt 2 vollständig: keine Abmeldung, keine
|
||||
geschlossene Instanzdatenbank. Deshalb `exitCurrentApp: false` und
|
||||
`waitForCurrentProcess: true` — der Agent bekommt unsere Prozesskennung und wartet, bis
|
||||
wir wirklich weg sind, statt über gesperrte Dateien zu kopieren.
|
||||
|
||||
`maintenance` statt `stopped` ist kein Schönheitsfehler: `stopped` heißt „bewusst
|
||||
beendet" und lässt den Monitor liegen, bis jemand ihn anfasst. Beim Update kommt die
|
||||
Instanz aber wieder.
|
||||
|
||||
**Doppeltes Aufräumen** war die Falle dabei: Nach Schritt 2 ruft `desktop.Shutdown()` die
|
||||
Behandlung, die erneut aufräumt — und dabei den gerade gesetzten Wartungszustand mit
|
||||
einer zweiten Abmeldung überschrieben hätte. `AppHost.DisposeAsync` sperrt sich jetzt
|
||||
selbst gegen den zweiten Durchlauf.
|
||||
|
||||
---
|
||||
|
||||
## 4. Erstinstallation (2.3) — `setup.json` steht
|
||||
|
||||
Der Konfigurationsort war der Blocker: `setup.json`-Ziele waren „relativ zum
|
||||
Installationsverzeichnis", unsere Konfiguration liegt aber in `%APPDATA%` bzw.
|
||||
`$XDG_CONFIG_HOME` — weil `/opt/clawddotnet` unter Linux für den Dienstbenutzer nicht
|
||||
schreibbar ist ([Linux-Analyse](Linux-Portierung-Analyse.md)).
|
||||
|
||||
Das Deploymentcenter hat daraufhin `location` am Ziel ergänzt (`install`, `config`,
|
||||
`data`, `home`) samt Variablenersetzung in `file`. Damit ist der Weg frei;
|
||||
[`setup.json`](../../src/ClawdDotNet.Desktop/setup.json) liegt im Projekt und wird ins
|
||||
Ausgabeverzeichnis kopiert, landet also im Paket neben der `manifest.json`.
|
||||
|
||||
### Der Ordnername ist bewusst kleingeschrieben
|
||||
|
||||
`AppPaths` legt das Verzeichnis plattformabhängig unterschiedlich an:
|
||||
|
||||
| Plattform | Pfad |
|
||||
|---|---|
|
||||
| Windows | `%APPDATA%\ClawdDotNet` |
|
||||
| Linux | `$XDG_CONFIG_HOME/clawddotnet` (klein, Konvention) |
|
||||
|
||||
Eine `setup.json` kennt nur **eine** Schreibweise. `clawddotnet/Settings.json` trifft
|
||||
unter Linux exakt und unter Windows ebenfalls, weil NTFS Groß- und Kleinschreibung nicht
|
||||
unterscheidet. Andersherum ginge es nicht: `ClawdDotNet` wäre unter Linux ein zweites,
|
||||
leeres Verzeichnis neben dem, aus dem die Anwendung liest.
|
||||
|
||||
### Was dabei abfällt
|
||||
|
||||
Das Token stellt der Server aus (`source: "provision"`), die Server-Adresse kommt aus dem
|
||||
Installer (`detect:baseurl`). Damit entfällt der Absatz „bis die Avalonia-Einstellungs-
|
||||
ansicht steht, von Hand in `Settings.json`" aus der
|
||||
[Integrationsbeschreibung](../Deploymentcenter-Integration.md) — jedenfalls für frisch
|
||||
installierte Systeme.
|
||||
|
||||
Der Installer schreibt Lizenzschlüssel und Token **im Klartext**; er kennt unsere
|
||||
DPAPI-Hülle nicht. Das ist in Ordnung und abgesichert: `SecretProtector.Unprotect` gibt
|
||||
Klartext unverändert zurück, beim ersten Speichern wird verschlüsselt. Der Test dazu
|
||||
steht in `SecretProtectorTests` und nennt jetzt beide Gründe, damit ihn niemand als
|
||||
Altlast entfernt.
|
||||
|
||||
### Zwei Grenzen bleiben
|
||||
|
||||
- **`CLAWD_CONFIG_DIR` kennt der Installer nicht.** Wer den Ort per Umgebungsvariable
|
||||
verlegt, muss die Datei selbst verschieben.
|
||||
- **Wer installiert, entscheidet mit** (SETUP warnt selbst davor): `config` bezieht sich
|
||||
auf das Konto, unter dem der Installer läuft. Für einen systemd-Dienst mit eigenem
|
||||
Benutzer heißt das: als dieser Benutzer installieren, sonst landet die Konfiguration
|
||||
im falschen Profil.
|
||||
|
||||
### Durchgespielt (2026-08-15)
|
||||
|
||||
Anmeldung mit dem Installationskonto und der gesamte Ablauf gegen den echten Server:
|
||||
|
||||
| Schritt | Ergebnis |
|
||||
|---|---|
|
||||
| `POST /api/setup/v1/login` | 201, Rolle `installer`, Recht `setup:install`, Token 30 min gültig |
|
||||
| `GET /api/setup/v1/catalog?platform=win-x64` | `clawddotnet` (dev=0.1.2) erscheint. **Ohne `platform` leer** — wie die Update-Prüfung, der Agent schickt `PlatformId.Current` |
|
||||
| `POST /api/setup/v1/token` | Anwendungstoken mit genau `watchdog:ping` + `bugtracker:report` |
|
||||
| Rechteschranke | Das ausgestellte Token kann **kein** `updateservice:publish` nachziehen (403 `provision_denied`) |
|
||||
| `SetupPaths.Resolve` gegen unsere `setup.json` | löst unter Windows auf `%APPDATA%\ClawdDotNet\Settings.json` auf (`fileWindows` greift) |
|
||||
| **Round-Trip** SDK schreibt → `SettingsManager` liest | trägt: camelCase-Keys treffen, der Klartext-Lizenzschlüssel geht durch den Entschlüsselungspfad (der Klartext unverändert durchreicht) |
|
||||
|
||||
Damit ist der Weg vollständig: Ein frisch aufgesetztes System bekommt über den Installer
|
||||
Server-Adresse, Lizenzschlüssel und ein vom Server ausgestelltes Instanz-Token in die
|
||||
`Settings.json` geschrieben, die ClawdDotNet dann ohne Zutun lädt.
|
||||
|
||||
### Befund am Rande: alte Felder bleiben stehen
|
||||
|
||||
Der `SetupWriter` merged in eine vorhandene `Settings.json`, statt sie zu ersetzen —
|
||||
richtig so, sonst gingen Logging-Einstellungen und Ähnliches verloren. Auf einem System
|
||||
mit einer **alten** Datei bleiben dabei Felder stehen, die es in der aktuellen
|
||||
`AppSettings` nicht mehr gibt (`watchdogServerUrl`, `licensePublicKeyBase64` aus der
|
||||
LicenseLabrador-Zeit). Harmlos — `SettingsManager` ignoriert unbekannte Felder beim
|
||||
Laden —, aber tote Einträge in der Datei. Kein Handlungsbedarf; beim ersten `Save` der
|
||||
laufenden App verschwinden sie.
|
||||
|
||||
**Nicht enthalten** (SETUP §7): systemd-Unit und Windows-Dienst legt der Installer nicht
|
||||
an. Für den kopflosen Betrieb bleibt das unsere Aufgabe.
|
||||
|
||||
---
|
||||
|
||||
## 4a. Der Update-Weg ist durchgespielt
|
||||
|
||||
Am 2026-08-14 gegen den echten Server geprüft, nicht nur gebaut. Ausgangslage: das
|
||||
0.1.0-Paket mit Lizenzschlüssel geladen und entpackt — also eine Installation, wie sie
|
||||
beim Kunden aussieht — plus eine selbst angelegte Datei, die in keinem Manifest steht.
|
||||
|
||||
| Fall | Ergebnis |
|
||||
|---|---|
|
||||
| **0.1.0 → 0.1.1** | RC 0. SHA256 des Pakets und 117 Manifest-Hashes geprüft. Fremde Datei unangetastet, `setup.json` da, `update-agent.exe` neu im Ziel |
|
||||
| **Rücksprung 0.1.1 → 0.1.0** | RC 0. `update-agent.exe` als nicht mehr zum Release gehörig **entfernt** — und nur die, die fremde Datei blieb liegen |
|
||||
| **Ohne Lizenzschlüssel** | `UNAUTHORIZED: … Erwartet wird der Lizenzschluessel dieser Installation`, RC 2. Sauber von einem Netzfehler unterschieden |
|
||||
| **Abbruch mitten im Schreiben** (Datei exklusiv gesperrt) | RC 1, „Vorheriger Stand wurde wiederhergestellt". Version, Dateizahl und Inhalt unverändert — die Installation blieb lauffähig |
|
||||
|
||||
Damit trägt die Zusage aus §4B des UpdateService-Handbuchs: Ein Abbruch hinterlässt keine
|
||||
halbe Installation, und verwaiste Dateien werden aufgeräumt, ohne fremde anzufassen.
|
||||
|
||||
Zwei Kleinigkeiten am Rand:
|
||||
|
||||
- Nach dem gescheiterten Lauf blieb ein **leeres** `.dc-update-backup/` zurück. Kein
|
||||
Speicherverlust — der Rollback hatte alles zurückgeholt —, und der nächste erfolgreiche
|
||||
Lauf hat es entfernt. Ein leeres Verzeichnis dieses Namens sieht für einen Betreiber
|
||||
aber nach „Update hängt" aus.
|
||||
- Der Agent weist bei **jedem** Lauf auf das unsignierte Release hin. Das ist richtig so
|
||||
und wird erst still, wenn §5 erledigt ist.
|
||||
|
||||
---
|
||||
|
||||
## 5. Signierte Releases (2.2) — Schlüssel steht, Prüfung getestet
|
||||
|
||||
Der Signierschlüssel ist seit dem 2026-08-14 serverseitig hinterlegt (RSA-SHA256,
|
||||
`canonical-line-v1`). **0.1.2 ist das erste signierte Release**; 0.1.0 und 0.1.1 bleiben
|
||||
unsigniert, weil serverseitig beim Veröffentlichen signiert wird.
|
||||
|
||||
Am Testsystem durchgespielt:
|
||||
|
||||
| Fall | Ergebnis |
|
||||
|---|---|
|
||||
| Signiertes 0.1.2 mit `--require-signature` | RC 0, kein Unsigniert-Hinweis mehr |
|
||||
| Unsigniertes 0.1.1 mit `--require-signature` | **RC 1, Abbruch vor dem Herunterladen** |
|
||||
| Unsigniertes 0.1.1 ohne die Pflicht | RC 0 mit Hinweis — wie dokumentiert |
|
||||
|
||||
Der öffentliche Schlüssel wird beim ersten Lauf geholt und als
|
||||
`dc-release-pubkey.pem` neben dem Agenten festgehalten. Ein später abweichender Schlüssel
|
||||
fällt damit auf.
|
||||
|
||||
### Offen: die Pflicht ist aus der Anwendung heraus nicht erreichbar
|
||||
|
||||
`--require-signature` gibt es **nur als Kommandozeilenschalter**.
|
||||
`UpdateClient.LaunchUpdateAgent` — der vom Handbuch empfohlene Weg, den auch wir
|
||||
benutzen — hat dafür keinen Parameter, und der Agent liest keine Umgebungsvariable dafür
|
||||
(`Program.cs:66` liest ausschließlich `HasFlag(args, "--require-signature")`).
|
||||
|
||||
Damit läuft jede Anwendung, die den empfohlenen Weg geht, ohne Signaturprüfung, während
|
||||
derselbe Vorgang von Hand auf der Kommandozeile geschützt wäre. Wir könnten den Start
|
||||
selbst nachbauen — dann verlieren wir aber `--restart`, `--wait-for-pid` und
|
||||
`--wait-timeout`, also genau die Handgriffe, für die es die Hilfsmethode gibt.
|
||||
|
||||
**Gemeldet.** Sobald `LaunchUpdateAgent` einen Parameter dafür hat, setzen wir ihn:
|
||||
Alle unsere Releases ab 0.1.2 sind signiert, ein Rückschritt auf unsignierte Stände wäre
|
||||
danach kein Verlust.
|
||||
|
||||
---
|
||||
|
||||
## 6. Reihenfolge
|
||||
|
||||
1. **Zugangsschutz** (§1) — erledigt, muss im ersten Release drin sein.
|
||||
2. **`setup.json`** (§4) — erledigt, wird mit dem ersten Paket ausgeliefert.
|
||||
3. **Release-Strecke** (§2): `packager.config.json`, erster Testlauf nach `dev`.
|
||||
4. **Update anwenden** (§3): Agent mitliefern, `maintenance` beim Update melden.
|
||||
5. **Signatur scharf** (§5), sobald der Serverschlüssel steht.
|
||||
|
||||
Schritt 3 ist die Voraussetzung für alles Weitere: Solange kein Release veröffentlicht
|
||||
ist, lässt sich weder Update noch Erstinstallation erproben — und die `setup.json` wirkt
|
||||
erst, wenn sie in einem Paket steckt.
|
||||
|
||||
---
|
||||
|
||||
## 7. Befunde vom 2026-08-13 — alle behoben
|
||||
|
||||
Zur Nachvollziehbarkeit, weil einige unsere Umsetzung geformt haben:
|
||||
|
||||
| Befund | Behoben durch |
|
||||
|---|---|
|
||||
| `.htpasswd` enthielt alle Lizenzschlüssel im Klartext (Benutzernamenspalte wird nicht gehasht) | `ReleaseGuard::licenseUsername()` leitet `lic_<sha256[0..16]>` ab; `ReleaseCredentials.UsernameForLicenseKey` bildet dieselbe Ableitung nach. Die Datei enthält jetzt nur noch bcrypt über einen hochentropen Schlüssel |
|
||||
| Doku beschrieb Nginx, der Schutz greift nur unter Apache; WebUI meldete „GESCHÜTZT" allein anhand vorhandener Dateien | Echter HTTP-Selbsttest (`ReleaseGuard::selfTest`, erwartet 401), Warnhinweis und eigener Nginx-Abschnitt in der Doku |
|
||||
| UPDATESERVICE §7 dokumentierte `{"status":"ok"}`, der Code liefert `"success"` | Doku berichtigt |
|
||||
| `setup.json` schrieb nur ins Installationsverzeichnis | `location`-Angabe am Ziel plus Variablenersetzung in `file` |
|
||||
|
||||
### Offen aus dem ersten Release (2026-08-13)
|
||||
|
||||
| Befund | Wirkung |
|
||||
|---|---|
|
||||
| **Veröffentlichen löst `ReleaseGuard` nicht aus.** `regenerateForProject` läuft nur bei Lizenzänderungen, Projektlöschung, Kontoänderungen und im Sechs-Stunden-Turnus von `cli/tick.php`. Ein Produktverzeichnis entsteht aber erst beim ersten Upload | `/releases/clawddotnet/` war nach dem Upload **ohne `.htaccess`** — das frische Paket bis zum nächsten Turnuslauf für jeden ladbar. Der Turnus hat es inzwischen geschlossen (401 bestätigt). Ein Aufruf am Ende von `/api/updateservice/v1/publish` würde das Fenster ganz vermeiden; das Verzeichnis existiert dort bereits |
|
||||
| **Die Prüfvorschrift aus UPGRADE §16.4 meldet falsch grün.** `curl -I …/.htpasswd → 403` trifft auch dann zu, wenn die Datei gar nicht existiert: Apache sperrt `.ht*` global | Wir hatten 403 auf `.htpasswd` **und** 200 auf `package.tar.gz`. Aussagekräftig ist nur der Paket-Abruf ohne Zugangsdaten |
|
||||
| **Kein Signierschlüssel auf dem Server.** `security.release_private_key` ist nicht gesetzt (UPGRADE §15.2) | Releases sind unsigniert, der Agent kann die Herkunft nicht prüfen. `--require-signature` ist damit unbenutzbar |
|
||||
|
||||
Die Ableitung des Benutzernamens muss auf beiden Seiten zeichengenau übereinstimmen —
|
||||
`lic_` plus die ersten 16 Hexzeichen des SHA-256 über den getrimmten Schlüssel. Wer eine
|
||||
Seite ändert, sperrt die gesamte Installationsbasis aus.
|
||||
Reference in New Issue
Block a user