001 ConMeet-Instanzen, Berechtigungen und Kontoverknüpfung Entwurf

Definiert, wie der Administrator des Betriebs ConMeet-Instanzen verbindet und Mitarbeitende dafür berechtigt und wie jede Person ihren Suite-Benutzer per persönlichem Schnittstellenschlüssel mit ihrem ConMeet-Benutzer verknüpft. Stand 2026-10-30

Ausgangslage

Umgesetzt ist nur die Definition der Vertikale ConMeet.Core/Features/ConMeetInstances/: vertical.json, die drei Entitäten ohne Mapping und die Exceptions. Der Aufwand liegt in zwei Querschnitts-Vorleistungen, die es noch nicht gibt (Berechtigungen aus Produktrollen, Erweiterungspunkt der Replik), in der Verschlüsselung und in der Vertikale selbst samt Oberfläche. Die Vertikale wird mit validdata-vertical-generator in dessen Stufen gebaut (S4, S6–S9); Querschnitt nach validdata-backend-patterns.

Grundlage ist der Arbeitsstand vom 2026-10-29 mit dem uncommitteten Umzug nach Crosscutting/. R90–R100 (Prüfkonto) sind nach der letzten Prüfung entstanden und nicht erneut geprüft.

Pfadkürzel: srv/ = source/conmeet-server/, web/ = source/conmeet-webapp/, CI/ = srv/ConMeet.Core/Features/ConMeetInstances/.

Bestand je Anforderung

Anforderung Stand Fundstelle Schritt
R1 fehlt — S4, S6
R2 fehlt — S4, S6
R3 fehlt — S6
R4 fehlt — S4
R5 teilweise srv/ConMeet.Core/Crosscutting/Persistence/Interceptors/PersistenceInterceptors.cs:62 — Stempel des Mandanten; Benutzerprüfung fehlt S7, S8
R7 fehlt srv/ConMeet.Core/Crosscutting/Licensing/ProductRoles.cs:7 — Katalog leer S1
R8 fehlt srv/ConMeet.Service/Features/AuthenticationFeature.cs:105 — nur Standard-Policy S1, S9
R9 fehlt srv/ConMeet.Core/Crosscutting/Users/Api/UserQueries.cs:23 — Me ohne permissions S1, S10
R11 fehlt — S6
R13 fehlt — S6
R14 fehlt — S6, S10
R15 fehlt — S6
R18 fehlt — S10
R19 fehlt — S10
R20 erfüllt (Regel) AGENTS.md (Persistenz, Ausnahme für lesende Aufrufe) S5
R22 fehlt — S7
R23 fehlt — S7
R24 fehlt — S7, S10
R25 fehlt — S7, S10
R26 fehlt — S7
R27 fehlt — S7
R29 fehlt — S8, S11
R30 fehlt — S8
R37 fehlt — S8, S11
R38 fehlt — S8, S9
R39 fehlt — S8
R40 fehlt keine AES-GCM-Hilfe im Backend; nur web/src/lib/server/auth.ts:39 S2, S8
R41 fehlt — S9
R42 fehlt srv/ConMeet.Service/Features/PersistenceFeature.cs:21 — Blacklist kennt nur OutboxMessage S4, S5
R43 fehlt — S10, S11
R44 fehlt CI/Exceptions/ConMeetUserAlreadyLinkedException.cs S8
R46 teilweise srv/ConMeet.Core/Crosscutting/Persistence/TenantDbContext.cs:36 — Mandantenfilter S9
R47 fehlt web/src/lib/components/ui/sheet/ vorhanden, Bestätigungsdialog fehlt S10, S11
R48 fehlt — S10
R49 fehlt — S8, S9
R50 fehlt — S8
R51 fehlt — S7, S10
R52 fehlt — S5
R53 fehlt — S6
R54 fehlt — S8
R57 fehlt — S8
R60 fehlt — S11
R61 fehlt — S8, S11
R62 fehlt — S11
R63 fehlt — S5
R64 fehlt — S5, S10, S11
R65 fehlt — S8
R66 fehlt Codes nur in den Doc-Kommentaren von CI/Exceptions/ S9
R67 fehlt — S6, S9
R68 fehlt — S4, S8
R69 fehlt srv/ConMeet.Core/Crosscutting/Suite/Replication/SuiteReplica.cs:93 — Umzug läuft über RemoveUserAsync S3, S7
R70 fehlt — S7
R71 fehlt — S11
R72 fehlt — S2
R73 fehlt srv/ConMeet.Core/Crosscutting/Persistence/EntityModelConfiguration.cs:14 — Zeilenversion xmin vorhanden S6, S9
R74 teilweise CI/Entity/ConMeetInstance.cs, CI/Entity/AccountLink.cs — Konstanten ohne Mapping und Prüfung S4, S6
R75 fehlt — S6
R76 fehlt — S10, S11
R77 fehlt — S10
R78 fehlt web/src/lib/components/layout/app-sidebar.svelte:39 — nur „Übersicht“ S10, S11
R79 fehlt — S8
R80 fehlt — S8
R81 teilweise CI/Entity/AccountLink.cs — Konstante ohne Prüfung S4, S8
R82 fehlt CI/Exceptions/InvalidInputException.cs S6, S8, S9
R83 fehlt — S9, S10, S11
R86 fehlt SuiteReplica.cs:138 — RemoveUserAsync löscht nur Lizenzen und Benutzer S3, S7
R87 fehlt kein Erweiterungspunkt in srv/ConMeet.Core/Crosscutting/Suite/ S3, S7
R88 fehlt — S2
R89 fehlt — S8, S9
R90 fehlt CI/Entity/AccountLink.cs — IsAdmin ohne Index S4, S6
R91 fehlt — S6
R92 fehlt — S6
R93 fehlt — S6
R94 fehlt — S6
R95 fehlt — S5, S6, S8
R96 fehlt — S8
R97 fehlt CI/Exceptions/AdminLinkRequiredException.cs S8
R98 fehlt — S6
R99 fehlt — S6
R100 fehlt — S9, S10

Schritte

S1 — Berechtigungen aus Produktrollen

Erfüllt: R7, R8 (Policy), R9 (Backend) Abhängig von: — · parallel zu S2, S3, S4 Dateien: srv/ConMeet.Core/Crosscutting/Licensing/ProductRoles.cs, srv/ConMeet.Core/Crosscutting/Users/CurrentAccess.cs, srv/ConMeet.Core/Crosscutting/Users/Api/UserQueries.cs, srv/ConMeet.Service/Features/AuthenticationFeature.cs, srv/ConMeet.CoreSimulator/core-simulator.seed.json, web/src/lib/features/users/me.graphql, Tests unter srv/ConMeet.Core.Tests/Crosscutting/Suite/AuthorizationPolicyTests.cs und .../Users/MeQueryTests.cs Zu tun:

  1. Katalog um conmeet-admin → ManageInstances ergänzen; Berechtigungsnamen als Konstanten neben dem Katalog.
  2. CurrentAccess leitet die Berechtigungen aus User.Roles über den Katalog ab.
  3. Je Berechtigung eine Policy in ConfigureAuthorizationOptions (AuthenticationFeature.cs:89), die READY und die Berechtigung verlangt; kein Schema in der Policy (docs/backend-development.md).
  4. Me um permissions erweitern; me.graphql und generierten Code im BFF nachziehen.
  5. Seed: Rolle conmeet-admin im Produkt conmeet-suite anlegen und Anna zuweisen (core-simulator.seed.json:16, :70).
  6. Integrationsplan: Prüfzeile „Zugriff: Rollenkatalog“ und Hinweis zu WP5 auf den neuen Stand bringen. Fertig, wenn: Tests zeigen: mit Rolle permissions = [ManageInstances] und Policy erfüllt; ohne Rolle leer und Policy verweigert; fremde Rolle gewährt nichts. Anna sieht lokal ManageInstances in me.

S2 — Verschlüsselung des persönlichen Schlüssels in der Vertikale

Erfüllt: R40 (Mechanismus), R72, R88 Abhängig von: — · parallel zu S1, S3, S4; registriert wird sie in S6 Dateien: CI/ (eine Klasse zum Ver- und Entschlüsseln, ohne eigene Abstraktion), srv/ConMeet.AppHost/AppHost.cs, docs/vds-suite/deployment-handover.md, Tests unter srv/ConMeet.Core.Tests/Features/ConMeetInstances/ Zu tun:

  1. Verschlüsseln und Entschlüsseln direkt in der Vertikale mit AES-256-GCM, Associated Data aus Mandant, Zweck conmeet-personal-key und Zeilen-ID; Aufbau Nonce | Tag | Daten wie web/src/lib/server/auth.ts:47. Kein eigener Querschnitt, kein Interface.
  2. Schlüssel aus Secrets:EncryptionKey mit Prüfung beim Start (64 Hex-Zeichen), Muster SuiteRegistry.cs:35 und SuiteOptions.cs:49.
  3. AppHost setzt lokal einen Schlüssel aus einem Parameter (User Secrets), nichts eingecheckt.
  4. Übergabe: Pflichtwert Secrets:EncryptionKey mit Format eintragen. Fertig, wenn: Tests: Rundlauf gelingt; anderer Mandant, Zweck oder Zeilen-ID lässt das Entschlüsseln scheitern; fehlender oder falsch formatierter Schlüssel verhindert den Start.

S3 — Erweiterungspunkt der Replik für Benutzerdaten

Erfüllt: R86, R69, R87 (Querschnitt) Abhängig von: — · parallel zu S1, S2, S4 Dateien: srv/ConMeet.Core/Crosscutting/Suite/Replication/SuiteReplica.cs, Registrierung in srv/ConMeet.Core/Crosscutting/Suite/SuiteRegistry.cs, Tests in srv/ConMeet.Core.Tests/Crosscutting/Suite/SuiteReplicationTests.cs Zu tun:

  1. Erweiterungspunkt in Crosscutting/Suite/ anlegen, den Vertikalen registrieren; Aufruf in RemoveUserAsync (SuiteReplica.cs:138) vor dem Löschen des Benutzers, im selben Kontext und derselben Transaktion.
  2. RemoveUserAsync deckt Entfernen (SuiteMessageHandlers.cs:104), Ersetzen/Umzug (SuiteReplica.cs:93, :95) und Backfill (SuiteBackfill.cs:66, :110) ab; keinen zweiten Aufrufpfad anlegen.
  3. backend-development.md, Abschnitt Replik, um den Erweiterungspunkt ergänzen. Fertig, wenn: Ein im Test registrierter Teilnehmer wird bei UserDeleted, Backfill-Entfernung und Umzug je einmal aufgerufen; scheitert er, wird die Nachricht wiederholt und nichts ist gelöscht. LayerDirectionTests grün.

S4 — Persistenz der Vertikale (Generator-Stufe 2)

Erfüllt: R1, R2, R4, R68, R74, R81, R90 (Datenbank), R42 (Audit) Abhängig von: — · parallel zu S1–S3 Dateien: CI/Entity/*ModelConfiguration.cs (neu), srv/ConMeet.Core/Crosscutting/Persistence/ConMeetContext.cs:20, srv/ConMeet.Service/Features/PersistenceFeature.cs:21, neue Migration in srv/ConMeet.Service/Persistence/Migrations/, srv/ConMeet.Core.Tests/Crosscutting/Persistence/EntityModelTests.cs:21, Persistenztests unter srv/ConMeet.Core.Tests/Features/ConMeetInstances/ Zu tun:

  1. Je Entität EntityModelConfiguration<T> mit IsTenantOwned(); Längen aus den Constraints.
  2. Eindeutige Indizes, jeweils mit TenantId vorn: Name ohne Groß-/Kleinschreibung (R1), normalisierte BaseUrl (R2, gespeichert in normalisierter Form), (ConMeetInstanceId, UserId) für Grant und Link (R4), (ConMeetInstanceId, lower(ConMeetUserName)) (R68), partiell (ConMeetInstanceId) WHERE is_admin (R90).
  3. Fremdschlüssel der Kinder auf ConMeetInstance; Löschen erfolgt ausdrücklich (R15), nicht über Kaskade.
  4. DbSet-Zeilen, Migration ConMeetInstances (Entwickler vorher informieren), Audit-Blacklist für AccountLink.PersonalKey.
  5. Neue Entitäten in EntityModelTests klassifizieren. Fertig, wenn: Persistenztests gegen PostgreSQL: jeder Index weist das Doppel ab (auch Live/live, https://X/ gegen https://x), zweite admin-Verknüpfung scheitert, fremder Mandant sieht keine Zeile; EntityModelTests grün; die Audit-Zeile nennt PersonalKey nicht.

S5 — Client für die ConMeet Public API

Erfüllt: R20, R52, R63, R64 (Backend), R95 (Aufruf), R42 (Logs) Abhängig von: — · parallel zu S1–S4 Dateien: CI/ (Client-Klasse, Ablage nach references/structure.md des Generators), Tests unter srv/ConMeet.Core.Tests/Features/ConMeetInstances/ Zu tun:

  1. Typisierter Client nach dem Muster BootstrapClient.cs:30: GET {BaseUrl}/api/public/v1/customers?limit=1 mit Authorization: Bearer, ohne Cookies.
  2. Ergebnis mit Zustand nach R63, Statuscode, Antworttext (nicht bei CONNECTED, sonst auf 2000 Zeichen gekürzt) oder Fehlerart ohne Antwort.
  3. Zeitlimit 10 Sekunden je Aufruf und keine Wiederholungen: Standard-Resilience aus srv/ConMeet.ServiceDefaults/Extensions.cs:23 für diesen Client ersetzen.
  4. Header Authorization in Logs und Traces schwärzen. Fertig, wenn: Tests mit einem gestellten HTTP-Handler ergeben je Antwort (200, 401, 403, 402, 429, 500, 404, Zeitüberschreitung, TLS-Fehler) den Zustand aus R63; der Text bei 200 fehlt, bei 404 ist er gekürzt; kein Log enthält den Schlüssel.

S6 — Registry, Service: Instanzen verbinden, ändern, trennen, Prüfkonto (Generator-Stufe 3)

Erfüllt: R1, R2, R3, R11, R13, R14 (Zählung), R15, R53, R67 (Ergebnis), R73, R74, R75, R82 (Instanz), R90, R91, R92, R93, R94, R95, R98, R99 Abhängig von: S2, S4, S5 Dateien: CI/ConMeetInstancesRegistry.cs, CI/ConMeetInstancesService.cs, neu srv/ConMeet.Service/Features/VerticalFeatures.cs (analog CrosscuttingFeature.cs:11, ruft AddConMeetInstancesFeature()), srv/ConMeet.Service/Program.cs:10 (nach dem Querschnitt), Tests unter srv/ConMeet.Core.Tests/Features/ConMeetInstances/ Zu tun:

  1. Registry mit Schalter Features:ConMeetInstances:Enabled und Abhängigkeitsprüfung wie UsersRegistry.cs:15; registriert Service, Client und Verschlüsselung aus S2.
  2. Verbinden: Pflichtfelder und Längen (InvalidInputException), https außer in Development, Normalisierung der Adresse samt Pfad und Port, Eindeutigkeit von Name und Adresse; Prüfkonto mit verschlüsseltem Schlüssel, IsAdmin und InstanceGrant des Verbindenden in einer Transaktion; Eindeutigkeit des Benutzernamens vor der Prüfung; Prüfung nach dem Speichern, Ergebnis zurückgeben, Fehlschlag ist kein Fehler.
  3. Ändern: Name, Kommentar, Adresse; Berechtigungen und Verknüpfungen bleiben; Konflikt der Zeilenversion → InstanceChangedException; danach prüfen.
  4. Trennen: Zahl aller Verknüpfungen für den Dialog liefern; Instanz, Grants, Links mit ExecuteDelete ohne Soft-Delete-Filter und je Zeile eine Audit-Zeile, in einer Transaktion (Muster SuiteReplica.cs:222).
  5. Prüfung der Instanz mit dem Schlüssel des Prüfkontos, auch ruhend; ohne Prüfkonto NO_ADMIN_LINK.
  6. Übernahme: eigene, nicht ruhende Verknüpfung erhält IsAdmin, die bisherige verliert es, in einer Transaktion; ohne eigene AccountNotLinkedException. Fertig, wenn: Servicetests gegen PostgreSQL decken Beispiel-Schritte 1, 2 und 8 der Spezifikation ab, dazu: http außerhalb Development abgewiesen, Adresse mit anderem Pfad erlaubt, gleichzeitiges Ändern → INSTANCE_CHANGED, Übernahme verschiebt IsAdmin, ohne Prüfkonto NO_ADMIN_LINK. Abgeschaltete Vertikale registriert nichts.

S7 — Berechtigen und Aufräumen beim Entfernen eines Benutzers

Erfüllt: R5 (Grant), R22, R23, R24, R25 (Speicherung), R26, R27, R51 (Daten), R69, R70, R86, R87 (Vertikale) Abhängig von: S3, S6 Dateien: CI/ConMeetInstancesService.cs, CI/ConMeetInstancesRegistry.cs, Tests unter srv/ConMeet.Core.Tests/Features/ConMeetInstances/ Zu tun:

  1. „Alle“ setzen und entfernen; Einzelberechtigung nur für Benutzer des Mandanten mit gültiger Lizenz für conmeet-base (Lizenzlogik aus UserAccessResolver.EntitledFeatureCodesAsync); Entzug löscht das Grant physisch.
  2. Berechtigungsprüfung als eine Stelle im Service: GrantedToAll oder Grant; ruhende Verknüpfung = Verknüpfung ohne Berechtigung, nichts wird gelöscht.
  3. Personenliste: lizenzierte Benutzer (bei „Alle“ alle lizenzierten des Mandanten) mit Verknüpfungsstand, ruhende ausgeblendet, Prüfkonto gekennzeichnet.
  4. Teilnehmer am Erweiterungspunkt aus S3 registrieren: löscht Grants und Links des Benutzers physisch mit Audit-Zeilen; ein entfallendes Prüfkonto hinterlässt die Instanz ohne IsAdmin. Fertig, wenn: Tests decken Beispiel-Schritte 3 und 7 ab, dazu: unlizenzierter Benutzer nicht berechtigbar, „Alle“ blendet Einzelberechtigungen aus und stellt sie beim Entfernen wieder her, UserDeleted des Prüfkonto-Inhabers → Instanz liefert NO_ADMIN_LINK.

S8 — Konto verknüpfen, ersetzen, lösen

Erfüllt: R5 (Link), R29 (Daten), R30, R37, R38, R39, R40 (Anwendung), R44, R49, R50, R54, R57, R61, R65, R68, R79, R80, R81, R82 (Link), R89, R95 (Link), R96, R97 Abhängig von: S7 Dateien: CI/ConMeetInstancesService.cs, Tests unter srv/ConMeet.Core.Tests/Features/ConMeetInstances/ Zu tun:

  1. Alle Operationen wirken nur auf den Link des angemeldeten Benutzers; keine Benutzer-ID als Eingabe.
  2. Verknüpfen und Ersetzen: Berechtigung prüfen, Benutzername trimmen und Länge prüfen, Eindeutigkeit ohne Groß-/Kleinschreibung über alle Links der Instanz, dann Prüfung mit dem eingegebenen Schlüssel; nur bei CONNECTED verschlüsselt speichern, sonst ConMeetCheckFailedException mit Statuscode und Antworttext.
  3. Lösen: ohne Berechtigung InstanceNotGrantedException; Prüfkonto → AdminLinkRequiredException; ohne Link ohne Wirkung; sonst physisch löschen.
  4. Eigene Prüfung: ohne Berechtigung InstanceNotGrantedException, ohne Link AccountNotLinkedException.
  5. Liste „meine Instanzen“: berechtigte Instanzen mit eigenem Link (Benutzername, IsAdmin).
  6. Kein Code greift im Namen einer Person auf einen anderen Schlüssel als ihren eigenen zurück. Fertig, wenn: Tests decken Beispiel-Schritte 4–6 ab, dazu: Clara.Schmidt gegen clara.schmidt abgewiesen ohne Aufruf an ConMeet, ruhender Name bleibt belegt, KEY_REJECTED speichert nichts, Prüfkonto nicht lösbar, fremde Instanz → INSTANCE_NOT_GRANTED.

S9 — GraphQL-Port (Generator-Stufe 4)

Erfüllt: R8, R38, R41, R46, R49, R66, R67, R73 (Code), R82 (Code), R83 (Codes), R89, R100 (API) Abhängig von: S1, S8 Dateien: CI/Api/ (Queries, Mutations, Typen, Fehlerfilter), CI/ConMeetInstancesRegistry.cs, Schema-Snapshot-Test unter srv/ConMeet.Core.Tests/Features/ConMeetInstances/, Bruno-Collection api/ (neu, nach references/bruno.md des Generators), web/schema.graphql Zu tun:

  1. Operationen aus Abschnitt 7 der Spezifikation als [ExtendObjectType]-Erweiterungen (Muster UserQueries.cs:8, QuickAccessMutations.cs:9); Verwaltung unter der Policy ManageInstances, persönliche unter der Standard-Policy.
  2. Typen ohne Schlüsselfelder; Instanz mit Inhaber und Benutzername des Prüfkontos; Basis über GraphQLHelpers.cs:10.
  3. Fehlerfilter der Vertikale, registriert in ihrer Registry: jede Exception der Vertikale auf ihren Code in extensions.code (so liest der BFF Codes, web/src/lib/server/graphql/client.ts:32), CONMEET_CHECK_FAILED mit Statuscode und Antworttext, INVALID_INPUT mit Feldname; gilt für Queries und Mutations. Keine [Error<T>]-Typen der Mutation-Konventionen.
  4. Schema exportieren (pnpm codegen:schema), ersten Schema-Snapshot-Test des Projekts anlegen, erste Bruno-Collection unter api/ mit Anfragen je Operation. Fertig, wenn: AuthorizationPolicyTests.cs:47 grün mit den neuen Feldern; HTTP-Tests: Mitarbeitende erhalten bei Verwaltungsoperationen „nicht autorisiert“, Instanz eines anderen Mandanten → INSTANCE_NOT_FOUND, Schema enthält kein Schlüsselfeld; Snapshot steht.

S10 — Oberfläche: Verwaltung der Instanzen

Erfüllt: R9 (UI), R14 (Dialog), R18, R19, R24 (Auswahl), R25 (Anzeige), R43, R47, R48, R51 (Anzeige), R64 (Anzeige), R76, R77, R78, R83 (Anzeige), R100 Abhängig von: S9 · parallel zu S11 Dateien: web/src/routes/(authenticated)/admin/conmeet-instances/ (neu, Liste und [id]), Remote Functions und .graphql-Dokumente unter web/src/lib/features/conmeet-instances/ (neu), web/src/lib/components/layout/app-sidebar.svelte:39, fehlende shadcn-Komponenten (Bestätigungsdialog, Tabelle, Label, Textarea, Schalter) per CLI, Tests nach web/src/lib/features/*/*.spec.ts und web/src/routes/access.e2e.ts Zu tun:

  1. Navigationseintrag „ConMeet-Instanzen“ nur bei ManageInstances aus me.permissions.
  2. Liste sofort anzeigen, je Zeile „Wird geprüft …“, höchstens fünf Prüfungen gleichzeitig, „Erneut prüfen“; Status mit Code und aufklappbarem Text; Empty State mit „Instanz verbinden“.
  3. Sheet zum Verbinden (inkl. Benutzername und Schlüssel des Prüfkontos, Feld password ohne Autovervollständigung) und Bearbeiten; Meldungen je Code am Feld oder als Hinweis.
  4. Detailseite: Freigabe „Alle“, durchsuchbare Auswahl lizenzierter Personen (bei „Alle“ ausgeblendet), Personenliste mit Verknüpfungsstand und „Prüfkonto“, „Prüfkonto übernehmen“, „Trennen“ im Bestätigungsdialog mit Zahl der Verknüpfungen.
  5. Muster nach validdata-frontend-patterns, Gestaltung nach validdata-styleguide. Fertig, wenn: Als Anna lokal: Instanz verbinden, Status erscheint ohne Klick, Trennen nennt die Zahl; als Tom erscheint der Eintrag nicht und die Adresse liefert keine Daten. Unit-Tests für die Begrenzung auf fünf Prüfungen.

S11 — Oberfläche: Meine ConMeet-Verbindungen

Erfüllt: R29, R37 (Dialog), R43, R47, R60, R61 (UI), R62, R64 (Anzeige), R71, R76, R78, R83 (Anzeige) Abhängig von: S9 · parallel zu S10 Dateien: web/src/routes/(authenticated)/conmeet-connections/ (neu), web/src/lib/features/conmeet-instances/, web/src/lib/components/layout/app-sidebar.svelte:39, Tests wie S10 Zu tun:

  1. Navigationseintrag „Meine ConMeet-Verbindungen“ für jede Person mit READY.
  2. Liste berechtigter Instanzen mit Name, Kommentar und Status nach R71; Text bei leerer Liste nach R29.
  3. Prüfung verknüpfter Instanzen beim Öffnen, höchstens fünf gleichzeitig, Status mit Code und Text.
  4. Sheet „Verknüpfen“/„Schlüssel ersetzen“ mit Benutzername, Schlüssel (password) und Anleitung; „Verknüpfung lösen“ im Bestätigungsdialog; ADMIN_LINK_REQUIRED als Meldung. Fertig, wenn: Lokal mit zwei Personen sind Beispiel-Schritte 4–6 gegen die ConMeet-Testumgebung (https://dev.conmeet.io) durchspielbar; ein e2e-Test deckt Verknüpfen mit abgelehntem Schlüssel gegen die Testumgebung ab. Keine gestellte Gegenstelle.

Reihenfolge

S1 ─────────────────────────────┐
S2 ──┐                          │
S4 ──┼──► S6 ──► S7 ──► S8 ──► S9 ──► S10
S5 ──┘           ▲                └──► S11
S3 ──────────────┘

S1–S5 sind voneinander unabhängig und können parallel laufen. S10 und S11 sind nach S9 unabhängig.

Entschieden

Entscheidungen von Martin Linkwitz am 2026-10-29, Grundlage der Schritte:

  • Verschlüsselung: direkt in der Vertikale, ohne eigenen Querschnitt (S2).
  • Registrierung fachlicher Vertikalen: srv/ConMeet.Service/Features/VerticalFeatures.cs (S6).
  • Fehlercodes: Fehlerfilter der Vertikale setzt extensions.code, für Queries und Mutations (S9).
  • Tests gegen ConMeet: gegen die vorhandene Testumgebung, keine gestellte Gegenstelle (S11).
  • Format der vertical.json: eine Entität, Beziehungen der Kinder als InstanceGrant.UserId und AccountLink.UserId.
  • Bruno und Schema-Snapshot: S9 legt die erste Collection und den ersten Snapshot an.
  • Mandantenwechsel ohne Neuanlage: SuiteReplica.cs:74 verweigert ihn und protokolliert nur; R69 greift dann nicht. Bleibt so, 001 verlangt es nicht.

Offene Entscheidungen

  • Offen in der Spezifikation: Rollencode conmeet-admin noch nicht von Core bestätigt (R7); Wortlaut der Anleitung zum Schlüssel (R62) — S11 setzt einen Platzhaltertext.

Nicht im Plan

  • Fachliche Aufrufe an ConMeet im Namen einer Person (Anrufinformation) — R30 legt nur fest, welcher Schlüssel gilt; eigene Spezifikationen.
  • Contracts für andere Vertikalen (vertical.json: keine) — folgen mit der Anrufinformation.
  • Schlüsselwechsel für Secrets:EncryptionKey — nach R72 nicht vorgesehen.
  • Umbau des Mandantenwechsels in SuiteReplica — siehe Entschieden.