diff --git a/docs/anleitung/bilder/assistent-fertig-desktop.png b/docs/anleitung/bilder/assistent-fertig-desktop.png
new file mode 100644
index 0000000..8b12fdf
Binary files /dev/null and b/docs/anleitung/bilder/assistent-fertig-desktop.png differ
diff --git a/docs/anleitung/bilder/assistent-ziel-desktop.png b/docs/anleitung/bilder/assistent-ziel-desktop.png
new file mode 100644
index 0000000..2690a7b
Binary files /dev/null and b/docs/anleitung/bilder/assistent-ziel-desktop.png differ
diff --git a/prisma/migrations/20261012110000_zielkonto_vorgaenge/migration.sql b/prisma/migrations/20261012110000_zielkonto_vorgaenge/migration.sql
new file mode 100644
index 0000000..8baa5b7
--- /dev/null
+++ b/prisma/migrations/20261012110000_zielkonto_vorgaenge/migration.sql
@@ -0,0 +1,10 @@
+-- AlterEnum
+-- This migration adds more than one value to an enum.
+-- With PostgreSQL versions 11 and earlier, this is not possible
+-- in a single migration. This can be worked around by creating
+-- multiple migrations, each migration adding only one value to
+-- the enum.
+
+
+ALTER TYPE "VorgangsArt" ADD VALUE 'ZIELKONTO_ANLEGEN';
+ALTER TYPE "VorgangsArt" ADD VALUE 'ZIELKONTO_ENTFERNEN';
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 6b67b8c..38d6161 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -110,6 +110,10 @@ enum VorgangsArt {
SCHLUESSEL_IMPORTIEREN
/// Gespeicherten Private Key endgueltig verwerfen (#014).
PRIVATE_KEY_LOESCHEN
+ /// Zielbenutzer anlegen und sudoers setzen (#018).
+ ZIELKONTO_ANLEGEN
+ /// sudoers und – falls von sshkv angelegt – den Benutzer entfernen (#018, #024).
+ ZIELKONTO_ENTFERNEN
}
enum VorgangsStatus {
diff --git a/src/app/(app)/assistent/bestaetigt/page.tsx b/src/app/(app)/assistent/bestaetigt/page.tsx
new file mode 100644
index 0000000..b49a2a9
--- /dev/null
+++ b/src/app/(app)/assistent/bestaetigt/page.tsx
@@ -0,0 +1,16 @@
+import type { Metadata } from "next";
+import { Leerzustand } from "@/components/ui/Leerzustand";
+import { sitzungErforderlich } from "@/server/auth/sitzungspflicht";
+
+export const metadata: Metadata = { title: "Bestätigt" };
+
+/** Ziel der Bestaetigung aus dem Assistenten (#020): der Assistent wartet im anderen Tab. */
+export default async function BestaetigtSeite() {
+ await sitzungErforderlich();
+ return (
+
+ );
+}
diff --git a/src/app/(app)/assistent/page.tsx b/src/app/(app)/assistent/page.tsx
new file mode 100644
index 0000000..851516e
--- /dev/null
+++ b/src/app/(app)/assistent/page.tsx
@@ -0,0 +1,27 @@
+import type { Metadata } from "next";
+import { Assistent } from "@/components/assistent/Assistent";
+import { Breadcrumb } from "@/components/ui/Breadcrumb";
+import { Seitenkopf } from "@/components/ui/Seitenkopf";
+import { assistentDaten } from "@/server/queries/assistent";
+import { bestaetigungFrisch, sitzungErforderlich } from "@/server/auth/sitzungspflicht";
+
+export const metadata: Metadata = { title: "Host-zu-Host-Assistent" };
+
+/**
+ * Host-zu-Host-Assistent (#020): ein Schluesselpaar fuer genau einen Zweck, Private Key auf
+ * Host A, Public Key auf Host B, Verbindungstest. Nur Admins – es wird ein Private Key ausgerollt.
+ */
+export default async function AssistentSeite() {
+ const { benutzer } = await sitzungErforderlich("privateKeyAusrollen", "/assistent");
+ const daten = await assistentDaten();
+ return (
+
+ }
+ titel="Host-zu-Host-Assistent"
+ untertitel="Host A soll sich per SSH an Host B anmelden – Schlüssel, Ausrollen und Test in einem Durchgang."
+ />
+
+