Der automatische Merge von PR #217 (OPS-12) gegen main liess
internal/tenant/provisioner.go::ProvisionWithID mit einem nicht mehr
existierenden Feld (p.dsnTemplate) zurueck und vergass dort den
grantRuntimeRole-Aufruf, den Provision() bereits hat -- echter
Build-Fehler, kein reiner Textkonflikt, git markierte ihn nicht als
Konflikt. Drei weitere Aufrufer von NewProvisioner (Migrationswerkzeug,
Cutover-Orchestrator, Test) riefen noch mit der alten Drei-Parameter-
Signatur auf. Alle auf die neue Vier-Parameter-Signatur (Admin-DSN +
Runtime-DSN) nachgezogen, wo keine eigene Runtime-DSN-Variable existiert
faellt auf dieselbe Vorlage zurueck (admin-only Werkzeuge).
Verifiziert auf Host 204: go build/vet sauber, betroffene Pakete
(tenant, migrationtenant, cutoverorchestrator, cor01-tenant-migrate)
gruen. Einziger verbleibender FAIL (TestAllHTTPServersUseHTTPHarden)
ist der bereits bekannte, unabhaengige Befund aus PR #345 (noch nicht
gemergt), keine neue Regression.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
PR #217 (OPS-12) war nicht mergefaehig: additiver Konflikt in Config{}
-- main hatte inzwischen ModuleDirectoryPath ergaenzt, OPS-12 ergaenzt
unabhaengig davon TenantRuntimeDSNTemplate. Beide Felder behalten,
keine inhaltliche Aenderung an der eigentlichen OPS-12-Logik.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Orchestrator (internal/cutoverorchestrator, cmd/cutover-dryrun) fuer den
Trockenlauf aller Migrationspakete gegen eine vollstaendig synthetische
Testkopie: Ablaufplan mit Abhaengigkeitspruefung, Abbruch beim ersten
Fehler, JSON-/Textprotokoll mit Redigierung von Zugangsdaten, Generator
und Anonymisierungsnachweis der Testkopie.
Modulstufen (dms/mail/archive/workflow cmd/cutover-stage) liefern die
Quell-Adapter und rufen die bestehenden Migratoren der jeweiligen Module
auf, da deren internal-Pakete aus dem Root-Modul nicht importierbar sind.
migrationdocument.DocumentID macht die DOK-01-Ziel-ID fuer TAR-01/TAR-02
im Archive-Modul verfuegbar.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Export der users-Tabellen aus archivmail/archivdms (internal/usermatch)
und E-Mail-Abgleich ausschliesslich innerhalb bereits per MAP-02
freigegebener Tenants (tenantapproval.Store.Approved()). Kandidatenliste
als CSV, kein automatischer Merge -- Freigabe bleibt MAP-04.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Generischer SQL-Export der tenants-Tabellen (slug/name/domain), automatischer
Abgleichsvorschlag per Slug- (hart) und Domain-Matching (weicher Fallback) mit
Konfidenzwert, uebersteuerbar durch eine manuell gepflegte CSV-Zuordnungsliste.
Konflikte zwischen automatischem Vorschlag und manueller Liste werden sichtbar
markiert, nie still aufgeloest. Ausgabe als Kandidatenliste (CSV), direkt
nutzbar als Eingabe fuer die menschliche Freigabe in tenant_migration_map
(COR-01/MAP-02).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Uebernahme freigegebener Benutzer aus user_migration_map in Core IAM-01,
bcrypt-Hashes 1:1 ohne Rehash, analog zu COR-01s Tenant-Migration-Pattern.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Uebernahme freigegebener tenant_migration_map-Eintraege (MAP-02) als
Core-Tenants (TEN-01), tenants.id folgt der bereits freigegebenen
nexarch_tenant_id. Idempotent, kein Import ohne Freigabe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
seal/open (bzw. wrap/unwrap) in allen vier betroffenen Stellen binden
ein Additional Authenticated Data an Chiffretext/DEK-Wrapper: ein
Austausch zwischen zwei Kontexten faellt beim Entschluesseln auf,
statt still zu funktionieren.
- dms/internal/crypto/envelope.go + service.go: AAD = Tenant-Slug
(bereits vorhandener Parameter in Service.Seal/Open, Signatur
unveraendert -- keine der rund 30 externen Aufrufstellen musste
angepasst werden)
- archive/internal/backupcrypto/envelope.go + reposecret.go/file.go:
gleiches Muster, AAD = Tenant-Slug
- mail/internal/crypto/envelope.go + service.go + kekversions.go:
gleiches Muster, AAD = Tenant-Slug (inkl. ARC-08
KEK-Versionsverwaltung)
- internal/kek/master.go + store.go: wrap/unwrap sowie
EncryptWithTenantKEK/DecryptWithTenantKEK binden AAD. Die drei
echten externen Aufrufer angepasst: ldapsync (Tenant-ID + Zweck),
webhook (Ziel-URL des Abonnements), account-api modulekeys (fester
Zweck-Bezeichner -- SecretCipher-Interface aus internal/moduletrust
reicht keine Datensatz-ID durch, eine Erweiterung wuerde in andere
Module hineinwirken, bewusst nicht Teil dieser Kachel)
Rueckwaertskompatibel: jedes open/unwrap versucht zuerst mit AAD,
faellt bei Fehlschlag auf kein-AAD zurueck -- vor dieser Aenderung
versiegelte Datensaetze bleiben lesbar.
Bekannte, dokumentierte Grenze: ein Austausch zwischen zwei
Datensaetzen DESSELBEN Mandanten/Kontexts bleibt unerkannt (identisches
AAD) -- volle Bindung an den einzelnen Datensatz braeuchte eine
Datensatz-ID an rund 30 Aufrufstellen, bewusst nicht Teil dieser
kleinstmoeglichen Loesung.
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>
Backend (kleinstmoeglich, wie im Ticket vorgesehen):
- internal/auth: TokenIssuer.IssueWithTTL, LoginService.LoginWithTTL
(additiv, bestehende Issue/Login unveraendert)
- internal/totp: LoginWithTOTPTTL (durchlaeuft denselben 2FA-Ablauf)
- cmd/account-api: neue Route POST /auth/mobile-login (JSON-Token statt
Cookie, 90 Tage TTL statt 30 Minuten)
Android-App (android/dms-capture, Kotlin, Gradle-Projekt):
- CameraX-Erfassung mit Live-Vorschau, erneuter Aufnahme, Mehrseiten-
Sammlung (AC2)
- Ruft ausschliesslich die bestehende DOC-01-Upload-API auf, kein neuer
Backend-Uploadpfad (AC3) -- echter Architektur-Fund dabei: dms-app
erwartet ein audience-gebundenes Modul-Token (aud=dms), kein rohes
Sitzungstoken; App tauscht ueber den bereits bestehenden Endpunkt
POST /auth/module-token (Cookie-Header), dokumentiert im Pruefprotokoll
- Room-basierte Offline-Warteschlange mit sichtbarem Status, WorkManager
fuer automatische Nachreichung ohne Nutzeraktion (AC4)
- Verstaendliche Fehlermeldungen bei Groessen-/Typ-Ablehnung statt nur
Fehlercode (AC5)
- Internes APK, kein Play-Store-Zwang (AC6)
Real getestet: Ende-zu-Ende-Auth-Kette (Login -> Modul-Token -> Bearer
gegen echten dms-app) auf Host 204, 13/13 Kotlin-Unit-Tests gruen, echter
APK-Build auf Host 236 (Android-SDK dort vorhanden, kein Install auf der
Entwicklungsmaschine, Gradle projektlokal nachgezogen statt System-Install).
Offen (Pruefprotokoll): Kamera-/Offline-/Smoke-Install-Pruefungen
brauchen einen Android-Emulator; kein System-Image auf Host 236
installiert, Nachinstallation bewusst nicht ohne Rueckfrage vorgenommen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
DMS' und Archives internal/tenantregistryclient (INT-14) riefen bereits
GET /tenants/{slug} mit X-Service-Token gegen Core auf, aber Core
registrierte diese Route nirgends -- jeder Aufruf schlug mit 404 fehl.
Beim Rollout-131-Versuch entdeckt.
Neuer Handler tenant.ConfirmHandler nutzt die bestehende Registry.GetBySlug,
dieselbe svcauth.Require-Absicherung wie TEN-12s Import-Verzeichnis-Routen
(kein neues Auth-Schema). 200 mit Slug/Status bei bekanntem Mandanten, 404
bei unbekanntem, 500 nur bei echtem DB-Fehler -- der bestehende Client
wertet ausschliesslich den Statuscode aus.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signaturschluessel der Modul-JWTs liegen verschluesselt in der Mandanten-Datenbank
(module_signing_keys), werden von mehreren Instanzen geteilt und rotieren mit
Ueberlappung; nicht lesbarer Speicher ergibt 503 statt Token/JWKS.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
POST /authorize nimmt optional den Mandanten entgegen und wertet modulgebundene Rechte ueber AuthorizeForTenant aus (fail closed). Mandant ist Pflicht fuer Rechte mit Modul-Bindung, das Service-Token laesst sich auf Mandanten einschraenken. Migration 0012 bindet die dms.*-Rechte an das Flag dms. Der DMS-policyclient sendet den Mandanten mit und trennt den Cache je Mandant.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Core exponierte bislang nur GET /audit/export (AUD-03) - physisch
getrennte Module (Archive, DMS, Mail) hatten keinen Weg, eigene
sicherheitsrelevante Vorgaenge im zentralen Audit-Log zu vermerken.
IngestHandler (POST /audit/ingest, X-Service-Token) ruft denselben
alleinigen Schreibpfad wie core-interne Aufrufer auf (audit.Log.Record) -
kein zweiter, laxerer Validierungspfad ueber REST.
Notwendige Grundlage fuer Archive CMP-04 (Audit-Trail-Kopplung an
Core-Audit-Log), das per REST an dieses Modul anbindet.
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