# 13 — Server-Administration: Continuwuity

> **Gilt für:** Continuwuity 0.5.x (Fork von Conduwuit, welches ein Fork von Conduit ist)  
> **Homeserver:** `steggi-matrix.work`  
> **WICHTIG:** Continuwuity hat KEINE Synapse Admin API (`/_synapse/admin/` existiert nicht!)

---

## Inhaltsverzeichnis
- [Server-Admin vs. Room Power Level](#server-admin-vs-room-power-level)
- [Admin Room — Befehle](#admin-room--befehle)
- [Continuwuity Admin HTTP API](#continuwuity-admin-http-api)
- [Power Level in Räumen erzwingen (als Server-Admin)](#power-level-in-räumen-erzwingen)
- [MSC3083: Restricted Join Rules auf Continuwuity](#msc3083-restricted-join-rules-auf-continuwuity)
- [Diagnose: Warum M_FORBIDDEN?](#diagnose-warum-m_forbidden)
- [User-Admin-Status prüfen/setzen](#user-admin-status-prüfensetzen)
- [Raum-Administration via Matrix API](#raum-administration-via-matrix-api)
- [Wichtige Unterschiede: Continuwuity vs. Synapse](#wichtige-unterschiede-continuwuity-vs-synapse)
- [Continuwuity `homeserver.yaml` — relevante Einstellungen](#continuwuity-homeserveryaml--relevante-einstellungen)

---

## Server-Admin vs. Room Power Level

```
Continuwuity Server-Admin
├── Ermöglicht: Admin-Room-Befehle, Server-API
├── Ermöglicht: User deaktivieren, Räume löschen (Server-Level)
└── NICHT: Automatisch PL 100 in jedem Matrix-Raum!

Matrix Room Power Level 100
├── Gilt pro Raum (in m.room.power_levels State-Event gespeichert)
├── Ermöglicht: Name/Topic/Avatar setzen, Join-Rules ändern, User kicken/bannen
└── Unabhängig vom Server-Admin-Status
```

**Beispiel-Problem:**
- Du bist Server-Admin auf `steggi-matrix.work` ✅
- Du hast PL 100 im Space `!xyz:steggi-matrix.work` ✅
- Du erstellst einen neuen Channel → Server gibt dir PL 100 als Ersteller ✅
- Du versuchst `join_rules: restricted` zu setzen → M_FORBIDDEN ❌
  - **Grund:** Continuwuity 0.5.x unterstützt MSC3083 möglicherweise nicht vollständig

---

## Admin Room — Befehle

Continuwuity hat einen speziellen Admin-Raum. Als Server-Admin kannst du dort Befehle eingeben:

```
# Admin-Raum beitreten (normalerweise automatisch eingeladen)
# Raum-Alias: #admins:steggi-matrix.work

# Befehle im Admin-Raum (als Nachricht senden):

!admin help                              # Alle Befehle anzeigen

!admin make_user_admin @user:server      # User zu Server-Admin machen
!admin deactivate_user @user:server      # User deaktivieren
!admin list_users                        # Alle User auflisten

!admin get_room_state !roomId:server     # Room-State anzeigen
!admin delete_room !roomId:server        # Raum löschen

!admin get_mem_usage                     # Server-Speicherverbrauch
!admin server_version                    # Server-Version
```

```bash
# Als curl (Admin-Token erforderlich):
# Admin-Room-ID herausfinden:
curl -s "https://steggi-matrix.work/_matrix/client/v3/directory/room/%23admins%3Asteggi-matrix.work" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"
# → {"room_id": "!adminRoomId:steggi-matrix.work", ...}
```

---

## Continuwuity Admin HTTP API

```bash
# Basis-URL: https://steggi-matrix.work/_conduwuit/admin/v1/
# (Nicht /_synapse/admin/ — das ist Synapse-only!)

# User zu Server-Admin machen
curl -X POST "https://steggi-matrix.work/_conduwuit/admin/v1/make_user_admin" \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "@todesneutron:steggi-matrix.work"}'

# Server-Status abfragen
curl "https://steggi-matrix.work/_conduwuit/server/version" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"

# Alternativ — Standard Matrix-Whoami (prüft ob Token gültig):
curl "https://steggi-matrix.work/_matrix/client/v3/account/whoami" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"
# → {"user_id": "@todesneutron:steggi-matrix.work", "is_guest": false}

# Prüfen ob User Server-Admin ist (Conduwuit-spezifisch):
curl "https://steggi-matrix.work/_conduwuit/admin/v1/users/@todesneutron:steggi-matrix.work" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"
```

---

## Power Level in Räumen erzwingen (als Server-Admin)

Als Server-Admin kannst du via Matrix Client API Power Levels in jedem Raum setzen,
**auch wenn du kein Mitglied bist** (Continuwuity erlaubt das für Server-Admins):

```bash
# Schritt 1: Aktuelles Power-Level-Event lesen
ROOM_ID="!raumId:steggi-matrix.work"
TOKEN="<ADMIN_TOKEN>"
SERVER="https://steggi-matrix.work"

curl "${SERVER}/_matrix/client/v3/rooms/${ROOM_ID}/state/m.room.power_levels/" \
  -H "Authorization: Bearer ${TOKEN}"

# Schritt 2: Power Level setzen (users-Map ändern)
curl -X PUT "${SERVER}/_matrix/client/v3/rooms/${ROOM_ID}/state/m.room.power_levels/" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "users": {
      "@todesneutron:steggi-matrix.work": 100
    },
    "users_default": 0,
    "events_default": 0,
    "state_default": 50,
    "ban": 50,
    "kick": 50,
    "invite": 0,
    "redact": 50
  }'
```

```dart
// In Flutter / Dart (mit Admin-Token):
// Zuerst aktuellen PL-State lesen:
final currentPl = await client.getRoomStateWithKey(
  roomId,
  EventTypes.RoomPowerLevels,
  '',
);

// Users-Map erweitern und zurückschreiben:
final users = Map<String, dynamic>.from(
  (currentPl['users'] as Map?)?.cast<String, dynamic>() ?? {},
);
users['@todesneutron:steggi-matrix.work'] = 100;

await client.setRoomStateWithKey(
  roomId,
  EventTypes.RoomPowerLevels,
  '',
  {
    ...currentPl,
    'users': users,
  },
);
```

---

## MSC3083: Restricted Join Rules auf Continuwuity

`join_rules: restricted` (MSC3083) erlaubt Räumen, nur Space-Mitglieder beizutreten.
**Continuwuity 0.5.x unterstützt MSC3083 möglicherweise nicht vollständig.**

```bash
# Testen ob restricted join rules funktionieren:
ROOM_ID="!testRaum:steggi-matrix.work"
TOKEN="<ADMIN_TOKEN>"
SPACE_ID="!spaceId:steggi-matrix.work"

curl -X PUT "https://steggi-matrix.work/_matrix/client/v3/rooms/${ROOM_ID}/state/m.room.join_rules/" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "join_rule": "restricted",
    "allow": [
      {
        "type": "m.room_membership",
        "room_id": "'"${SPACE_ID}"'"
      }
    ]
  }'
# → 200 OK: Unterstützt!
# → M_FORBIDDEN oder M_UNRECOGNIZED: Nicht unterstützt oder PL zu niedrig
```

**Workaround wenn restricted nicht funktioniert:**
```bash
# Fallback: invite-only (explizit jeden User einladen)
curl -X PUT "https://steggi-matrix.work/_matrix/client/v3/rooms/${ROOM_ID}/state/m.room.join_rules/" \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"join_rule": "invite"}'
```

```dart
// In space_admin_dialog.dart — Fallback wenn restricted nicht supported:
try {
  await room.setJoinRules(
    JoinRules.restricted,
    allow: [
      SpaceRoomJoinRulesAllowInner(
        roomId: spaceId,
        type: SpaceRoomJoinRulesAllowInnerType.mRoomMembership,
      ),
    ],
  );
} on MatrixException catch (e) {
  if (e.error == MatrixError.M_FORBIDDEN || e.error == MatrixError.M_UNKNOWN) {
    // Continuwuity unterstützt restricted join rules nicht
    // Fallback zu invite-only:
    await room.setJoinRules(JoinRules.invite);
  } else {
    rethrow;
  }
}
```

---

## Diagnose: Warum M_FORBIDDEN?

```bash
# 1. Prüfen: Welchen Power Level hat mein User in diesem Raum?
ROOM_ID="!raumId:steggi-matrix.work"
TOKEN="<ADMIN_TOKEN>"

curl "https://steggi-matrix.work/_matrix/client/v3/rooms/${ROOM_ID}/state/m.room.power_levels/" \
  -H "Authorization: Bearer ${TOKEN}"
# Antwort zeigt 'users' Map — prüfen ob '@todesneutron:steggi-matrix.work' drin ist

# 2. Prüfen: Was ist state_default?
# Im Antwort-JSON: "state_default": 50 bedeutet: PL 50 nötig für State-Events
# "m.room.join_rules": 100 in events-Map = PL 100 für Join-Rule-Änderung

# 3. Prüfen: Bin ich Mitglied des Raums?
curl "https://steggi-matrix.work/_matrix/client/v3/rooms/${ROOM_ID}/state/m.room.member/%40todesneutron%3Asteggi-matrix.work/" \
  -H "Authorization: Bearer ${TOKEN}"
# → {"membership": "join"} = Mitglied ✅
# → {"errcode": "M_NOT_FOUND"} = Nicht Mitglied ❌ (Server-Admin kann trotzdem State setzen)

# 4. Prüfen: Unterstützt der Server restricted join rules?
curl "https://steggi-matrix.work/_matrix/client/v3/capabilities" \
  -H "Authorization: Bearer ${TOKEN}"
# Antwort: capabilities-Objekt — MSC3083 ist nicht explizit gelistet (kein Standard-Capability)
```

**Häufige Ursachen und Lösungen:**

| Fehler | Ursache | Lösung |
|---|---|---|
| M_FORBIDDEN beim State-Event | PL zu niedrig im Raum | Power Level via curl erhöhen (s.o.) |
| M_FORBIDDEN bei join_rules: restricted | MSC3083 nicht unterstützt | Fallback zu `invite` nutzen |
| M_FORBIDDEN beim Channel erstellen | Bug in space_admin_dialog.dart | Raum manuell erstellen, dann Space-Child setzen |
| M_NOT_FOUND | Raum nicht bekannt | Raum beitreten oder Admin-Join |
| M_FORBIDDEN bei Power-Level-Änderung | PL darf nur gleich/höher gesetzt werden | Als Ersteller oder via Admin-API |

---

## User-Admin-Status prüfen/setzen

```bash
# Prüfen ob User Server-Admin ist:
curl "https://steggi-matrix.work/_matrix/client/v3/account/whoami" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"

# Auf Pi (SSH): User-Datenbank direkt abfragen (Conduwuit/Continuwuity nutzt RocksDB)
# Alternativ: Über Admin-Room-Befehl
# (Im Admin-Room als Nachricht senden:)
# !admin make_user_admin @todesneutron:steggi-matrix.work

# Über HTTP (Conduwuit Admin-Endpoint):
curl -X POST "https://steggi-matrix.work/_conduwuit/admin/v1/make_user_admin" \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "@todesneutron:steggi-matrix.work"}'
```

---

## Raum-Administration via Matrix API

Da kein Synapse-Admin-API existiert, nutze die Standard-Matrix-Client-API mit Admin-Token:

```bash
# Raum beitreten (als Admin, auch wenn invite-only):
curl -X POST "https://steggi-matrix.work/_matrix/client/v3/join/!roomId%3Asteggi-matrix.work" \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{}'

# User in Raum einladen:
curl -X POST "https://steggi-matrix.work/_matrix/client/v3/rooms/!roomId%3Asteggi-matrix.work/invite" \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "@user:steggi-matrix.work"}'

# Space-Child-Relation setzen (Channel einem Space hinzufügen):
curl -X PUT "https://steggi-matrix.work/_matrix/client/v3/rooms/!spaceId%3Asteggi-matrix.work/state/m.space.child/!channelId%3Asteggi-matrix.work/" \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "via": ["steggi-matrix.work"],
    "suggested": false
  }'

# Space-Parent-Relation setzen (im Channel-Raum):
curl -X PUT "https://steggi-matrix.work/_matrix/client/v3/rooms/!channelId%3Asteggi-matrix.work/state/m.space.parent/!spaceId%3Asteggi-matrix.work/" \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "via": ["steggi-matrix.work"],
    "canonical": true
  }'

# Alle Mitglieder eines Raums auflisten:
curl "https://steggi-matrix.work/_matrix/client/v3/rooms/!roomId%3Asteggi-matrix.work/members" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"

# Raum verlassen (als User, nicht löschen):
curl -X POST "https://steggi-matrix.work/_matrix/client/v3/rooms/!roomId%3Asteggi-matrix.work/leave" \
  -H "Authorization: Bearer <ADMIN_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{}'
```

---

## Wichtige Unterschiede: Continuwuity vs. Synapse

| Feature | Synapse | Continuwuity 0.5.x |
|---|---|---|
| Admin API URL | `/_synapse/admin/v1/` | `/_conduwuit/admin/v1/` |
| Admin Room | Optional | Standard |
| MSC3083 (restricted join rules) | ✅ Vollständig | ⚠️ Teilweise / experimentell |
| Spaces (MSC1772) | ✅ | ✅ Grundfunktionen |
| MatrixRTC / MSC3401 | ✅ (Element Call) | ⚠️ Eingeschränkt |
| Push-Gateway | ✅ | ✅ |
| Server-side search | ✅ | ✅ Grundfunktionen |
| E2EE / Cross-Signing | ✅ | ✅ |
| Presence | ✅ | ✅ |
| Account Deactivation | Admin API | Admin Room / API |
| Room Deletion/Purge | Admin API | Admin Room / API |
| User Listing | Admin API | Admin Room / API |
| Federation | ✅ | ✅ |
| Application Services | ✅ | ⚠️ Eingeschränkt |

---

## Continuwuity `homeserver.yaml` — relevante Einstellungen

```yaml
# /etc/continuwuity/homeserver.yaml (oder /etc/conduwuit/conduwuit.toml je nach Version)

# Matrix-RTC / LiveKit
# MSC3266: Room Summary
# MSC3401: MatrixRTC
allow_unstable_room_versions: true

# Federation (für Multi-Server-Calls nötig)
allow_federation: true
allow_public_room_directory_without_auth: false

# Push (für Notifications)
# Kein spezieller Config — nutzt Standard-Matrix-Push-API

# TURN-Server für WebRTC
turn_uris:
  - "turn:steggi-matrix.work:3478?transport=udp"
  - "turn:steggi-matrix.work:3478?transport=tcp"
turn_secret: "dein-turn-secret"
turn_username: ""  # leer = HMAC-basiert
turn_password: ""
turn_ttl: 86400

# Registrierung
allow_registration: false  # Kein offener Signup
registration_token: "dein-token"  # Oder token-basiert

# Log-Level
log: "warn,conduwuit=info"

# Performance
rocksdb_parallelism_threads: 4  # Pi 5 hat 4 Cores

# Unter Continuwuity: Admin-User beim ersten Start setzen
# Erster registrierter User wird automatisch Server-Admin
```

```bash
# Auf dem Pi: Config-Datei editieren
ssh steggi@192.168.178.71
sudo nano /etc/continuwuity/homeserver.yaml
# oder:
sudo nano /etc/conduwuit/conduwuit.toml  # Falls TOML-Format

# Service neu starten:
sudo systemctl restart continuwuity
# oder:
sudo systemctl restart conduwuit

# Logs prüfen:
sudo journalctl -u continuwuity -f
```

---

## Schnell-Diagnose Checkliste

Wenn du M_FORBIDDEN bekommst:

```bash
# 1. Token gültig?
curl "https://steggi-matrix.work/_matrix/client/v3/account/whoami" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"

# 2. PL im betroffenen Raum prüfen:
curl "https://steggi-matrix.work/_matrix/client/v3/rooms/!ROOM_ID/state/m.room.power_levels/" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"

# 3. Mitglied des Raums?
curl "https://steggi-matrix.work/_matrix/client/v3/rooms/!ROOM_ID/state/m.room.member/%40todesneutron%3Asteggi-matrix.work/" \
  -H "Authorization: Bearer <ADMIN_TOKEN>"

# 4. Was genau schlägt fehl? (Antwort-Body lesen!)
# M_FORBIDDEN + "Power level too low" → PL erhöhen
# M_FORBIDDEN + "Not a member" → Raum beitreten
# M_UNRECOGNIZED → Feature nicht unterstützt
# M_FORBIDDEN + "..." → join_rules restricted Problem
```
