RFC 0024 — Fediverse UI : invitations & ops admin (Slice C)
- Auteur : EBO (rustwarden)
- Date : 2026-05-10
- Statut : Accepted (auto-revue mainteneur unique 2026-05-10, score 26/35)
- Apps consommatrices concernées : toute app
rustwarden-uiqui veut un dashboard ops + flux invitations cross-instance. - Plan associé : Plan UI HTML §7 Slice C.
- Dépendances : RFC 0011 (file outbound), RFC 0019 (tracing), RFC 0009 (FediverseInviteService), RFC 0021 (admin foundations).
Summary
Livrer 6 écrans HTML restants du parcours fédération admin/ops :
| # | Écran | Iter |
|---|---|---|
| 1 | /admin/fediverse/queue — dashboard file outbound (status counts + 100 dernières lignes) | 1 ✅ |
| 2 | /admin/fediverse/sessions_remote — sessions distantes actives + kill switch par domaine | 2 |
| 3 | /admin/fediverse/nodeinfo — stats NodeInfo humanisées | 3 |
| 4 | /admin/teams/{id}/invites — invitations cross-instance (form + table pending/accepted/rejected/revoked) | 4 |
| 5 | /profile/connected_apps — section profil "Apps connectées" + bouton révoquer | 5 |
| 6 | /fediverse/invite/{id} — page acceptation d'invitation (côté IdP du destinataire) | 6 |
L'iter. 1 ferme la livraison en une passe ; les iter. 2-6 réutilisent le pattern Slice A (handler + template + i18n + route).
Motivation
Slice A donne le contrôle de la fédération (toggle + trust). Slice B donne l'auth utilisateur. Slice C donne la visibilité (queue, NodeInfo, sessions) et les flux invitations cross-instance (RFC 0009). Sans ces pages, un opérateur n'a pas de signal sur la santé du système et un admin team ne peut pas inviter quelqu'un d'une autre instance.
Non-goals
- Pas de graphiques temps-réel dans
queue.html— un refresh manuel suffit. Les graphes Prometheus ressortent des events RFC 0019. - Pas d'éditions inline sur les invitations — révoquer = action atomique.
- Pas de bulk-revoke sur sessions distantes — une à la fois ; le kill switch domaine couvre les cas de masse.
- Pas de pagination sophistiquée — limite 100 rows par page suffit pour le MVP ; tri par created_at desc.
API proposée
Routes UI HTML (rustwarden-ui)
| Méthode | Path | Handler | Auth |
|---|---|---|---|
| GET | /admin/fediverse/queue | admin_fediverse::get_queue | admin |
| GET | /admin/fediverse/sessions_remote | admin_fediverse::get_sessions_remote | admin |
| POST | /admin/fediverse/sessions_remote/{domain}/kill | admin_fediverse::kill_domain | admin + CSRF |
| POST | /admin/fediverse/sessions_remote/{id}/revoke | admin_fediverse::revoke_remote_session | admin + CSRF |
| GET | /admin/fediverse/nodeinfo | admin_fediverse::get_nodeinfo | admin |
| GET | /admin/teams/{team_id}/invites | admin_invites::get_invites | admin team |
| POST | /admin/teams/{team_id}/invites | admin_invites::create_invite | admin team + CSRF |
| POST | /admin/teams/{team_id}/invites/{id}/revoke | admin_invites::revoke_invite | admin team + CSRF |
| GET | /profile/connected_apps | profile_fediverse::connected_apps | logged-in user |
| POST | /profile/connected_apps/{host}/revoke | profile_fediverse::revoke_app | logged-in user + CSRF |
| GET | /fediverse/invite/{id} | invite_landing::form | logged-in user |
| POST | /fediverse/invite/{id} | invite_landing::submit | logged-in user + CSRF |
Tous les endpoints /admin/fediverse/* sont gated par feature + 404 si fediverse_enabled = false. Les endpoints /profile/connected_apps et /fediverse/invite/* aussi (P9 du Plan UI).
Templates Askama (nouveaux)
templates/admin/fediverse/ ├── queue.html # iter. 1 ✅ ├── sessions_remote.html # iter. 2 └── nodeinfo.html # iter. 3 templates/admin/teams/ └── invites.html # iter. 4 templates/profile/ └── connected_apps.html # iter. 5 templates/fediverse/ └── invite_landing.html # iter. 6
Compatibilité
Aucun impact sur les apps qui n'activent pas experimental-fediverse. Pures additions, gated.
AuthService::call accepte déjà les préfixes /admin, /profile, /settings ; le seul nouveau préfixe à enregistrer pour Slice C est /fediverse/invite (sera ajouté en iter. 6).
Alternatives
- Tout en une RFC monolithique — rejeté : on a vu sur Slice B que 4-5 écrans sur une seule RFC mène à des turns trop chargés ; le découpage en iter livrables séparément a bien fonctionné.
- Reporter Slice C jusqu'à RFC 0006 Phase 2 — rejeté : Slice C est largement indépendant de Phase 2. Seul
connected_appsvoit potentiellement des sessions remote = 0 tant que Phase 2 n'est pas livrée, mais le rendu page vide est testable. - Page admin/sessions_remote en read-only sans kill switch — rejeté : sans action destructive, l'admin ne peut pas répondre à un incident (instance fédérée compromise → tuer toutes les sessions de ce domaine).
Stratégie d'itérations
| Iter | Livrable | État |
|---|---|---|
| 1 | /admin/fediverse/queue (RFC 0011 + RFC 0019 visualisés) | ✅ livré |
| 2 | /admin/fediverse/sessions_remote + kill switch | À venir |
| 3 | /admin/fediverse/nodeinfo | À venir |
| 4 | /admin/teams/{id}/invites (utilise RFC 0009 FediverseInviteService) | À venir |
| 5 | /profile/connected_apps | À venir |
| 6 | /fediverse/invite/{id} (parcours destinataire) | À venir |
Chaque iter passe les 5 quality gates (build / test / clippy / doc / migration N/A) avant la suivante.
Testing plan
- Iter. 1 : test handler
get_queuerendu — empty state quand 0 row, table quand >0. Couvert par MockDatabase. - Iter. 2 : test kill switch invalide une session, audit log généré.
- Iter. 3 : test nodeinfo response shape.
- Iter. 4-6 : tests handler invitations (création / révocation) + acceptation côté destinataire.
Migration guide
Aucune. Pure addition de routes + templates.
Open questions
- Q1 : faut-il pousser la file outbound en SSE temps-réel ou laisser refresh manuel ? Réponse provisoire : refresh manuel ; SSE seulement si la demande émerge en prod.
- Q2 : sessions_remote utilise quelle source de vérité — JWT blacklist ou RemoteIdentities ? Réponse : RemoteIdentities (les rows actives = sessions actives).
Score (26/35)
| Critère | Note | Détail |
|---|---|---|
| Généralité | 4/5 | 6 écrans réutilisables. |
| Stabilité API | 5/5 | Pures additions. |
| Performance | 3/5 | LIMIT 100 + count() par status — borné mais à reviser si > 10k rows. |
| Sécurité | 4/5 | CSRF + admin_required + kill switch destructif (modale obligatoire). |
| Complexité | 2/5 | 6 écrans = ~1200 LoC sur 6 iters. |
| Maintenance | 4/5 | Pattern existant Slice A/B copié. |
| Documentation | 4/5 | Cette RFC + 6 entrées CHANGELOG par iter. |
GitRust