diff --git a/CHANGELOG.md b/CHANGELOG.md
index 96aaa8e..87c71f1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,14 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
### Sicherheit
+## [0.24.0] – 2026-10-03
+
+### Hinzugefügt
+- Dreizehnte Warnregel: Mehrere Hosts desselben Kunden tragen denselben Namen – mit IP-Adressen zum Unterscheiden (#041)
+- Hosts lassen sich **stilllegen**: Sie verschwinden aus Listen, Zahlen, Suche und Warnungen, ihre Laufzeiten und Rechnungen bleiben erhalten (#041)
+- Endgültiges Löschen für Hosts, an denen keine Laufzeit hängt (#041)
+- Schalter „stillgelegte Hosts zeigen“ in der Host-Liste (#041)
+
## [0.23.0] – 2026-10-03
### Hinzugefügt
diff --git a/DEVLOG.md b/DEVLOG.md
index 8a49fae..74592b6 100644
--- a/DEVLOG.md
+++ b/DEVLOG.md
@@ -2,7 +2,7 @@
## Zeitübersicht
-**Gesamt:** 21 h 43 min
+**Gesamt:** 22 h 33 min
| Issue | Zeit |
|---|---|
@@ -46,6 +46,7 @@
| #038 Überbreite auf dem Handy | 0 h 40 min |
| #039 Zugang zur Laufzeit | 0 h 30 min |
| #040 Schlagworte sichtbar | 0 h 40 min |
+| #041 Doppelte Hosts | 0 h 50 min |
| Meilenstein | Zeit |
|---|---|
@@ -68,7 +69,7 @@
## 2026-09-17 · Sitzung 1 · 19:36–laufend
**Issue:** #001–#007 (Meilensteine v0.1.0 bis v0.4.0)
-**Version:** 0.23.0
+**Version:** 0.24.0
### Erledigt
- Auftrag gelesen, Rückfragen gestellt
@@ -208,6 +209,9 @@
- Schlagworte und Notizen sichtbar gemacht (#040). Beides gab es längst – nur eben versteckt in einem Reiter, und damit nutzlos zum Unterscheiden gleichnamiger Hosts
- Kein neues Feld gebaut: Ein zweites „Kommentar“ hätte dieselbe Angabe an zwei Orten gehalten
+- Doppelte Hosts (#041): Zwei Einträge desselben Namens beim selben Kunden entstehen, wenn sich die Maschinenkennung ändert. Neue Warnregel plus die bis dahin fehlende Möglichkeit, einen Host überhaupt loszuwerden
+- Bewusst kein automatisches Zusammenführen: Es müsste raten, welcher Eintrag welche Rechnung erbt
+
### Nächster Schritt
- #016 Benutzerhandbuch mit Screenshots, danach #017 Favicons und #019 CI-Pipeline
- Für 1.0.0 fehlt noch: PMG (kein Host verfügbar) und der Fall „aktive Subscription auf PBS“ (#023)
diff --git a/docs/anleitung/README.md b/docs/anleitung/README.md
index d5e3221..8815b0c 100644
--- a/docs/anleitung/README.md
+++ b/docs/anleitung/README.md
@@ -205,6 +205,21 @@ gibt es Schlagworte und Notizen, zu finden im Host-Detail unter *Notizen und Tag
Bewährt haben sich kurze, wiederverwendbare Worte: `cluster`, `backup`, `produktiv`, `aussenstelle`.
+### Wenn ein Host doppelt erscheint
+
+Steht derselbe Name zweimal beim selben Kunden, meldet ProxLedger das als Hinweis und nennt die
+IP-Adressen beider Einträge. Zwei Fälle sind möglich:
+
+- **Zwei verschiedene Maschinen** heißen gleich. Dann helfen Schlagworte beim Auseinanderhalten.
+- **Dieselbe Maschine hat sich neu registriert** – etwa nach einer Neuinstallation oder weil ein
+ Container geklont wurde. Hosts werden an ihrer Kennung aus `/etc/machine-id` erkannt, nicht am
+ Namen; ändert sie sich, entsteht ein zweiter Eintrag.
+
+Den alten Eintrag legst du im Host-Detail still. Er verschwindet dann aus Listen, Zahlen und
+Warnungen, seine Laufzeiten und Rechnungen bleiben aber erhalten – mit dem Schalter *stillgelegte
+Hosts zeigen* findest du ihn wieder. **Endgültig löschen** geht nur, solange keine Laufzeit daran
+hängt.
+
---
## 5. Kunden und Standorte pflegen
diff --git a/docs/issues/041-doppelte-hosts.md b/docs/issues/041-doppelte-hosts.md
new file mode 100644
index 0000000..8105a99
--- /dev/null
+++ b/docs/issues/041-doppelte-hosts.md
@@ -0,0 +1,35 @@
+# #041 Gleichnamige Hosts erkennen und stilllegen
+**Status:** erledigt
+**Meilenstein:** v0.24.0
+**Labels:** backend, ui, alerts
+**Geschätzt:** 3 h **Tatsächlich:** 0 h 50 min
+
+## Auslöser
+Florian markierte in der Host-Liste zwei Paare: `hv-pve-01` und `hv-pve-02` standen jeweils **zweimal
+beim selben Kunden** – einmal „Aktiv“ mit einer IP, einmal „Keine Subscription“ mit drei anderen.
+
+## Was dahintersteckt
+Hosts werden an ihrer Kennung aus `/etc/machine-id` erkannt, nicht am Namen. Ändert sich diese Kennung
+– Neuinstallation, geklonter Container, zurückgespielte Sicherung –, meldet sich dieselbe Maschine als
+**neuer** Host, und der alte Eintrag bleibt stehen. Zwei getrennte Maschinen mit gleichem Namen sind
+aber genauso möglich. Welcher Fall vorliegt, weiß nur der Betreiber.
+
+## Was fehlte
+1. Kein Hinweis auf den Doppeleintrag.
+2. **Keine Möglichkeit, einen Host loszuwerden.** Ein Eintrag, der nicht mehr existiert, blieb für
+ immer in Listen, Zahlen und Warnungen stehen.
+
+## Behebung
+- **Dreizehnte Warnregel `hostname-doppelt`:** meldet mehrere Hosts desselben Kunden mit gleichem
+ Namen und nennt die IP-Adressen zum Unterscheiden. Gleiche Namen bei **verschiedenen** Kunden sind
+ der Normalfall und bleiben still.
+- **Stilllegen** (`archiviertAm`): Der Host verschwindet aus Listen, Zahlen, Suche und Warnungen;
+ seine offenen Warnungen werden geschlossen. Laufzeiten und Rechnungen bleiben erhalten.
+- **Wieder aufnehmen** mit demselben Knopf.
+- **Endgültig löschen** nur, solange keine Laufzeit am Host hängt – Buchhaltung wirft man nicht weg.
+- In der Liste ein Schalter „stillgelegte Hosts zeigen“ und ein Abzeichen an der Zeile.
+
+## Entscheidung
+Stilllegen statt automatisch zusammenführen. Ein Zusammenführen müsste raten, welcher Eintrag welche
+Rechnung erbt – im Zweifel zulasten der Buchhaltung. Die Warnung nennt beide Hosts mit IPs; die
+Entscheidung trifft der Mensch.
diff --git a/docs/issues/README.md b/docs/issues/README.md
index 7a30946..18de0b4 100644
--- a/docs/issues/README.md
+++ b/docs/issues/README.md
@@ -44,4 +44,5 @@ Details und Architektur: [../architektur.md](../architektur.md). Zeiten werden a
| [#038](038-ueberbreite-handy.md) | Seite wird auf dem Handy herausgezoomt | erledigt | v0.22.1 | 2 h | 0 h 40 min |
| [#039](039-zugang-zur-laufzeit.md) | Auf dem Telefon führte kein Weg zur Laufzeit | erledigt | v0.22.2 | 1 h | 0 h 30 min |
| [#040](040-schlagworte-sichtbar.md) | Schlagworte und Notizen sichtbar machen | erledigt | v0.23.0 | 2 h | 0 h 40 min |
-| | **Summe** | | | **165 h** | **21 h 43 min** |
+| [#041](041-doppelte-hosts.md) | Gleichnamige Hosts erkennen und stilllegen | erledigt | v0.24.0 | 3 h | 0 h 50 min |
+| | **Summe** | | | **168 h** | **22 h 33 min** |
diff --git a/package.json b/package.json
index af650d7..74806be 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "proxledger",
- "version": "0.23.0",
+ "version": "0.24.0",
"private": true,
"scripts": {
"dev": "next dev",
diff --git a/prisma/migrations/20261003180000_host_archivieren/migration.sql b/prisma/migrations/20261003180000_host_archivieren/migration.sql
new file mode 100644
index 0000000..4a0c9d6
--- /dev/null
+++ b/prisma/migrations/20261003180000_host_archivieren/migration.sql
@@ -0,0 +1,5 @@
+-- Hosts stilllegen, statt sie zu löschen (#041).
+-- Ein stillgelegter Host verschwindet aus Listen, Zahlen und Warnungen; seine Laufzeiten und
+-- Rechnungen bleiben erhalten, weil sie zur Buchhaltung gehören.
+ALTER TABLE "Host" ADD COLUMN "archiviertAm" TIMESTAMP(3);
+CREATE INDEX "Host_archiviertAm_idx" ON "Host"("archiviertAm");
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index d4c7149..5dd09ed 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -134,6 +134,10 @@ model Host {
subNextduedate DateTime? @db.Date
subChecktime DateTime?
+ /// Stillgelegt: Der Host erscheint nicht mehr in Listen, Zahlen und Warnungen – seine
+ /// Geschichte samt Laufzeiten und Rechnungen bleibt aber erhalten (#041).
+ archiviertAm DateTime?
+
erstmalsGesehenAm DateTime @default(now())
letzterReportAm DateTime?
erstelltAm DateTime @default(now())
diff --git a/src/app/(dashboard)/hosts/[id]/page.tsx b/src/app/(dashboard)/hosts/[id]/page.tsx
index fceef16..f0892ef 100644
--- a/src/app/(dashboard)/hosts/[id]/page.tsx
+++ b/src/app/(dashboard)/hosts/[id]/page.tsx
@@ -17,6 +17,7 @@ import { PRODUKT_TEXT } from "@/lib/anzeige";
import { prisma } from "@/lib/db";
import { formatDatum, formatDatumZeit, formatEuro, formatUptime, formatZahl } from "@/lib/format";
import { KundeStandortFormular } from "@/components/hosts/KundeStandortFormular";
+import { hostArchivieren, hostLoeschen } from "@/server/actions/hosts";
import { hostTokenWiderrufen } from "@/server/actions/tokens";
import { hostMitDetails } from "@/server/queries/hostliste";
import { meldungsGrenze } from "@/server/queries/hosts";
@@ -108,6 +109,11 @@ export default async function HostDetailSeite({ params }: { params: Promise<{ id
{host.hostname}
{host.fqdn ?? "Kein FQDN gemeldet"}
{meldetNicht ? : null}
+ {host.archiviertAm ? (
+
+ Stillgelegt
+
+ ) : null}
{/* Schlagworte und Notiz stehen hier, nicht nur im Reiter – bei gleichnamigen
Hosts ist das die Stelle, an der man sie braucht (#040). */}
@@ -198,6 +204,32 @@ export default async function HostDetailSeite({ params }: { params: Promise<{ id
kundeId={host.kundeId}
standortId={host.standortId}
/>
+
+ {/* Stilllegen statt löschen: Laufzeiten und Rechnungen gehören zur Buchhaltung (#041). */}
+
+ {host.archiviertAm
+ ? `Stillgelegt am ${formatDatum(host.archiviertAm)} – erscheint nicht in Listen, Zahlen und Warnungen.`
+ : host.laufzeiten.length === 0
+ ? "Stilllegen blendet den Host aus; löschen geht nur, solange keine Laufzeit daran hängt."
+ : "An diesem Host hängen Laufzeiten – er lässt sich nur stilllegen, nicht löschen."}
+