Alle drei Pruefungen real durchgefuehrt und bestanden. Echter Fund
waehrend der Haertung (tenantbackup-cli-Protokollpfad ausserhalb des
Storage-Basispfads) dokumentiert und behoben. Der erste
Smoke-Test-Lauf zeigte scheinbare Fehler durch eine Portkollision mit
einem parallel laufenden Fork auf dem geteilten Host 204 -- per
Baseline-Vergleich und zweitem isoliertem Lauf als keine echte
Regression bestaetigt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
10 bisher ungehaertete Archive-Backup/Reconcile/Scrub-Oneshot-Vorlagen
(deploy/systemd/) bekommen die etablierten Sandbox-Direktiven
(NoNewPrivileges, ProtectSystem=strict, ProtectHome, PrivateTmp, ggf.
ReadWritePaths). Drei Vorlagen ohne Dateisystemzugriff (reconcile,
retention-notify, scrub) bekommen bewusst keine ReadWritePaths,
begruendet dokumentiert (S3-kompatible Storage-Abstraktion statt
lokaler Pfade).
Neue, getrennte Postgres-Laufzeit-Rolle (nexarch_tenant_runtime, OHNE
CREATEDB) statt der bisher gemeinsam genutzten CREATEDB-Rolle fuer
NEXARCH_TENANT_DSN_TEMPLATE. Provisioner.Provision() berechtigt die
neue Rolle je frisch angelegter Tenant-DB (GRANT CONNECT + Schema-
Rechte inkl. Default-Privileges), Tenant.DBDSN wird jetzt mit dieser
Rolle statt der CREATEDB-Rolle gebaut. Neue Umgebungsvariable
NEXARCH_TENANT_RUNTIME_DSN_TEMPLATE, deploy/install.sh legt die Rolle
automatisch an.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Drei beim ersten echten 131-Testlauf gefundene Luecken geschlossen:
- rollout-131-selfheal.sh prueft/baut jetzt fehlende Ziel-Binaries nach
(dms-app, dms-worker, connect-app), statt anzunehmen dass
rollout-131-apply.sh das schon erledigt hat.
- rollout-131-selfheal.sh leitet dms-worker.env aus dms-app.env ab, falls
sie fehlt (identische Logik wie in rollout-131-apply.sh, dort aber nie
erreicht, wenn das Skript vorher an fehlenden TODO-Werten abbricht).
- rollout-131-healthcheck.sh liest die tatsaechlichen Listen-Adressen aus
den Env-Dateien statt fest angenommene Ports (dms-app lief real auf
127.0.0.1:8096, nicht dem angenommenen 8600).
Weiterhin kein systemctl-Aufruf in beiden Skripten.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Automatisiert alles vor dem Dienst-Start (Env-Vervollstaendigung inkl.
neuem NEXARCH_CORE_ADMIN_TOKEN, Build, Verzeichnisse, Unit-Dateien) in
einer Wiederholschleife. Ruft bewusst kein systemctl auf -- der
Dienst-Start bleibt manueller letzter Schritt, wird am Ende als
fertiger Befehlsblock ausgegeben.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Buendelt Connect-DB-Anlage, KEK-Client-Registrierung fuer DMS und
eine klare Meldung fuer den noch fehlenden Core-Service-Token-Endpunkt
(INT-14) in einem Lauf. Schreibt Secrets direkt in die Env-Dateien,
gibt sie nie aus. Kein systemctl-Aufruf.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Branch docs/rollout-131-vorbereitung war von einem älteren main-Stand
abgezweigt; nur die neuen Deploy-Dateien werden hier sauber auf den
aktuellen main-Stand übertragen, ohne die seither gemergten Pakete zu
verlieren.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Google Chrome stable 154.0.8037.57 + pa11y 10.0.0 auf Entwicklungshost 204
installiert (Prüfsumme/GPG-Signatur verifiziert, deploy/chrome/README.md).
web/a11y-audit/run.sh lief damit erstmals real dort: 68/68 Prüfschritte
bestanden, keine axe-Verstöße, pdf-viewer vollständig (vorher 1/5).
Echter Fund behoben: keyboard-flow.js rief die API ohne Identität auf,
folderapi.handleDocuments verlangt aber immer 'caller'/X-User-Id.
UX-11 (vier neue Seiten) und ein Token-Modus-Lauf bleiben offen, weil sie
eine eigenständige Erweiterung der Testfixture brauchen, kein Nachtrag.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Auf 192.168.1.131 belegen Core-Dienste 8098-8102; der bisherige DMS-Bereich
8100-8199 haette kollidiert. Register erlaubt mehrere Bereiche je Modul und
prueft Ueberschneidungen zwischen Modulen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Testumgebung enthaelt bei jedem BAK-06-Lauf echte wiederhergestellte
Mandantendaten - Schutzniveau war bisher nur implizit im Code vorhanden,
nicht explizit festgelegt/dokumentiert.
- Zugriffskontrolle: User=nexarch identisch zu den produktiven
Sicherungsdiensten (unveraendert, jetzt explizit verglichen/dokumentiert).
- Netzwerksegmentierung: testPostgresqlConf/testPgHbaConf als benannte
Konstanten extrahiert und direkt testbar gemacht (kein Netzwerkzugriff
moeglich); PrivateNetwork=true/NoNewPrivileges=true/ProtectHome=true
zusaetzlich in nexarch-archive-restoretest.service.tmpl erzwungen.
- Loeschfrist: bestehendes defer-os.RemoveAll-Verhalten real verifiziert,
auch im Fehlerfall - keine Aenderung noetig, nur Nachweis erbracht.
Schutzniveau jetzt explizit in docs/BAK-06-PRUEFPROTOKOLL.md benannt statt
nur implizit im Code zu stecken.
Fund waehrend Recherche: es existiert ein zweiter, aelterer Archive-
Branch-Strang (z.B. feature/cmp-02-dsgvo-datenschutz-berichte) mit
eigener Migrationsnummerierung (0001-0009) ohne RET-11/RET-12/RBAC-06 -
nie mit dem hier fortgefuehrten Strang zusammengefuehrt. CMP-05 (naechste
Kachel im Backlog) haengt von CMP-01 (dieser Strang) UND CMP-02 (anderer
Strang) ab und ist damit aktuell nicht sauber umsetzbar, ohne beide
Straenge zusammenzufuehren - das ist kein Auftrag dieses Tickets.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Haertet die Control-Plane-Registry als eigene Schutzzone:
- Dedizierte Postgres-Rolle nexarch_registry (ohne CREATEDB) fuer
NEXARCH_REGISTRY_DSN, getrennt von der Provisioning-Rolle nexarch_core
(deploy/install.sh). Neu angelegte Tenant-Datenbanken entziehen PUBLIC
explizit das CONNECT-Recht (internal/tenant/provisioner.go), damit die
Registry-Rolle unter keinen Umstaenden Zugriff auf eine Tenant-Datenbank
erhaelt (Akzeptanzkriterium 1).
- Auditierte Zugriffsmethoden (*AsActor) fuer Superadmin-Konten
(internal/user.SuperadminStore) und Verbindungsinformationen
(internal/tenant.Registry) — jeder lesende/schreibende Zugriff erzeugt
einen Audit-Event mit Akteur und Zeitstempel, verdrahtet in
internal/tenantadmin (TEN-05-Konsole) und cmd/core (Akzeptanzkriterium 2).
- Betriebsanleitung docs/TEN-09-REGISTRY-BETRIEB.md dokumentiert
Rollentrennung und den auf den Core-Dienst beschraenkten Netzwerkzugriff
(pg_hba.conf, listen_addresses, Firewall-Default-Policy), abgesichert
durch internal/opsdocs/registry_hardening_test.go (Akzeptanzkriterium 3).
Automatisierte Tests je Akzeptanzkriterium: internal/tenant/isolation_test.go
(negativer Zugriffstest), internal/audit/wiring_test.go
(TestWiring_SuperadminReadIsAudited, TestWiring_RegistryConnectionInfoReadIsAudited),
internal/opsdocs/registry_hardening_test.go.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QPsyieCYwvbUBsnN598SbP
- cmd/kek-api: startet internal/kek.Handler.TenantKEKHandler (API-10),
tenantResolverAdapter bildet tenant.Registry.GetBySlug auf
kek.TenantResolver ab (nur Signatur-Anpassung)
- reines Wiring, kein Diff an internal/kek|moduleregistry|tenant
(verifiziert)
- real deployed auf 131, end-zu-ende bewiesen: echtes Modul
registriert+provisioniert, echter Tenant-KEK erzeugt, curl gegen
laufenden Dienst liefert exakt denselben KEK zurueck (byte-fuer-byte
verglichen), falsches Credential -> 403
- reale Grant-Luecke gefunden und behoben (nexarch_core auf
tenant_keks), verifiziert
Pruefungen siehe docs/API-12-PRUEFPROTOKOLL.md
- cmd/notifyprefs-api: startet internal/notifyprefs.Handler (CFG-04),
hinter auth.RequireAuth mit demselben JWT-Secret wie IAM-16
- reines Wiring, kein Diff an internal/notifyprefs/ (verifiziert)
- real deployed auf 131, cross-service-Session real bewiesen: Login
gegen account-api, dasselbe Cookie gegen notifyprefs-api verwendet ->
echtes Setzen und Lesen einer Praeferenz, ohne Cookie -> 401
- nutzt dieselbe nexarch_registry-DB wie CFG-05, Grants bereits
vorhanden
Pruefungen siehe docs/CFG-06-PRUEFPROTOKOLL.md
- cmd/rbac-admin-api: startet internal/rbac.Handler (RBAC-05), hinter
auth.RequireAuth mit demselben JWT-Secret wie IAM-16 (account-api)
- reines Wiring, kein Diff an internal/rbac/ (verifiziert)
- real deployed auf 131, cross-service-Session real bewiesen: Login
gegen account-api (Port 8099), dasselbe Session-Cookie gegen
rbac-admin-api (Port 8100) verwendet -> echte Rollenliste, ohne
Cookie -> 401
- Migrationen (role_assignments/groups) real auf tenant_acme
angewendet, reale Grant-Luecke behoben und verifiziert
- Bootstrap-Erkenntnis dokumentiert: ListRoles selbst verlangt bereits
eine Rolle mit PermManageUsers, Testnutzer daher mit direkter
DB-Bootstrap-Rolle angelegt (wie Ersteinrichtung)
Pruefungen siehe docs/RBAC-07-PRUEFPROTOKOLL.md
- archive/cmd/moduleadapter-api: startet den fertigen RET-05
RegisterHandler als eigenstaendigen HTTP-Dienst, Port 8095
- reines Wiring, kein Diff an internal/moduleadapter/ (verifiziert)
- real deployed auf 131, end-zu-ende per curl: Registrierung +
Idempotenz-Nachweis (widerspruechliche zweite Werte werden ignoriert,
urspruengliche Registrierung bleibt bestehen)
Pruefungen siehe archive/docs/RET-09-PRUEFPROTOKOLL.md
- internal/notifyapi.Mount (POST /notify/enqueue), reiner Wrapper um
notifyprefs.EnqueueIfAllowed (CFG-04)
- Reuse von internal/policyapi.RequireServiceToken (RBAC-06), kein
neues Provisorium, eigener Service-Token pro Endpunkt
- Tests: 401 ohne Token, Aequivalenz HTTP vs. direkter Aufruf
(aktiviert/deaktiviert), realer Fremd-Modul-Client mit echter
notification_jobs-Zeile
- real deployed auf 131 (notify-api, Port 8094), Grant-Nachverfolgung
fuer nexarch_core auf notification_preferences/notification_jobs,
end-zu-ende per curl nachgewiesen (401, job_id+skipped:false)
Pruefungen siehe docs/CFG-05-PRUEFPROTOKOLL.md
internal/policyapi: POST /authorize wrapt policy.Enforcer.Authorize
(RBAC-02) fuer physisch getrennte Module (DMS, Mail, Archive) - reiner
Wrapper, keine zweite Autorisierungslogik, 100 Stichproben beweisen
Uebereinstimmung mit dem direkten Enforcer-Aufruf. Service-Auth ueber
schlanken, timing-safe verglichenen Token statt moduleregistrys
schwererem Credential-System (unnoetige internal/flag-Abhaengigkeit,
nie mit internal/policy gemergt). Ersetzt spaeter das dokumentierte
RET-06-API-Provisorium (Archive, eigenes Folgeticket). Reale
Rechtevergabe-Luecke auf policy_rules gefunden und behoben (Tabelle
von frueherem Testlauf unter anderem Owner). Real auf 131 deployed,
beide Pfade (Allow/Deny) per curl end-to-end verifiziert.
Board-Entscheidung: Backend-API zuerst, echtes Next.js-Frontend als
separates Folgeticket - vermeidet Pseudo-Frontend-Protokoll.
internal/retentionapi: 4 Endpunkte (anlegen/aendern, deaktivieren,
liste, vorschau), Vorschau nutzt dieselbe ListExpiringObjects-Funktion
wie RET-02s periodischer Job (keine Doppel-Implementierung).
RequireRole ist AUSDRUECKLICH kein RBAC-02-Ersatz, sondern ein
dokumentiertes Provisorium (Header-Check) - RBAC-02 ist reiner
Core-interner Go-Code ohne HTTP-Schnittstelle fuer andere Module,
derselbe Befund wie FDN-03/FDN-09. Provisorium real getestet inkl.
Negativfall (403 ohne/mit falscher Rolle). retention_class_rules um
active-Flag erweitert (deaktivieren ohne Historienverlust). Real auf
131 deployed und per curl end-to-end verifiziert.
internal/tenantbackup: datenbank-scharfes pg_dump/pg_restore statt
BAK-01s Cluster-weitem pg_basebackup - bei Modell C (TEN-01, physisch
isolierte DB je Mandant) wuerde ein Cluster-Restore zwangslaeufig ALLE
Mandanten ueberschreiben. Objekt-Seite nutzt BAK-02 direkt (Mandanten
haben eigene Buckets/Pfad-Roots). Eigene Postgres-Rolle
nexarch_tenantbackup (CREATEDB, kein Superuser, getrennt von
nexarch_backup). Zwei-Tenant-Isolation real in beide Richtungen
bewiesen (Markerwert-Nachweis), JSONL-Protokoll fuer Sicherung UND
Restore. Realer End-zu-Ende-Lauf ueber tenantbackup-cli auf 131.
internal/restoretest wiederholt BAK-03s eigene Pruefdiskt als
Produktcode: echter Restore der neuesten Sicherung, echter Kurzstart
einer isolierten Postgres-Instanz, echte SELECT-1-Abfrage; Objekt-Seite
echter restic-Restore + Inhaltspruefung. JSONL-Historie (append-only),
sichtbare Warnung ueber denselben OPS-05-Pull-Weg wie BAK-08, eigenes
/metrics-Modul (archive-restoretest). Zwei reale Defekte gefunden und
behoben: Unix-Socket-Pfadlaenge unter tief verschachtelten Testpfaden,
restic-stderr-Vermischung beim JSON-Parsen unter dem Dienstnutzer.
Real auf 131 verdrahtet und ausgeloest (beide Testarten erfolgreich,
End-zu-Ende ueber OPS-03 bestaetigt).
Deterministischer Abgleich zwischen DMS file_revisions und
Objekt-Storage-Verzeichnis, existenz-only (keine Inhaltspruefung,
saubere Abgrenzung zu BAK-08). Report sortiert nach storage_key fuer
stabile Weiterverarbeitung durch BAK-08. Systemd-Timer taeglich,
real auf 192.168.1.131 verdrahtet und ausgeloest.
restic statt Eigenbau (Content-defined Chunking fuer Dedup, verschluesseltes
Repository ab Werk, geprueftes Tooling). internal/objectbackup wrapt
restic init/backup/check/forget ueber os/exec, cmd/objectbackup-cli fuer
systemd-Timer (stuendliche Sicherung, woechentliche Vollstaendigkeitspruefung,
taegliche Rotation).
Auf 192.168.1.131 real verifiziert (kein Mock): zweiter Lauf gegen
unveraenderten Bestand ueberraegt nichts (files_new=0), zwei identische
Dateien erzeugen nachweislich nur 1 data_blob (echter Dedup-Nachweis),
absichtlich gekipptes Byte in einer Pack-Datei wird von check --read-data
zuverlaessig erkannt. Echt verdrahtet: 3 systemd-Timer installiert+
aktiviert, jeder Dienst einmal end-to-end via systemctl start ausgeloest.
Im selben Rutsch BAK-01-Fund korrigiert: NEXARCH_BACKUP_DIR zeigte auf
/var/backups/nexarch (Root-Dateisystem, kein Dataset) statt auf das
persistente ZFS-Dataset /var/nexarch-archiv - korrigiert, erneut ausgeloest,
landet nachweislich am richtigen Ort. objectbackup-cli-Repository liegt von
Anfang an korrekt dort.
Siehe archive/docs/BAK-02-PRUEFPROTOKOLL.md fuer alle Pruefungsergebnisse.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
Neues Modul-Verzeichnis code/archive/ (Monorepo-Muster wie code/dms/).
PostgreSQL-17-natives inkrementelles Backup (pg_basebackup --incremental,
WAL-Summarization) statt WAL-Archiving, um den geteilten Testhost ohne
Neustart umzustellen (summarize_wal=on per pg_reload_conf). Rolle
nexarch_backup mit REPLICATION-Attribut angelegt.
internal/backup: FullBackup/IncrementalBackup (pg_basebackup-Wrapper),
Verify (vollstaendiges Lesen von base.tar.gz, gzip+tar, nicht nur Header),
Rotate/ListGenerations (generationsbasiert, aeltere zuerst entfernt).
cmd/backup-cli fuer systemd-Timer-Aufruf (deploy/systemd/
nexarch-archive-backup-*.timer, taeglich/stuendlich/taeglich).
Auf 192.168.1.131 verifiziert: 4/4 Tests gegen echte Postgres-17-Instanz
(kein Mock) - inkrementelle Sicherung real kleiner als Vollsicherung,
Verifikation erkennt absichtlich beschaedigte Datei, Rotation entfernt nur
die aeltesten Generationen. Zusaetzlich ECHT verdrahtet: backup-cli gebaut,
3 systemd-Timer installiert+aktiviert, jeder der drei Dienste einmal ueber
systemctl start end-to-end ausgeloest (status=0/SUCCESS je Dienst) - nicht
nur go test, sondern der reale Automatisierungspfad selbst geprueft.
Siehe archive/docs/BAK-01-PRUEFPROTOKOLL.md fuer alle Pruefungsergebnisse.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
deploy/install.sh: idempotenter Installer fuer Debian/systemd. Legt
Systembenutzer 'nexarch', Postgres-Rolle+Registry-DB an, baut alle 7
Core-Binaries (cmd/core + *-devserver), wendet Registry-Migrationen an
(nur bei leerer DB), generiert Secrets (Audit-Admin-Token, Ed25519-
Lizenzschluesselpaar) nach /etc/nexarch/core.env (0600), installiert und
startet je Dienst einen systemd-Service.
Tatsaechlich auf 192.168.1.131 ausgefuehrt und verifiziert: alle 7 Dienste
laufen (systemctl active), alle Health-Endpunkte antworten 200. Zweiter
Installer-Lauf bestaetigt Idempotenz (ueberspringt bestehende Rolle/DB/
Secrets/Migrationen, Dienste bleiben durchgehend aktiv).
Hinweis: kein unified Server (siehe QA-05/AUD-06-Befund) - weiterhin 7
getrennte Binaries/Ports, dieser Installer deployt sie so wie sie sind.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ