fediverse-consumer-guide.md 20618 octets

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.md et fediverse-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éRFCsCrate exposant l'API
Configuration + builder0001rustwarden-fediverse
WebFinger + Actor + NodeInfo0002rustwarden-fediverse
Cache acteurs distants0003rustwarden-fediverse
Modèle de confiance (allowlist / blocklist)0004rustwarden-fediverse (services::fediverse_trust_service)
Clés RSA d'acteur chiffrées0005rustwarden-fediverse (services::actor_key_service)
Auth AP-native (challenge / consent / back-channel)0006, 0013, 0025rustwarden-fediverse (auth_routes)
Claims.identity_kind + remote_id0007rustwarden-core (auth)
Abstraction Identity + sibling FKs0008rustwarden-core
Équipes / partages cross-instance0009, 0014rustwarden-fediverse (invite_routes)
Signaux lifecycle + retry/backoff0010, 0012rustwarden-fediverse (inbox)
Worker outbound + métriques0011, 0019rustwarden-fediverse (services::fediverse_outbound_service)
Signatures HTTP draft-cavage RSA-SHA2560015, 0016, 0017rustwarden-fediverse (http_signatures)
Tests interopérabilité wiremock0020— (tests)
UI admin foundations + parcours utilisateur + ops0021, 0023, 0024rustwarden-fediverse-ui
Smoke tests anti-régression0022— (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.org dans 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 appImpliqueCe que ça apporteQuand l'activer
fediversecrate rustwarden-fediverseProtocole 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-uifediverse + crate rustwarden-fediverse-uiUI 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-guardfediverse + crate rustwarden-fediverse-guardDé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 db avant 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_prefix câ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 que Option<Extension<Claims>> soit alimenté dans vos handlers — le middleware injecte les claims des deux côtés. Réservez register_protected_prefix aux routes qui doivent rejeter les non-authentifiés. Détail dans auth-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 appStratégie
Prototype / devrustwarden-fediverse = "*"
Pré-prod sur version expérimentalerustwarden-fediverse = "=0.X.Y" (pin exact)
ProductionAttendez 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::SignatureVerifier si 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 porter X-CSRF-Token ou csrf_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 experimental en 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 AlwaysAcceptVerifier ailleurs qu'en testbed. Tout message entrant signé "n'importe comment" passe — un faux Delete{Actor} peut détruire vos sessions remote. Ne le faites qu'avec FEDIVERSE_DEBUG_LOCAL_FEDERATION=true et un guard explicite dans main.
  • Dupliquer un service framework. Si FediverseTrustService ne 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 les dead par les clients qui râlent.
  • Forker rustwarden-fediverse-ui pour 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 et merge_routes votre 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.md configuré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 en experimental-*).

12. Références