Sicherheit
- install_db.php war ohne Authentifizierung erreichbar und setzte bei jedem
Aufruf das Admin-Passwort auf einen fest im Code stehenden Wert zurück.
Jetzt Auth-Pflicht; ein Konto wird nur bei leerer Benutzertabelle angelegt.
- Stored XSS im Bugtracker-Detail-Modal: Titel, Beschreibung, Fehlermeldung,
Stacktrace und Kommentare gingen ungefiltert durch innerHTML.
- report.php, projects.php und das Veröffentlichen von Releases verlangen jetzt
zwingend ein Token. Publish war zuvor völlig ungeschützt.
- CSRF-Token in allen Formularen, Session-Regenerierung nach Login,
Drosselung fehlgeschlagener Anmeldeversuche.
- Zugangsdaten aus der Versionskontrolle entfernt (Serverdaten.txt,
config.php, .htpasswd, deploy_config.json). Historie enthält sie weiterhin,
Rotation erforderlich (siehe docs/UPGRADE.md).
- Token-Validierung nur noch über SHA-256-Hash; expires_at wird ausgewertet.
Behobene 500er
- Audit::log() war in index.php weder eingebunden noch importiert. Jeder
Klick auf "Aktivierung freigeben" endete in einem Fatal Error.
- Derselbe benannte PDO-Platzhalter mehrfach je Statement (:id in
revokeToken/deleteToken, :q siebenfach in der Volltextsuche). Bei
EMULATE_PREPARES=false ist das nicht zulässig und warf HY093.
- Migration 005 nutzte dynamisches SQL, dessen Semikolons in String-Literalen
vom alten explode(';')-Installer als Statement-Ende gelesen wurden. Sie
schlug still fehl, wodurch push_id/target_agent/tags dauerhaft fehlten.
- Monitor-Umbenennung ohne Transaktion, verschachtelte Transaktionen im
RateLimiter.
Funktionale Korrekturen
- Der Watchdog-Evaluator fehlte vollständig: Monitor-Zustände änderten sich nur
beim Eintreffen eines Heartbeats, ein ausgefallenes System blieb dauerhaft
"up". Erster Lauf auf dem Produktivsystem: 7 von 10 Monitoren waren
tatsächlich seit über einem Tag nicht erreichbar.
- Das Feld "os" fehlte im Monitor-Dialog, wurde aber gespeichert und löschte
damit bei jedem Speichern das Betriebssystem.
- Der Resolve-Dialog existierte im HTML nicht; der Button war funktionslos.
- Versionsvergleich erfolgte lexikografisch, wodurch 1.9.0 als neuer galt
als 1.10.0.
- Schreiboperationen meldeten Erfolg auch für nicht existierende IDs.
- Post/Redirect/Get gegen doppelte Einträge beim Neuladen.
Neue Struktur
- src/bootstrap.php mit PSR-4-Autoloader ersetzt die require-Ketten.
- Core: Config, Http, Csrf, ApiAuth, Logger, Migrator, ErrorReporter.
- Migrator mit zeichenweisem SQL-Parser, dc_migrations und Baseline-Verfahren,
damit bestehende Installationen keine Beispieldaten zurückbekommen.
Agenten-Workflow
- Claim/Lease: Items werden exklusiv übernommen, damit nicht zwei Agenten am
selben Problem arbeiten. action=next holt und reserviert in einem Zug.
- Idempotenz über client_ref, Deduplizierung auch für Feature Requests,
Erkennung von Regressionen, automatische Eskalation des Schweregrads.
- Strukturierter Code-Kontext (repo_url, commit_sha, file_path, line_no).
- Delta-Abfragen über updated_since, Pagination, Bulk-Update.
- Beim Veröffentlichen eines Releases schließen sich Items mit passendem
resolved_in_build selbst.
- Ausgehende Webhooks mit HMAC-Signatur, /api/health, /api/openapi.json.
- Unbehandelte Fehler meldet die Plattform in ihren eigenen Bugtracker.
WebUI
- Serverseitige Filterung mit Pagination statt Rendern aller Datensätze.
- Migrations-Schranke, Evaluator-Warnung, Übersicht aktiver Agenten.
Zeitstempel liegen in der Datenbank durchgängig in UTC und werden für die
Anzeige in die App-Zeitzone umgerechnet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
13 KiB
Deployment Center — Agenten-Handbuch
Diese Seite beschreibt, wie ein Coding-Agent den Bugtracker und den UpdateService des Deployment Centers benutzt.
Maschinenlesbare Fassung: GET /api/openapi.json
0. Was sich geändert hat
Wer eine ältere Integration betreibt, muss zwei Dinge anpassen:
| Änderung | Auswirkung |
|---|---|
POST /api/bugtracker/v1/report verlangt jetzt zwingend ein Token |
Aufrufe ohne Token liefern 401 unauthorized |
GET /api/bugtracker/v1/projects verlangt jetzt ein Token |
dito |
POST auf UpdateService-Publish verlangt updateservice:publish |
Aufrufe ohne Token liefern 401 |
| Antwortformat vereinheitlicht | Erfolg: {"status":"success",...}, Fehler: {"status":"error","error":{"code":"…","message":"…"}} |
Der Feldname error_hash bleibt erhalten; zusätzlich gibt es dedup_key.
1. Authentifizierung
Alle Endpunkte akzeptieren das Token in einem dieser Header:
Authorization: Bearer dc_sub_xxxxxxxxxxxx
X-Agent-Token: dc_sub_xxxxxxxxxxxx
Token-Hierarchie
- Master-Token (
dc_master_…) — wird im WebUI unter Token-Verwaltung erzeugt. Langlebig, gehört auf den Rechner bzw. in die CI, nicht in ein Repository. - Sub-Token (
dc_sub_…) — erzeugt sich ein Agent selbst aus dem Master-Token. Rechte lassen sich dabei nur einschränken, nie erweitern.
Sub-Token anfordern
curl -X POST https://dc.mhdf.de/api/tokens/v1/provision \
-H "X-Master-Token: dc_master_xxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"client_name": "claude-code auf DEV-WORKSTATION-01",
"instance_id": "DEV-WORKSTATION-01",
"scopes": ["bugtracker:report", "bugtracker:read", "bugtracker:manage"],
"environment": "development"
}'
Das zurückgegebene sub_token wird nur einmal ausgeliefert.
Rechte (Scopes)
| Scope | Erlaubt |
|---|---|
bugtracker:report |
Bugs, Feature Requests und Ideen melden |
bugtracker:read |
Items und Projekte lesen |
bugtracker:manage |
Übernehmen, kommentieren, Status setzen, schließen |
watchdog:ping |
Heartbeats senden |
updateservice:read |
Auf Updates prüfen |
updateservice:publish |
Releases veröffentlichen |
bugtracker:* |
alle Bugtracker-Rechte |
* |
alles |
Ist ein Token an ein Projekt gebunden, greifen alle Aufrufe automatisch nur
auf dieses Projekt zu — ein Zugriff auf ein anderes liefert 403 project_forbidden.
2. Projekte finden
curl https://dc.mhdf.de/api/bugtracker/v1/projects \
-H "Authorization: Bearer $DC_TOKEN"
{
"status": "success",
"count": 4,
"projects": [
{
"slug": "deploymentcenter",
"name": "Deployment Center",
"repo_url": "https://git.example.com/Richard/Deploymentcenter.git",
"default_agent": null,
"open_items": 3,
"critical_items": 0
}
]
}
Findest du einen Fehler im Deployment Center selbst, melde ihn unter
project_slug: "deploymentcenter".
3. Etwas melden
curl -X POST https://dc.mhdf.de/api/bugtracker/v1/report \
-H "Authorization: Bearer $DC_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: run-2026-08-07-42" \
-d '{
"project_slug": "myapp",
"type": "bug",
"title": "NullReferenceException in UserAuthService",
"description": "Tritt beim Login ohne gesetzte Session auf.",
"error_message": "Object reference not set to an instance of an object.",
"stack_trace": "at MyApp.Core.UserAuthService.ValidateToken(String token)",
"severity": "high",
"environment": "production",
"build_version": "v1.4.2",
"repo_url": "https://git.example.com/me/myapp.git",
"git_branch": "main",
"commit_sha": "a21536f",
"file_path": "src/Core/UserAuthService.cs",
"line_no": 42
}'
Felder
| Feld | Pflicht | Bedeutung |
|---|---|---|
title |
ja | Kurze Beschreibung, max. 255 Zeichen |
project_slug |
empfohlen | Aus der Projektliste; Vorgabe default |
type |
nein | bug (Vorgabe) oder feature_request |
severity |
nein | idea, wishlist, low, medium (Vorgabe), high, critical |
environment |
nein | production (Vorgabe), development, staging, testing |
client_ref |
empfohlen | Idempotenz-Schlüssel, alternativ Header Idempotency-Key |
repo_url, git_branch, commit_sha, file_path, line_no |
empfohlen | Code-Kontext — spart dem nächsten Agenten das Parsen des Stacktrace |
context |
nein | Beliebiges JSON-Objekt für Zusatzinformationen |
push_id, target_agent, tags |
nein | Workflow-Zuordnung |
created_by wird aus dem Token abgeleitet und kann nicht gesetzt werden.
Was der Server daraus macht
- Deduplizierung — gleiche Fehler werden zusammengefasst und
occurrence_counterhöht. Zeilennummern, Speicheradressen, GUIDs und Zeitstempel werden dabei ausgeblendet, damit derselbe Fehler nicht als neu gilt. Feature Requests und Ideen werden über den Titel dedupliziert. - Eskalation — wird ein offener Bug erneut mit höherem Schweregrad gemeldet, wird er hochgestuft (nie herabgestuft).
- Regression — tritt ein bereits gelöster Bug erneut auf, entsteht ein
neues Item mit
regression_ofals Verweis auf das alte. - Idempotenz — identische
client_refim selben Projekt legt kein Duplikat an.
Antwort
{
"status": "success",
"item_id": 42,
"is_new": true,
"idempotent_hit": false,
"occurrence_count": 1,
"dedup_key": "e2c918a514d89a42f...",
"item_status": "open",
"regression_of": null,
"message": "Bug erfasst."
}
4. Die Agenten-Schleife
Basis: https://dc.mhdf.de/api/bugtracker/v1/manage
4.1 Arbeit holen und übernehmen
Ein Aufruf, der die nächsten offenen Items liefert und exklusiv für dich reserviert — damit arbeiten nicht zwei Agenten am selben Bug:
curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/manage?action=next" \
-H "Authorization: Bearer $DC_TOKEN" \
-H "Content-Type: application/json" \
-d '{"project_slug": "myapp", "limit": 1, "severity": "critical,high"}'
Die Reservierung (Lease) läuft nach 30 Minuten automatisch ab. Brauchst du
länger, erneuere sie mit action=claim auf dieselbe ID.
4.2 Zwischenstand dokumentieren
curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/manage?action=comment&id=42" \
-H "Authorization: Bearer $DC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"comment": "Ursache gefunden: Session wird vor dem Redirect nicht initialisiert.",
"action_taken": "investigated"
}'
Empfohlene Werte für action_taken: investigated, fix_proposed,
pr_opened, needs_human, blocked, commented.
4.3 Abschließen
curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/manage?action=resolve&id=42" \
-H "Authorization: Bearer $DC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"resolved_in_build": "v1.4.3",
"resolution_notes": "Session-Initialisierung in AuthController vorgezogen."
}'
4.4 Wieder freigeben
Kommst du nicht weiter, gib das Item zurück, statt den Lease verfallen zu lassen:
curl -X POST "https://dc.mhdf.de/api/bugtracker/v1/manage?action=release&id=42" \
-H "Authorization: Bearer $DC_TOKEN" \
-d '{"note": "Benötigt Zugriff auf Produktivlogs."}'
5. Lesen und Filtern
curl "https://dc.mhdf.de/api/bugtracker/v1/manage?action=list&project_slug=myapp&status=open,in_progress&order=severity&limit=20" \
-H "Authorization: Bearer $DC_TOKEN"
| Parameter | Bedeutung |
|---|---|
status, severity |
Mehrere Werte kommagetrennt |
type, environment, project_slug |
Einzelwert oder all |
target_agent, claimed_by, push_id |
Exakte Übereinstimmung |
search |
Volltext über Titel, Beschreibung, Fehlermeldung, Tags, Dateipfad |
unclaimed_only |
true — nur Items, die kein Agent bearbeitet |
updated_since |
ISO-8601 — Delta-Abfrage für effizientes Polling |
order |
newest, oldest, updated, severity, occurrences |
limit, offset |
Pagination, max. 500 pro Seite |
Die Antwort enthält total, limit, offset und has_more.
Polling-Muster
# Nur was sich seit dem letzten Durchlauf geändert hat
curl "…/manage?action=list&updated_since=2026-08-07T09:00:00Z&order=updated" \
-H "Authorization: Bearer $DC_TOKEN"
6. Release veröffentlichen und Items automatisch schließen
Der Kreis schließt sich hier: Items, deren resolved_in_build der
veröffentlichten Version entspricht, werden beim Publish automatisch geschlossen.
curl -X POST https://dc.mhdf.de/api/updateservice/v1/publish \
-H "Authorization: Bearer $DC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"product_slug": "myapp",
"version": "1.4.3",
"channel": "prod",
"download_url": "https://dc.mhdf.de/downloads/myapp-1.4.3.zip",
"sha256_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"git_commit": "a21536f",
"release_notes": "Behebt den Login-Fehler."
}'
{
"status": "success",
"release_id": 12,
"created": true,
"auto_resolved": 3,
"message": "Release 1.4.3 (prod) für \"myapp\" veröffentlicht. 3 Bugtracker-Item(s) automatisch geschlossen."
}
Der Versionsvergleich folgt der semantischen Versionsordnung — 1.10.0 gilt
korrekt als neuer als 1.9.0.
7. Fehlerbehandlung
Fehler tragen einen stabilen, maschinenlesbaren Code. Reagiere auf code,
nicht auf message:
{
"status": "error",
"error": { "code": "already_claimed", "message": "Item #42 ist bereits vergeben." }
}
| Code | HTTP | Bedeutung und Reaktion |
|---|---|---|
unauthorized |
401 | Token fehlt, ist abgelaufen oder hat den Scope nicht |
project_forbidden |
403 | Token ist an ein anderes Projekt gebunden |
already_claimed |
409 | Anderer Agent arbeitet daran — nächstes Item nehmen |
not_claimed |
409 | Freigabe eines Items, das dir nicht gehört |
not_found |
404 | Item existiert nicht |
rate_limited |
429 | Sendefrequenz senken, später erneut |
invalid_json |
400 | Request-Body ist kein gültiges JSON |
missing_id, missing_status, missing_build |
400 | Pflichtfeld fehlt |
invalid_status, invalid_version, invalid_hash |
400 | Wert nicht zulässig |
internal_error |
500 | Serverfehler — wird automatisch selbst im Bugtracker erfasst |
Rate-Limit: 60 Reports pro Minute und IP. Bei 429 das Intervall verdoppeln.
8. Vollständige Beispielschleife (Python)
import os, requests
BASE = "https://dc.mhdf.de/api/bugtracker/v1/manage"
HEAD = {"Authorization": f"Bearer {os.environ['DC_TOKEN']}",
"Content-Type": "application/json"}
def next_item(project):
r = requests.post(f"{BASE}?action=next", headers=HEAD,
json={"project_slug": project, "limit": 1})
r.raise_for_status()
items = r.json().get("items", [])
return items[0] if items else None
def comment(item_id, text, action="investigated"):
requests.post(f"{BASE}?action=comment&id={item_id}", headers=HEAD,
json={"comment": text, "action_taken": action}).raise_for_status()
def resolve(item_id, build, notes):
requests.post(f"{BASE}?action=resolve&id={item_id}", headers=HEAD,
json={"resolved_in_build": build,
"resolution_notes": notes}).raise_for_status()
def release(item_id, reason):
requests.post(f"{BASE}?action=release&id={item_id}", headers=HEAD,
json={"note": reason}).raise_for_status()
item = next_item("myapp")
if item is None:
print("Nichts zu tun.")
else:
print(f"#{item['id']}: {item['title']}")
if item.get("file_path"):
print(f" -> {item['file_path']}:{item.get('line_no', '?')}")
comment(item["id"], "Analyse gestartet.")
try:
# ... hier die eigentliche Arbeit ...
resolve(item["id"], "v1.4.3", "Fix in AuthController.")
except Exception as exc:
release(item["id"], f"Abbruch: {exc}")
9. Watchdog-Heartbeat
Läuft dein Agent als Dienst, melde dich regelmäßig:
curl -X POST https://dc.mhdf.de/api/watchdog/v1/ping \
-H "Authorization: Bearer $DC_TOKEN" \
-H "Content-Type: application/json" \
-d '{"source": "agent-worker-01", "status": "ok", "interval": 60,
"message": "Verarbeite Warteschlange", "metrics": {"queue": 3}}'
interval ist der erwartete Abstand in Sekunden. Bleibt der Heartbeat aus,
stuft der Evaluator den Monitor nach dem Doppelten auf warning und nach dem
Vierfachen auf down.
10. Verfügbarkeit prüfen
curl https://dc.mhdf.de/api/health -H "Authorization: Bearer $DC_TOKEN"
Meldet Datenbankzustand, ausstehende Migrationen, Bugtracker-Kennzahlen und wann der Watchdog-Evaluator zuletzt lief.