**Warum keine Wiederherstellung funktionierte.** Der Dienst laeuft mit
`ProtectSystem=strict` und `ReadWritePaths` nur auf Repository und
Sicherungsordner — alles andere ist fuer ihn schreibgeschuetzt. Jedes Ziel
ausserhalb endete mit "mkdir: permission denied", und zwar **nach** der
Vorabpruefung. `/tmp` scheiterte anders: Mit `PrivateTmp=yes` hat der Dienst ein
eigenes /tmp, und was dort landet, sieht man von aussen gar nicht.
`setup.sh` legt jetzt `/srv/syncova-restore` an und traegt es in
`ReadWritePaths` ein; `--wiederherstellungsziel` ergaenzt weitere. Der Ort liegt
unter /srv und nicht unter /var/lib — Letzteres steht auf der Sperrliste des
Zielschutzes. Beide Regeln zugleich zu erfuellen laesst genau /srv uebrig; das
ist mir erst aufgefallen, nachdem ich die Flaeche zunaechst falsch gelegt hatte
und der eigene Zielschutz sie ablehnte.
**Zwei neue Endpunkte** (Vertrag entsprechend erweitert):
- `GET /filesystem/browse` — Verzeichnisse mit der Angabe, ob der **Dienst**
dort schreiben darf. **Gemessen** durch eine Probedatei, nicht aus den
Rechtebits geraten: Unter ProtectSystem=strict sagen die Bits nichts ueber
das aus, was der Namensraum zulaesst. Gesperrte Orte werden gezeigt, nicht
versteckt — sonst bliebe offen, warum ein Pfad fehlt.
- `GET /backups/{id}/contents` — das Manifest als Ebene eines Baums. Der Baum
entsteht aus den **Pfaden**, nicht aus Verzeichniseintraegen: Ein Manifest
kann eine Datei enthalten, deren Elternverzeichnis nicht als eigener Eintrag
vorliegt, und wer nur `directory`-Eintraege auflistet, verliert ganze
Teilbaeume. Durch Mutation bestaetigt.
**Auswahl statt Textfeld.** Der Assistent hat jetzt einen Ordnerbaum fuer das
Ziel und einen Browser fuer den Backup-Inhalt. Ordner **und** einzelne Dateien
lassen sich waehlen; beides geht als `path_prefix` in die Anfrage, weil der
Server auf Gleichheit oder Praefix mit Verzeichnisgrenze vergleicht. Bewusste
Grenze: eine Auswahl je Lauf — eine Liste kennt die API nicht, und mehrere
Laeufe vorzutaeuschen ergaebe mehrere Ausgaenge, die niemand mehr erklaeren
kann.
**Integritaetslauf.** "can't access property toLocaleString, chunks_checked is
undefined" — die Ergebnisse liegen unter `details`, und die Felder heissen
`missing_chunks`/`corrupted_chunks`, nicht umgekehrt. Betrifft alle vier
Pruefendpunkte; sie tragen dieselbe Huelle. Derselbe Fehler wie bei
/retention-policies: die Antwortform angenommen statt geprueft.
**Geist Mono liegt jetzt im Paket** (drei Schnitte, 128 KB, OFL-Lizenz dabei).
Ausgeliefert vom eigenen Ursprung — das verlangt die CSP, und ein
Backup-Server, dessen Oberflaeche von einem CDN abhaengt, waere auch ohne CSP
falsch. `font-display: swap`, damit der Text sofort steht.
Nachgewiesen gegen Debian 12: Vollwiederherstellung (5 Dateien), nur ein Ordner
(2 Dateien), nur eine Datei (1 Datei) — alle drei bitgenau. Der Server nennt
/srv/syncova-restore als beschreibbar und /etc, /usr, /var als gesperrt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
127 lines
5.5 KiB
Plaintext
127 lines
5.5 KiB
Plaintext
# Eingefrorener API-Vertrag (Phase 22)
|
|
#
|
|
# Eine Zeile je Endpunkt: <METHODE> <pfad> <berechtigung>
|
|
#
|
|
# "/api/v1" ist ausgeliefert. Diese Datei zu aendern ist erlaubt — aber es ist
|
|
# eine bewusste Handlung, und genau darum geht es. Drei Faelle:
|
|
#
|
|
# Neuer Endpunkt -> eintragen. Er nimmt niemandem etwas weg.
|
|
# Geaenderte Berechtigung -> pruefen. Eine gelockerte Pruefung ist die
|
|
# gefaehrlichste Aenderung ueberhaupt: Der Endpunkt
|
|
# funktioniert weiter, nur duerfen ihn ploetzlich
|
|
# mehr Leute aufrufen.
|
|
# Entfallener Endpunkt -> gehoert nicht hierher. Bestehende Aufrufer brechen;
|
|
# das ist ein Fall fuer /api/v2.
|
|
#
|
|
# "-" bedeutet: ohne Anmeldung erreichbar. Das sind genau die Endpunkte, die es
|
|
# sein muessen — Anmeldung selbst und die Aufnahme eines Agenten, der noch kein
|
|
# Betriebstoken besitzt.
|
|
# "agent-token" bedeutet: Betriebstoken eines Agenten statt Benutzersitzung.
|
|
# "sitzung" bedeutet: angemeldet, aber ohne besondere Berechtigung — die
|
|
# Endpunkte, die jeder ueber sich selbst aufruft.
|
|
|
|
DELETE /api/v1/backups/{id} backups.delete
|
|
DELETE /api/v1/backups/{id}/legal-hold immutability.manage
|
|
DELETE /api/v1/jobs/{id} jobs.write
|
|
DELETE /api/v1/notification-channels/{id} settings.write
|
|
DELETE /api/v1/proxmox/clusters/{id} providers.write
|
|
DELETE /api/v1/retention-policies/{id} retention.write
|
|
DELETE /api/v1/roles/{id} roles.write
|
|
DELETE /api/v1/users/{id} users.write
|
|
GET /api/v1/agents agents.read
|
|
GET /api/v1/agents/{id} agents.read
|
|
GET /api/v1/agents/{id}/health agents.read
|
|
GET /api/v1/alerts alerts.read
|
|
GET /api/v1/alerts/summary alerts.read
|
|
GET /api/v1/alerts/{id} alerts.read
|
|
GET /api/v1/audit-events audit.read
|
|
GET /api/v1/backups backups.read
|
|
GET /api/v1/backups/{id}/assurance backups.read
|
|
GET /api/v1/backups/{id}/protection backups.read
|
|
GET /api/v1/backups/{id}/ransomware-assessment backups.read
|
|
GET /api/v1/dashboard backups.read
|
|
GET /api/v1/health -
|
|
GET /api/v1/jobs jobs.read
|
|
GET /api/v1/jobs/{id} jobs.read
|
|
GET /api/v1/jobs/{id}/runs jobs.read
|
|
GET /api/v1/me sitzung
|
|
GET /api/v1/metrics monitoring.read
|
|
GET /api/v1/metrics/{metric} monitoring.read
|
|
GET /api/v1/notification-channels settings.read
|
|
GET /api/v1/permissions roles.read
|
|
GET /api/v1/proxmox/clusters providers.read
|
|
GET /api/v1/proxmox/clusters/{id} providers.read
|
|
GET /api/v1/proxmox/clusters/{id}/hosts providers.read
|
|
GET /api/v1/proxmox/clusters/{id}/vms providers.read
|
|
GET /api/v1/proxmox/vms/{id} providers.read
|
|
GET /api/v1/reports reports.read
|
|
GET /api/v1/repositories repositories.read
|
|
GET /api/v1/repositories/{id} repositories.read
|
|
GET /api/v1/restores restores.read
|
|
GET /api/v1/restores/{id} restores.read
|
|
GET /api/v1/retention-policies repositories.read
|
|
GET /api/v1/roles roles.read
|
|
GET /api/v1/roles/{id} roles.read
|
|
GET /api/v1/security security.read
|
|
GET /api/v1/security/findings security.read
|
|
GET /api/v1/users users.read
|
|
GET /api/v1/users/{id} users.read
|
|
GET /api/v1/verification verification.read
|
|
GET /api/v1/verification/{id} verification.read
|
|
GET /api/v1/verification/{id}/results verification.read
|
|
GET /api/v1/virtual-machines providers.read
|
|
GET /health/live -
|
|
GET /health/ready -
|
|
PATCH /api/v1/repositories/{id} repositories.write
|
|
PATCH /api/v1/retention-policies/{id} retention.write
|
|
PATCH /api/v1/roles/{id} roles.write
|
|
PATCH /api/v1/users/{id} users.write
|
|
POST /api/v1/agents/enrollment-tokens agents.enroll
|
|
POST /api/v1/agents/heartbeat agent-token
|
|
POST /api/v1/agents/register -
|
|
POST /api/v1/agents/tasks/claim agent-token
|
|
POST /api/v1/agents/tasks/{id}/progress agent-token
|
|
POST /api/v1/agents/tasks/{id}/result agent-token
|
|
POST /api/v1/agents/{id}/revoke agents.write
|
|
POST /api/v1/agents/{id}/rotate-credentials agents.write
|
|
POST /api/v1/alerts/{id}/acknowledge alerts.write
|
|
POST /api/v1/alerts/{id}/resolve alerts.write
|
|
POST /api/v1/auth/login -
|
|
POST /api/v1/auth/logout sitzung
|
|
POST /api/v1/auth/mfa/verify -
|
|
POST /api/v1/auth/refresh -
|
|
POST /api/v1/backup-runs/{id}/cancel jobs.run
|
|
POST /api/v1/backups/{id}/legal-hold immutability.manage
|
|
POST /api/v1/backups/{id}/retention/extend immutability.manage
|
|
POST /api/v1/jobs jobs.write
|
|
POST /api/v1/jobs/{id}/pause jobs.run
|
|
POST /api/v1/jobs/{id}/resume jobs.run
|
|
POST /api/v1/jobs/{id}/run jobs.run
|
|
POST /api/v1/me/mfa/confirm sitzung
|
|
POST /api/v1/me/mfa/enroll sitzung
|
|
POST /api/v1/notification-channels settings.write
|
|
POST /api/v1/proxmox/clusters providers.write
|
|
POST /api/v1/proxmox/clusters/{id}/discover providers.write
|
|
POST /api/v1/proxmox/clusters/{id}/test providers.write
|
|
POST /api/v1/reports/generate reports.read
|
|
POST /api/v1/repositories repositories.write
|
|
POST /api/v1/repositories/{id}/enforcement/measure repositories.write
|
|
POST /api/v1/repositories/{id}/health-check repositories.read
|
|
POST /api/v1/repositories/{id}/integrity-scan repositories.write
|
|
POST /api/v1/repositories/{id}/rebuild-catalog repositories.write
|
|
POST /api/v1/repositories/{id}/retention/apply retention.write
|
|
POST /api/v1/repositories/{id}/retention/preview repositories.read
|
|
POST /api/v1/repositories/{id}/test repositories.read
|
|
POST /api/v1/restores restores.execute
|
|
POST /api/v1/restores/validate restores.read
|
|
POST /api/v1/restores/{id}/cancel restores.execute
|
|
POST /api/v1/restores/{id}/resume restores.execute
|
|
POST /api/v1/retention-policies retention.write
|
|
POST /api/v1/roles roles.write
|
|
POST /api/v1/users users.write
|
|
POST /api/v1/users/{id}/mfa/disable users.write
|
|
POST /api/v1/verification verification.write
|
|
POST /api/v1/verification/{id}/cancel verification.write
|
|
GET /api/v1/filesystem/browse restores.read
|
|
GET /api/v1/backups/{id}/contents restores.read
|