# Auftragsübermittlung an Agenten (Phase 5) Bis zu dieser Phase war der Agent ein **angemeldeter Beobachter**: Er meldete sich an und gab Lebenszeichen, aber der Server konnte ihm nichts zu tun geben. Zentral gesteuerte Sicherungen liefen nur für Quellen, die der Control-Server selbst erreicht. Jetzt läuft die Kette durch: ```text Server stellt Auftrag ein → Agent holt ihn ab → Agent sichert über die Engine → Agent meldet Fortschritt und Ergebnis → Server trägt den Wiederherstellungspunkt ein ``` ## Der Agent holt ab, der Server drückt nicht Das ist keine Bequemlichkeit, sondern die einzige Wahl, die in echten Netzen funktioniert. Ein Agent steht hinter einer Firewall, oft hinter NAT, und ist vom Server aus nicht erreichbar — jedenfalls nicht ohne eine eingehende Portfreigabe auf **jedem** gesicherten System. Die Verbindung geht deshalb immer vom Agenten aus, in genau der Richtung, die auch seine Lebendmeldung nimmt. Drei Endpunkte, alle am **Betriebstoken des Agenten**: ```bash POST /api/v1/agents/tasks/claim # holt den nächsten Auftrag (204 = nichts zu tun) POST /api/v1/agents/tasks/{id}/progress # Fortschritt und Lebenszeichen POST /api/v1/agents/tasks/{id}/result # Ergebnis ``` Jeder prüft, dass der Auftrag dem anfragenden Agenten gehört. Ohne diese Prüfung könnte ein übernommener Agent die Aufträge aller anderen Systeme abholen — samt Quell- und Repositorypfaden, also einer Landkarte der gesamten Anlage. Ein fremder und ein bereits abgeschlossener Auftrag ergeben **dieselbe** Antwort: Die Unterscheidung verriete, welche Auftragskennungen es gibt. ## Der Fund: Der Server sperrte das Repository für seinen eigenen Agenten Beim ersten Durchlauf meldete der Agent: ``` REPOSITORY_UNREACHABLE — Das Repository unter "…/p5/repo" ist von diesem Agenten aus nicht erreichbar ``` Das Repository lag einwandfrei da. Gesperrt hatte es der **Server**: Phase 8 öffnet das Repository einmal je Lauf und hält dabei die Schreibsperre — eine richtige Entscheidung, solange der Server selbst sichert. Bei einer Delegation wartete er nun mit gehaltener Sperre darauf, dass der Agent in dasselbe Repository schreibt. Der Server öffnet das Repository jetzt nur noch, wenn mindestens eine Quelle des Laufs **serverseitig** gesichert wird. Ein Lauf mit ausschließlich Agentenquellen löst den Datensatz auf — der Agent braucht den Pfad —, öffnet das Repository aber nicht. ## Wie der Agent an das Repository kommt Der Auftrag enthält den Repositorypfad; der Agent öffnet ihn unmittelbar. Daraus folgt eine Betriebsanforderung, die klar benannt sein muss: **Der Agent braucht Schreibzugriff auf das Repository.** Auf einem gemeinsamen Server ist das der lokale Pfad. Bei getrennten Maschinen braucht es eine Freigabe (NFS, SMB) oder ein eingehängtes Verzeichnis. Erreicht der Agent das Ziel nicht, meldet er `REPOSITORY_UNREACHABLE` mit dem Hinweis auf die Freigabe — statt einen technischen Pfadfehler weiterzureichen oder eine leere Sicherung abzuliefern. Ein Streaming-Protokoll, bei dem der Agent seine Blöcke an den Server schickt und dieser sie ablegt, wäre die Alternative. Es ist bewusst **nicht** gebaut: Es verdoppelt den Datenweg, macht den Control-Server zum Engpass jeder Sicherung und verlangt ein eigenes Übertragungsprotokoll samt Wiederaufnahme. Für einen ersten funktionierenden Weg ist die Freigabe die ehrlichere Lösung. ## Regeln, die auch hier gelten - **Ein Auftrag je Agent.** Ein Teilindex auf `(agent_id) WHERE status IN ('claimed','running')` erzwingt es. Zwei gleichzeitige Sicherungen desselben Systems konkurrierten um dieselben Dateien und dieselbe Schreibsperre. - **Ein Teilfehler bleibt ein Teilfehler.** Der Agent meldet ihn als solchen, der Server berichtigt ein widersprüchliches Ergebnis, und die Datenbank lehnt „erfolgreich mit übergangenen Objekten" per CHECK ab. Drei Ebenen für dieselbe Regel — weil sie die wichtigste ist (verbindliche Regel 1). - **Ein Fehler ist eine Antwort.** Der Agent meldet auch im Fehlerfall zurück. Ein Agent, der bei einem Fehler schweigt, lässt den Lauf bis zum Ablauf der Frist hängen. - **Verschlüsselung wird nie stillschweigend weggelassen.** Verlangt der Auftrag sie und hat der Agent kein Schlüsselmaterial, wird der Auftrag abgelehnt. Ein Backup, das der Server für verschlüsselt hält und das es nicht ist, wäre eine Zusicherung ins Leere. - **Der Lauf wartet synchron auf den Agenten.** Würde der Server den Auftrag nur einstellen und den Lauf als erfolgreich vermerken, stünde ein grüner Lauf in der Übersicht, während der Agent noch arbeitet — oder bereits gescheitert ist. - **Verwaiste Aufträge werden freigegeben, nicht wiederholt.** Stirbt ein Agent mitten im Lauf, gilt sein Auftrag nach Fristablauf als gescheitert (`AGENT_LOST`, Klasse `transient`). Nicht erneut eingereiht: Er könnte bereits Blöcke geschrieben haben. - **Der Fortschritt ist zugleich die Lebendmeldung** und wird gedrosselt (alle fünf Sekunden). Die Engine ruft ihren Rückruf sehr häufig auf; ungedrosselt erzeugte eine Sicherung mit einer Million Dateien eine Million HTTP-Anfragen. ## Nachweis Auf einer Maschine, auf der Server und Agent laufen: ```text Agent aufgenommen dev-agent Auftrag mit agent_id angelegt Quelle ist Agent zugewiesen ✓ Lauf angestoßen HTTP 202 Agent holt ab und führt aus "auftrag wird ausgefuehrt" Ergebnis succeeded, 3.500.014 Byte, 4 Objekte Wiederherstellungspunkt in der DB complete, 3.500.014 Byte Quelle gelöscht, wiederhergestellt ✓ alle Prüfsummen stimmen ``` ## Was noch fehlt - **Der Windows-Dienst ist weiterhin ungeprüft.** `service_windows.go` ist geschrieben, aber auf macOS nicht ausführbar. Der Kommandozeilenweg und die Auftragsausführung sind plattformunabhängig; der Dienstwrapper ist der letzte ungeprüfte Teil von Phase 5. - **Wiederherstellung über den Agenten.** Der Auftragstyp `restore` ist im Modell vorgesehen und nicht umgesetzt: Eine Wiederherstellung schreibt auf ein fremdes System und braucht dieselben drei Hürden wie die serverseitige (Phase 9). Sie wird als eigener Schritt gebaut, nicht nebenbei. - **Kein Streaming zum Server.** Siehe oben — der Agent braucht Zugriff auf das Repository. - **Keine Abbruchmöglichkeit von außen.** Ein laufender Agentenauftrag lässt sich derzeit nicht über die API abbrechen; er läuft zu Ende oder in die Frist.