4.8 KiB
Federation Management Design
Goal
Allow users to manage their ActivityPub federation: accept/reject incoming remote follow requests, remove accepted remote followers, and unfollow remote actors they're following. Surface this in three places via a shared component set.
Architecture
Hexagonal layers respected throughout:
- Application layer: new use cases in
federation_management.rs— all business routing lives here - Presentation layer: handlers call use cases only, no direct port access
- Frontend: four components under
components/federation/, used in three locations
Backend
New use cases — crates/application/src/use_cases/federation_management.rs
Six functions, each taking &dyn FederationActionPort and &UserId. Return domain types (Vec<RemoteActor> or ()).
list_pending_requests(federation, user_id) → Result<Vec<RemoteActor>, DomainError>
accept_follow_request(federation, user_id, actor_url: &str) → Result<(), DomainError>
reject_follow_request(federation, user_id, actor_url: &str) → Result<(), DomainError>
list_remote_followers(federation, user_id) → Result<Vec<RemoteActor>, DomainError>
remove_remote_follower(federation, user_id, actor_url: &str) → Result<(), DomainError>
list_remote_following(federation, user_id) → Result<Vec<RemoteActor>, DomainError>
Unfollow remote reuses the existing unfollow_actor use case in social.rs (already routes @handle to federation.unfollow_remote).
New HTTP endpoints — crates/presentation/src/handlers/federation_management.rs
All routes require authentication (AuthUser extractor). Actor URLs go in the JSON request body to avoid percent-encoding issues.
| Method | Path | Body | Action |
|---|---|---|---|
GET |
/federation/me/followers/pending |
— | List pending follow requests |
POST |
/federation/me/followers/accept |
{ actor_url: String } |
Accept a follow request |
DELETE |
/federation/me/followers |
{ actor_url: String } |
Remove/reject a follower |
GET |
/federation/me/followers |
— | List accepted remote followers |
GET |
/federation/me/following |
— | List remote actors being followed |
DELETE |
/federation/me/following |
{ handle: String } |
Unfollow a remote actor (delegates to unfollow_actor) |
Handlers are thin: extract auth, call use case, return JSON. No logic.
Frontend
API client additions — thoughts-frontend/lib/api.ts
Six new functions mirroring the six endpoints. All take token: string.
getPendingFollowRequests(token)
acceptFollowRequest(actorUrl: string, token)
rejectFollowRequest(actorUrl: string, token)
getRemoteFollowers(token)
removeRemoteFollower(actorUrl: string, token)
getRemoteFollowing(token)
// unfollowRemote reuses existing unfollowUser or a new call to DELETE /federation/me/following
Response schema: RemoteActorSchema (already defined — handle, display_name, avatar_url, url).
Components — thoughts-frontend/components/federation/
pending-requests.tsx
- Client component
- Fetches
getPendingFollowRequestson mount - Renders list of remote actors with Accept and Reject buttons
- On action: optimistic removal from list, then API call
- Prop:
compact?: boolean— when true, renders as a flat list without card chrome (for notifications embed)
remote-followers.tsx
- Client component
- Fetches
getRemoteFollowerson mount - Renders list of accepted remote followers with a Remove button
- On remove: optimistic removal, then API call
remote-following.tsx
- Client component
- Fetches
getRemoteFollowingon mount - Renders list of remote actors being followed with an Unfollow button
- On unfollow: optimistic removal, then API call
federation-panel.tsx
- Composes the three above inside a shadcn
Tabscomponent - Tabs: "Requests", "Followers", "Following"
- Shows a numeric badge on "Requests" tab when pending count > 0
- No data fetching of its own — delegates entirely to sub-components
Usage locations
app/settings/federation/page.tsx (new)
- Server component shell, renders
<FederationPanel /> - Add "Federation" link to the settings sidebar alongside "Profile" and "API Keys"
app/users/[username]/page.tsx (modify)
- Add a "Federation" tab to the profile tabs row
- Render
<FederationPanel />as its content - Tab is only visible when
isOwnProfile === true
Notifications page (modify)
- Render
<PendingRequests compact />as a card section above the notification feed - Only shown when the user is authenticated
Out of scope
- Local follow request management (local follows are auto-accepted)
- Blocking remote actors (separate feature, already partially implemented)
- Notification count badge in the nav for pending requests (can be added later)