feat: production hardening — security, scale, protocol, DX
Breaking changes to FederationRepository, ApObjectHandler, ApUser:
FederationRepository:
- add is_activity_processed / mark_activity_processed (inbox idempotency)
- add get_accepted_follower_inboxes (DB-side dedup/filtering, replaces in-memory load-all)
ApObjectHandler:
- add on_announce_of_remote (cross-server boosts, previously silently dropped)
ApUser:
- add manually_approves_followers: bool
- add actor_type: ApActorType (was hardcoded Person)
Security:
- block check before actor HTTP fetch in Follow (prevents SSRF on blocked actors)
- 4xx responses use generic "not found"/"bad request" (no internal leak)
- 1 MB DefaultBodyLimit on inbox routes
- zeroize private key after generation
Delivery:
- all broadcasts are now non-blocking (tokio::spawn fallback, or EventPublisher queue)
- EventPublisher redesigned with typed FederationEvent enum (DeliveryRequested/DeliveryFailed)
- new deliver_to_inbox() public method for queue consumers
- configurable delivery_max_attempts and delivery_initial_delay_secs via builder
- Follow saved as Pending BEFORE delivery (race condition fix)
Router:
- GET /users/{id} (actor), GET /users/{id}/followers, GET /users/{id}/following now mounted
Protocol:
- mention extraction from Create/Update tag arrays → on_mention() dispatched
- WebFinger: add aliases field (acct: URI + AP actor URL)
- outbox: add last link, use count_local_posts for totalItems
- idempotency guard added to every inbound activity receive()
- actor serializes display_name and configurable actor_type/manually_approves_followers
Bump: 0.1.10 → 0.2.0
This commit is contained in:
43
src/data.rs
43
src/data.rs
@@ -4,11 +4,47 @@ use crate::content::ApObjectHandler;
|
||||
use crate::repository::FederationRepository;
|
||||
use crate::user::ApUserRepository;
|
||||
|
||||
/// Minimal event-publishing abstraction — project-specific implementations
|
||||
/// are wired in by the consuming crate via `FederationData::new`.
|
||||
/// Typed event emitted by the federation layer. Consumers wire in an
|
||||
/// [`EventPublisher`] to receive these and drive side effects (job queues,
|
||||
/// webhooks, metrics, etc.).
|
||||
///
|
||||
/// # Delivery flow
|
||||
///
|
||||
/// When an `EventPublisher` is configured, outbound activities are NOT
|
||||
/// delivered directly — instead a [`FederationEvent::DeliveryRequested`] event
|
||||
/// is published for each target inbox. The consumer's job queue should:
|
||||
/// 1. Persist the event.
|
||||
/// 2. Call [`crate::service::ActivityPubService::deliver_to_inbox`] when
|
||||
/// processing the queue item.
|
||||
///
|
||||
/// Without a publisher, the library falls back to fire-and-forget
|
||||
/// `tokio::spawn` delivery (no persistence across restarts).
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum FederationEvent {
|
||||
/// An outbound activity must be delivered to `inbox`.
|
||||
/// Call `ActivityPubService::deliver_to_inbox(inbox, activity, signing_actor_id)`.
|
||||
DeliveryRequested {
|
||||
inbox: url::Url,
|
||||
activity: serde_json::Value,
|
||||
signing_actor_id: uuid::Uuid,
|
||||
},
|
||||
/// Delivery to `inbox` failed permanently after all in-process retries.
|
||||
/// The consumer may schedule additional retries or alert.
|
||||
DeliveryFailed {
|
||||
inbox: url::Url,
|
||||
activity: serde_json::Value,
|
||||
signing_actor_id: uuid::Uuid,
|
||||
error: String,
|
||||
},
|
||||
}
|
||||
|
||||
/// Receives typed federation events from the library.
|
||||
///
|
||||
/// Implement this trait to bridge federation events into your application's
|
||||
/// job queue, message broker, or metrics system.
|
||||
#[async_trait::async_trait]
|
||||
pub trait EventPublisher: Send + Sync {
|
||||
async fn publish(&self, event: &str) -> anyhow::Result<()>;
|
||||
async fn publish(&self, event: FederationEvent) -> anyhow::Result<()>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -20,7 +56,6 @@ pub struct FederationData {
|
||||
pub(crate) domain: String,
|
||||
pub(crate) allow_registration: bool,
|
||||
pub(crate) software_name: String,
|
||||
#[allow(dead_code)]
|
||||
pub(crate) event_publisher: Option<Arc<dyn EventPublisher>>,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user