SsrfVerifier rejects private/reserved IPs (loopback, RFC1918, link-local,
CGNAT, ULA) on all federation fetches. Raw reqwest calls in webfinger and
backfill also validated. Debug mode bypasses via PermissiveVerifier.
Closes#4
Adds fetched_at to RemoteActor, configurable TTL via builder
(.actor_cache_ttl_secs, default 24h), and get_or_refresh_remote_actor
helper that re-fetches stale actors from origin.
Closes#3
Builder: .signed_fetch_actor_id(uuid) sets instance-level signing actor.
Service: .signed_fetch(&url) performs a signed GET returning raw JSON.
Closes#2
Bump to 0.3.1. These fields are available on DbActor at follow/ingest
time but were discarded when constructing RemoteActor. Now populated
in from_json and follow(), so consuming repos can store and return
rich actor profiles without extra queries.
These paths need content negotiation in real apps (AP JSON vs UI JSON).
k-ap can't serve the UI half, so the consuming app owns the route and
calls actor_json/followers_collection_json/following_collection_json
to produce the AP response.
The route conflict caused a panic when thoughts mounted its own
/users/{username}/... routes alongside service.router().
router() now registers only what k-ap fully owns:
- POST /inbox, POST /users/{id}/inbox (signature verification)
- GET /users/{id}/outbox
- GET /users/{id}/featured
- GET /.well-known/webfinger, nodeinfo, /nodeinfo/2.0
Undo(Announce): now removes announce record from ActorRepository and
calls ApObjectHandler::on_announce_removed (default no-op, override
to decrement boost counts). Announce counts no longer drift.
Undo(Block): now logged at info level instead of silently ignored.
No automatic relationship restoration (spec doesn't require it).
AddActivity: now uses object["id"] as the stable ap_id (same as
CreateActivity), falling back to activity id only if object has no
id field. Fixes keying watchlist/collection items by the wrong id.
Featured collection: GET /users/{id}/featured now served by the router.
ApContentReader::get_featured_objects() has a default empty-list impl
— override to expose pinned posts without any breaking changes.
#1 count_accepted_followers / get_accepted_followers_page: new DB-side
methods on FollowRepository — no more loading all followers into memory
to count or page them.
#2 Move re-follows are now non-blocking: tokio::spawn instead of
awaiting each sign_and_send inside receive() — inbox handler no longer
stalls for slow remote servers during account migration.
#3 Remove get_local_objects_for_user from ApContentReader (dead code).
Backfill and outbox both use the paginated get_local_objects_page.
#6 Rename backfill_outbox → import_remote_outbox with a clear doc
explaining the direction (import FROM a remote server, not to a follower).
#7 also_known_as: Option<String> → Vec<String> on ApUser, LookedUpActor,
and DbActor. from_json now stores all aliases; move_act.rs checks all.
Mentions: broadcast_create_note / broadcast_update_note now accept
mentioned_inboxes: Vec<Url> — delivery goes to followers + mentioned
actors who aren't already followers. Deduplication is done before
sending. Pass vec![] if note has no external mentions.
Docs: ApObjectHandler and ApContentReader now have complete doc comments
with contracts, idempotency guidance, and error-handling semantics.
#15 @context security vocab: actor JSON now uses actor_ap_context()
which includes W3C security vocab + Mastodon toot extensions
(manuallyApprovesFollowers, discoverable, featured).
Applied to actor_handler, actor_json(), broadcast_actor_update().
Activity JSON keeps plain AS context (no security vocab needed).
#17 HTTP Digest (documented, no code change): production mode
(debug=false) REQUIRES Digest header on inbound POSTs via
require_digest() in the non-compat normalization config.
Added doc comment to ApFederationConfig::new() to clarify.
#26 Integration tests: 3 new tokio tests in src/tests/integration.rs
using in-memory trait stubs. Tests cover:
- check_guards idempotency (duplicate activity rejected)
- check_guards domain block (blocked domain skipped)
- extract_and_dispatch_mentions (on_mention called for local actor)
#18 featured collection: add featured_url to ApUser/DbActor/Person;
serialized as featured field in AP JSON when set by consumer.
#19 Tombstone in Delete: broadcast_delete_to_followers now sends
{"type":"Tombstone","id":"..."} instead of bare URL string.
#21 Backfill pagination: run_backfill uses get_local_objects_page
with cursor-based loop — avoids loading all posts into memory;
delivers newest-to-oldest in BATCH_SIZE chunks.
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
Threads serves actors at threads.net but their id field uses www.threads.net.
Extract apex_domain() helper and fall back to apex comparison when the
strict verify_domains_match check fails.
Person struct now deserializes gracefully when outbox, followers, or
following are absent (Threads omits them for some actors). Accepts
Service/Application/Organization/Group in addition to Person.
manually_approves_followers defaults to false when absent.
Add display_name field to DbActor, populated from AP Person.name in
from_json. Expose LookedUpActor type and lookup_actor_by_handle method
on ActivityPubService — uses the existing signed webfinger_https path
so strict instances (Threads, etc.) return full actor data.