Fédération — Guide d'intégration pour les consommateurs du framework
Public visé : développeurs d'applications qui consomment le framework Rustwarden et veulent activer la fédération ActivityPub (SSO inter-instances + invitations cross-instance d'équipe).
Hors périmètre : déploiement / runtime opérationnel (variables d'env, supervision, troubleshooting). Voir
fediverse.mdetfediverse-operations.md.
Ce document décrit comment exploiter, depuis une application
consommatrice, les fonctionnalités de fédération livrées entre le
9 et le 14 mai 2026 (plans 2026-05-09
à 2026-05-14,
RFCs 0001 à
0025). Il s'inscrit dans
le processus d'évolution Rustwarden
(§11 — guide consommateur).
1. Périmètre fonctionnel livré
| Capacité | RFCs | Crate exposant l'API |
|---|---|---|
| Configuration + builder | 0001 | rustwarden-fediverse |
| WebFinger + Actor + NodeInfo | 0002 | rustwarden-fediverse |
| Cache acteurs distants | 0003 | rustwarden-fediverse |
| Modèle de confiance (allowlist / blocklist) | 0004 | rustwarden-fediverse (services::fediverse_trust_service) |
| Clés RSA d'acteur chiffrées | 0005 | rustwarden-fediverse (services::actor_key_service) |
| Auth AP-native (challenge / consent / back-channel) | 0006, 0013, 0025 | rustwarden-fediverse (auth_routes) |
Claims.identity_kind + remote_id | 0007 | rustwarden-core (auth) |
Abstraction Identity + sibling FKs | 0008 | rustwarden-core |
| Équipes / partages cross-instance | 0009, 0014 | rustwarden-fediverse (invite_routes) |
| Signaux lifecycle + retry/backoff | 0010, 0012 | rustwarden-fediverse (inbox) |
| Worker outbound + métriques | 0011, 0019 | rustwarden-fediverse (services::fediverse_outbound_service) |
| Signatures HTTP draft-cavage RSA-SHA256 | 0015, 0016, 0017 | rustwarden-fediverse (http_signatures) |
| Tests interopérabilité wiremock | 0020 | — (tests) |
| UI admin foundations + parcours utilisateur + ops | 0021, 0023, 0024 | rustwarden-fediverse-ui |
| Smoke tests anti-régression | 0022 | — (tests) |
L'ensemble est expérimental (cf. §6). L'API publique peut casser sans bump majeur jusqu'à promotion stable.
2. Décider si vous activez la fédération
Activez la fédération si au moins un de ces besoins existe côté app :
- SSO entre instances Rustwarden distinctes (un user d'instance A doit pouvoir s'authentifier sur instance B sans recréer de compte).
- Invitations cross-instance dans une équipe (inviter
@alice@autre-instance.orgdans une équipe locale). - Annonce d'un acteur ActivityPub consommable par Mastodon-likes (découverte WebFinger).
Si vous n'avez aucun de ces besoins : n'activez rien. La
fonctionnalité ajoute 4 tables, 1 worker tokio, ~2000 LOC compilées et
une surface réseau (/inbox, /users/{u}, /.well-known/*). Les
modules auth, oauth, team, etc. de rustwarden-core restent
parfaitement utilisables sans elle.
3. Choisir vos crates et vos features
Trois niveaux d'opt-in, cumulatifs (chaque niveau implique le précédent).
| Feature app | Implique | Ce que ça apporte | Quand l'activer |
|---|---|---|---|
fediverse | crate rustwarden-fediverse | Protocole ActivityPub headless : routes /.well-known/*, /users/{u}, /inbox, /fediverse/auth/*, /api/v1/teams/{id}/invites/*. Aucune UI HTML. | API-only / SPA séparée |
fediverse-ui | fediverse + crate rustwarden-fediverse-ui | UI HTML de référence : pages admin trust / blocks / outbound / invites, écrans utilisateur consent / login, carte de dashboard injectée via add_dashboard_card. | App SSR qui veut une UI prête à l'emploi |
fediverse-guard | fediverse + crate rustwarden-fediverse-guard | Détection d'abus temps-réel, ban manager, observer branché sur l'inbox (RFC 0028). UI admin guard si combinée à fediverse-ui. | Production exposée à des inconnus du fediverse public |
Exemple Cargo.toml côté app consommatrice (calqué sur
app/Cargo.toml) :
[features] default = [] fediverse = ["dep:rustwarden-fediverse"] fediverse-ui = ["fediverse", "dep:rustwarden-fediverse-ui"] fediverse-guard = ["fediverse", "dep:rustwarden-fediverse-guard"] [dependencies] rustwarden-core = { version = "*", features = ["oauth"] } rustwarden-ui = { version = "*" } rustwarden-fediverse = { version = "*", optional = true } rustwarden-fediverse-ui = { version = "*", optional = true } rustwarden-fediverse-guard = { version = "*", optional = true }
À ne pas faire : déclarer rustwarden-fediverse en dépendance
non-optionnelle. Vous perdez la possibilité de fournir une variante
sans fédération, et chaque utilisateur de votre app paye le coût compile.
4. Câbler la fédération dans votre main
Le câblage de référence est dans app/src/main.rs.
Le strict minimum côté app — sous #[cfg(feature = "fediverse")] :
use std::sync::Arc; use rustwarden_core::prelude::*; let builder = RustwardenApp::builder() .from_env() // UI partitionnée (CSRF, cf. SERVICES_REFERENCE §18) : SSR sur le canal // browser (CSRF), API JSON `/api/v1/*` sur le canal machine (pas de CSRF). .with_ui(rustwarden_ui::browser_routes) .merge_machine_routes(rustwarden_ui::api_routes()); #[cfg(feature = "fediverse")] let builder = { // 1. Connexion DB partagée avec le framework. let db_url = std::env::var("DATABASE_URL")?; let db = sea_orm::Database::connect(&db_url).await?; // 2. Config fédération chargée depuis l'environnement. let fediverse_config = rustwarden_fediverse::FediverseConfig::from_env()?; // 3. AuthConfig explicite — ANSSI F-NET-5 : on veut un échec // bruyant au démarrage si JWT_SECRET est un placeholder. let auth_config = Arc::new( rustwarden_core::config::auth::AuthConfig::from_env()?, ); // 4. Résolveur d'utilisateurs locaux (lookup `users` par handle). let resolver: Arc<dyn rustwarden_fediverse::resolver::LocalUserResolver> = Arc::new( rustwarden_fediverse::resolver::SeaOrmLocalUserResolver::new(db.clone()), ); // 5. Vérificateur de signatures HTTP. En prod : strict ; // en dev/testbed : AlwaysAcceptVerifier (vérifie seulement // que l'acteur résout, sans crypto). let verifier = Arc::new( rustwarden_fediverse::http_signatures::HttpSigVerifier::with_resolver( db.clone(), ), ); // 6. Routeur fédération complet (well-known + actor + inbox + // auth + invites). let fediverse_router = rustwarden_fediverse::routes::router_with_auth( fediverse_config, resolver, verifier, auth_config, ); // 7. Checker de révocation : un JWT remote dont la // `remote_identity` a reçu `Delete{Actor}` doit refuser // l'authentification au middleware. let checker: Arc<dyn rustwarden_core::middleware::revocation::RemoteIdentityRevocationChecker> = Arc::new( rustwarden_fediverse::services::revocation::SeaOrmRemoteIdentityRevocationChecker::new( db.clone(), ), ); builder .database(db) .register_protected_prefix("/fediverse/consent") .register_protected_prefix("/fediverse/invite") .register_revocation_checker(checker) // L'inbox (`POST /inbox`) est authentifiée par signature HTTP : aucun // token CSRF possible → canal machine (jamais de CSRF). Cf. §18. .merge_machine_routes(fediverse_router) }; #[cfg(feature = "fediverse-ui")] let builder = builder // Écrans SSR (consent / invite / login fediverse / admin) pilotés par // cookie de session → canal browser (CSRF). La validation par handler // `validate_csrf_token` est conservée en defense-in-depth. .merge_browser_routes(rustwarden_fediverse_ui::default_routes()) .add_dashboard_card(rustwarden_fediverse_ui::dashboard_card()); builder.build().await?.run().await
Points d'attention :
- DB partagée. Vous ouvrez
dbavant le builder et le passez via.database(db). La fédération doit lire/écrire dans les mêmes tables que le reste du framework — n'ouvrez pas une seconde pool. - Préfixes protégés.
register_protected_prefixcâble le middleware d'auth sur les routes qui exigent une session humaine (consent, invitations acceptées par un user identifié). Note RFC 0031 : depuis ce fix, vous n'avez pas besoin de déclarer un préfixe protégé juste pour queOption<Extension<Claims>>soit alimenté dans vos handlers — le middleware injecte les claims des deux côtés. Réservezregister_protected_prefixaux routes qui doivent rejeter les non-authentifiés. Détail dansauth-middleware-layering.md. - Revocation checker injecté. Sans cet appel, un
Delete{Actor}entrant n'invalide pas les JWT déjà émis pour ce remote.
5. Migrations à appliquer
Les 8 tables / extensions sont créées par les migrations SeaORM
m20260510_000009 à m20260510_000016 (cf. fediverse.md §4).
Côté app consommatrice :
cargo run --bin migrate
Le binaire migrate est ré-exposé par rustwarden-core — vous
n'avez rien à écrire côté app. Les migrations fédération
s'enregistrent automatiquement quand la feature fediverse du crate
est compilée.
Rollback : chaque migration a un down() testé en up/down dans
la CI du framework. Désactiver la fédération côté app ne rollback
pas automatiquement la DB ; faites-le explicitement si besoin
(cargo run --bin migrate -- down --num 8).
6. Statut experimental-fediverse — règles de versioning
Conformément au processus d'évolution §5, la fédération est encore expérimentale :
- Le code de la fédération est gated derrière des features
fediverse*qui restent en cycle "Birth → Adoption". Les RFCs sont Accepted mais l'API publique peut casser sans bump majeur tant que la promotion n'a pas eu lieu. - La promotion vers stable est conditionnée à la check-list
docs/plans/2026-05-10-fediverse-promotion-stable-evaluation.md: 6 scénarios E2E verts, 3 instances en topologie réelle, métriques exposées, doc opérateur tierce-personne-testable. - État au 2026-05-15 : tous les scénarios critiques E2E passent (cf. observation interne 618 du 2026-05-11). La promotion stable n'a pas encore été prononcée.
Recommandations de pinning côté app consommatrice (cf. §11.1 du processus) :
| Phase de votre app | Stratégie |
|---|---|
| Prototype / dev | rustwarden-fediverse = "*" |
| Pré-prod sur version expérimentale | rustwarden-fediverse = "=0.X.Y" (pin exact) |
| Production | Attendez la promotion stable, puis rustwarden-fediverse = "~0.X.Y" |
Tant que vous restez sur l'expérimental, vous devez surveiller le
CHANGELOG.md à chaque bump : un breaking sans
release majeure est légitime.
7. Suivre l'évolution upstream
Le processus §11.2 impose à chaque consommateur de tenir un
docs/UPSTREAM_<framework>.md. Le template Rustwarden vit dans
UPSTREAM_RUSTWARDEN.md. Pour la
fédération, ajoutez-y au minimum :
## Fédération (`fediverse*` features) - **Version pinned** : `rustwarden-fediverse = "=X.Y.Z"`. - **Features actives** : `fediverse`, `fediverse-ui`, `fediverse-guard`. - **Workarounds en cours** : - <aucun à ce jour> - **Breaking changes attendus** : - Promotion stable : `experimental-fediverse` → `fediverse` (suivre la closure du plan `2026-05-10-fediverse-promotion-stable-evaluation`). - Suivi RFCs > 0025 dans [`docs/rfcs/README.md`](rfcs/README.md). - **Hooks implémentés côté app** : - <ex. `ActiveTokenIterator` pour la blacklist JWT sur `Delete{Actor}`> - **Extensions custom au-dessus de `rustwarden-fediverse-ui`** : - <ex. dashboard card personnalisée, route admin supplémentaire>
À chaque release du framework, lisez la section Fediverse du CHANGELOG, mettez à jour ce fichier, puis bumpez (ou non) le pin.
8. Points d'extension consommateur
Quand le comportement par défaut ne suffit pas, étendez sans forker. Cinq points d'extension stables :
8.1 LocalUserResolver — qui est @alice@local ?
Par défaut : SeaOrmLocalUserResolver lit la table users. Si vous
avez un mapping custom (ex. les acteurs locaux sont une équipe + un
user), implémentez rustwarden_fediverse::resolver::LocalUserResolver
et injectez-le à router_with_auth.
8.2 SignatureVerifier — politique de confiance crypto
Trois variants livrés :
HttpSigVerifier: strict (prod, RFC 0015/0017).AlwaysAcceptVerifier: accepte tout (testbed local uniquement).- À vous d'écrire le vôtre via le trait
rustwarden_fediverse::services::fediverse_signal_service::SignatureVerifiersi vous voulez ajouter une mitigation custom (ex. rate-limit par domaine).
8.3 FediverseEventObserver — hook lifecycle
Branché par axum::Extension. Implémentez le trait pour réagir à
chaque inbox / outbound / signature (ex. compteurs custom, alerting
Slack). Le crate rustwarden-fediverse-guard est un consommateur
exemplaire de ce hook.
8.4 add_dashboard_card — cartes admin custom
Le builder expose .add_dashboard_card(DashboardCardDTO) (cf.
observation interne 799 du 2026-05-13). rustwarden-fediverse-ui
fournit une carte par défaut ; si vous voulez la remplacer ou ajouter
la vôtre, appelez add_dashboard_card après le merge des routes.
8.5 Monter vos routes — canal browser / machine
Si votre app veut sa propre page /profile/fediverse ou un endpoint
d'admin spécifique (ex. import/export de allowlist au format YAML),
écrivez un Router<DatabaseConnection> et fusionnez-le sur le bon
canal (CSRF, cf. SERVICES_REFERENCE §18) :
- page SSR mutante pilotée par cookie →
builder.merge_browser_routes(my_router)(CSRF appliqué ; le formulaire doit porterX-CSRF-Tokenoucsrf_token) ; - API JSON / webhook / endpoint signé →
builder.merge_machine_routes(my_router)(pas de CSRF).
merge_routes(..) reste disponible et équivaut à merge_browser_routes
(CSRF par défaut, fail-safe) : une API JSON montée par ce biais renverrait
403 faute de token → utilisez merge_machine_routes pour ces routes.
Ne dupliquez pas un service existant : étendez-le via wrapper /
trait (cf. processus §11.4).
9. Pilotage runtime (côté code consommateur)
Trois services exposés publiquement, à consommer depuis vos handlers / jobs custom.
9.1 Trust list (allowlist / blocklist)
use rustwarden_fediverse::services::fediverse_trust_service::FediverseTrustService; let svc = FediverseTrustService::new(db.clone()); svc.add_trusted_domain("mastodon.social", Some("Mastodon public"), None).await?; svc.block_domain("evil.example.org", "spam massif").await?;
Le mode (allowlist / open_with_blocklist) est piloté dans
app_settings — cf. fediverse.md §5.
9.2 Émettre une activité sortante
use rustwarden_fediverse::services::fediverse_outbound_service::FediverseOutboundService; let outbound = FediverseOutboundService::new(db.clone()); outbound .enqueue(activity_json, target_inbox, target_domain) .await?;
Le worker (à lancer dans un tokio::spawn — cf. fediverse.md §7)
applique le ladder de retry. Vous ne pilotez pas le retry depuis
votre code : tracez les événements via le target tracing
rustwarden::fediverse::outbound.
9.3 Forger un signal lifecycle local
use rustwarden_fediverse::services::fediverse_signal_service::FediverseSignalService; let signal = FediverseSignalService::new(db.clone()); signal.apply_delete_actor(&actor_iri).await?;
À utiliser quand votre app supprime un user local et veut publier le
Delete{Actor} correspondant côté fediverse.
10. Anti-patterns spécifiques fédération
- Activer
experimentalen prod sans plan de bump. Si vous ne surveillez pas le CHANGELOG hebdomadaire, vous prendrez un breaking par surprise. Pinnez exact ET planifiez l'audit des releases. - Câbler
AlwaysAcceptVerifierailleurs qu'en testbed. Tout message entrant signé "n'importe comment" passe — un fauxDelete{Actor}peut détruire vos sessions remote. Ne le faites qu'avecFEDIVERSE_DEBUG_LOCAL_FEDERATION=trueet un guard explicite dansmain. - Dupliquer un service framework. Si
FediverseTrustServicene suffit pas, ouvrez une RFC (processus §11.3) et proposez une extension. Une copie locale dérive en 1 release. - Lancer le worker outbound sans tracing. RFC 0019 expose 4
événements clés (
delivered/deferred/dead/drain_failed). Sans abonnement, vous découvrirez lesdeadpar les clients qui râlent. - Forker
rustwarden-fediverse-uipour changer un template. L'i18n et la dashboard card sont les points d'extension prévus. Pour un template entier custom, ré-écrivez-le côté app etmerge_routesvotre router à la place du sien.
11. Check-list d'adoption
Avant la première mise en route :
- Décision §2 prise et tracée dans votre ADR app.
-
Features Cargo §3 ajoutées côté app, build vert avec et sans la
feature
fediverse. -
Câblage §4 reproduit dans votre
main; au minimum :FediverseConfig::from_env(),LocalUserResolver,SignatureVerifier,register_revocation_checker,merge_routes. - Migrations §5 appliquées dans la DB cible.
-
Variables d'environnement §3 de
fediverse.mdconfigurées (FEDIVERSE_DOMAIN,ENCRYPTION_KEY,JWT_SECRET). -
Worker outbound lancé dans un
tokio::spawn. -
Tracing abonné au target
rustwarden::fediverse::outbound. -
UPSTREAM_<app>.md§7 mis à jour avec votre pin + workarounds. -
Plan de surveillance des bumps
rustwarden-fediverse(CHANGELOG lecture systématique tant qu'on est enexperimental-*).
GitRust