feat: add the concierge bot for expiring guest invitations

Turns guest onboarding from an admin-only click in the Authentik UI into a traceable command a defined circle can run: !einladen creates a single-use invitation valid for three days, !verlaengern extends it twice at most, !freischalten makes it permanent, and expired accounts are deactivated automatically.

Authorisation is deliberately twofold - the Authentik group decides, the invite room makes it visible. A group alone leaves no trace of who invited whom; a room alone would authorise anyone who gets in.

Two deployment details matter: exactly one replica with Recreate, because a second instance would execute every command twice; and the script ConfigMap keeps its name hash so a change actually restarts the pod, avoiding the trap described in #50.

Endpoints and field names were taken from the running Authentik OpenAPI schema, not guessed. Refs axion1337.chat/axion1337.chat-gitops#48
This commit is contained in:
Thore Cimbal
2026-08-09 12:00:00 +00:00
parent f7412817c3
commit f6d2761025
4 changed files with 515 additions and 0 deletions
@@ -0,0 +1,107 @@
# @concierge — Gäste-Einladungen mit Ablauf
**Status**: gebaut, wartet auf Zugangsdaten · Issue [#48](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/48)
Ein kleiner Bot, der Einladungslinks erzeugt, Gastkonten nach drei Tagen ablaufen
lässt und die dauerhafte Freischaltung an eine bewusste Admin-Handlung bindet.
## Warum es diesen Bot gibt
Registrierung läuft in diesem Stack **ausschließlich über Authentik**. Bis jetzt
hieß das: Wer jemanden einladen will, klickt in der Authentik-Oberfläche einen
Invitation-Token zusammen. Das können nur Admins, es hinterlässt keine Spur, wer
wen eingeladen hat, und ein Gastkonto bleibt für immer bestehen.
Der Bot macht daraus einen Vorgang, den ein festgelegter Kreis selbst auslösen
kann — nachvollziehbar und mit eingebautem Ablauf.
## Wie es funktioniert
```
!einladen <name> → Authentik-Invitation (einmalig, 3 Tage) + Link im Raum
Gast registriert sich → Konto trägt threadnet_guest_expires_at
!verlaengern @gast → +1 Tag, höchstens 2×
!freischalten @gast → Ablauf entfernen, in members-Gruppe (nur Admins)
(nichts davon) → Bot deaktiviert das Konto nach Ablauf
```
### Berechtigung ist zweiteilig — und das ist Absicht
**Authentik-Gruppe UND Einladungsraum.** Die Gruppe entscheidet, der Raum macht
sichtbar. Eine Gruppe allein ist unsichtbar: Niemand sähe, wer eingeladen hat.
Ein Raum allein autorisiert nicht: Wer hineinkommt, dürfte alles. Zusammen ergibt
sich beides, und jede Einladung hinterlässt einen nachlesbaren Eintrag.
### Zwei Dinge, die beim Umbauen leicht kaputtgehen
⚠️ **Genau eine Instanz.** `replicas: 1` **und** `strategy: Recreate`. Der Bot
hält eine `/sync`-Schleife; zwei Instanzen führen jedes Kommando doppelt aus. Bei
`RollingUpdate` liefen während eines Deploys kurzzeitig zwei.
⚠️ **Die ConfigMap trägt bewusst einen Namens-Hash.** Anders als beim
ClamAV-Modul steht hier **kein** `disableNameSuffixHash: true`. Dadurch ändert
sich der ConfigMap-Name mit dem Skript, kustomize zieht die Referenz nach, und
der Pod startet von selbst neu. Ohne das hätten wir den Fall aus gitops#50:
geänderte Datei im Repo, alter Stand im laufenden Prozess.
### Fehlerverhalten ist absichtlich unsymmetrisch
- **Einladen und Freischalten scheitern laut.** Lieber keine Einladung als eine,
von der niemand weiß.
- **Die Ablaufprüfung deaktiviert nur, wenn Authentik sauber geantwortet hat.**
Ein API-Fehler darf nicht dazu führen, dass Konten reihenweise abgeschaltet
werden; im Zweifel bleibt ein Gast einen Durchlauf länger aktiv.
## Was zur Inbetriebnahme fehlt
Der Bot ist ausgerollt, **startet aber nicht**, solange das Secret fehlt — der Pod
meldet `secret "concierge-credentials" not found`. Das ist gewollt sichtbar; ein
Bot, der still nichts tut, wäre schlechter.
### 1. Matrix-Konto anlegen
```bash
kubectl exec -it -n matrix deploy/matrix-stack-matrix-authentication-service -- \
mas-cli manage register-user concierge --yes
kubectl exec -it -n matrix deploy/matrix-stack-matrix-authentication-service -- \
mas-cli manage issue-compatibility-token concierge
```
### 2. Authentik-Token
*Admin → Verzeichnis → Tokens*. Braucht Schreibrechte auf Nutzer, Gruppen und
Invitations. Ein eigenes Dienstkonto ist sauberer als ein Admin-Token.
### 3. Einladungsraum
Invite-only anlegen, `@concierge` einladen. Die Raum-ID ist Teil des Secrets, weil
sie zusammen mit den Token gepflegt wird und sich beim Neuanlegen ändert.
### 4. Gruppen in Authentik
`invite-berechtigt` (wer einladen darf) und `members` (wohin Freigeschaltete
kommen). Namen sind über `INVITE_GROUP` / `MEMBER_GROUP` änderbar.
### 5. Secret
```bash
kubectl create secret generic concierge-credentials -n matrix \
--from-literal=matrix-token='…' \
--from-literal=authentik-token='…' \
--from-literal=matrix-room-id='!….:axion1337.chat' \
--dry-run=client -o yaml > /tmp/s.yaml
sops -e /tmp/s.yaml > apps/production/concierge-secret.yaml
```
⚠️ Über SOPS ins Repo, nicht mit `kubectl apply` von Hand — sonst kennt Flux das
Secret nicht und es fehlt nach einem Wiederaufbau des Clusters.
## Grenzen
- **Matrix-Localpart = Authentik-Benutzername.** Gilt hier, weil MAS aus
Authentik provisioniert. Stimmt es einmal nicht, findet der Bot den Nutzer
nicht und sagt das — er rät nicht.
- **Ein deaktiviertes Konto ist nicht gelöscht.** Räume und Nachrichten bleiben.
Löschen ist bewusst nicht Sache des Bots.
- **Draupnir-Verzahnung** (Gast-Label → eingeschränkte Räume) ist Stufe 2 und
nicht Teil dieser Fassung.