danielpetrica / activitypub-for-laravel
ActivityPub server implementation for Laravel applications. WebFinger, actor profiles, inbox/outbox, and federation.
Package info
github.com/danielpetrica/ActivityPub-for-Laravel
pkg:composer/danielpetrica/activitypub-for-laravel
Requires
- php: ^8.5
- ext-curl: *
- ext-json: *
- ext-openssl: *
- laravel/framework: ^13.0
Requires (Dev)
- larastan/larastan: ^3.12
- laravel/pint: ^1.0
- orchestra/testbench: ^11.2
- pestphp/pest: ^5.1
- phpstan/phpstan: ^2.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- dev-main
- v0.15.0-rc.1
- v0.14.1
- v0.14.0
- v0.13.9
- v0.13.8
- v0.13.7
- v0.13.6
- v0.13.5
- v0.13.4
- v0.13.3
- v0.13.2
- v0.13.1
- v0.13.0
- v0.12.0
- v0.11.0
- v0.10.4
- v0.10.3
- v0.10.2
- v0.10.1
- v0.10.0
- v0.9.4
- v0.9.3
- v0.9.2
- v0.9.1
- v0.9.0
- v0.8.0
- v0.7.2
- v0.7.1
- v0.7.0
- v0.6.4
- v0.6.3
- v0.6.2
- v0.6.1
- v0.6.0
- v0.5.2
- v0.5.1
- v0.5.0
- v0.4.1
- v0.4.0
- v0.3.3
- v0.3.2
- v0.3.1
- v0.3.0
- v0.2.1
- v0.2.0
This package is auto-updated.
Last update: 2026-10-21 15:48:58 UTC
README
A server-to-server (S2S) ActivityPub federation implementation for Laravel 13. It lets your Laravel application publish actors, inboxes, and outboxes that federate with the wider Fediverse (Mastodon, Pleroma/Akkoma, Misskey, Pixelfed, PeerTube, etc.).
Scope: This package implements the ActivityPub server-to-server (S2S) profile only. Client-to-server (C2S) and OAuth 2.0 authorization are explicitly out of scope. Mobile and desktop clients such as Tusky or Elk authenticate against an instance using the C2S API and OAuth, neither of which this package provides, so they cannot log into your application through it.
Requirements / Support policy
| Requirement | Version |
|---|---|
| PHP | ^8.5 |
| Laravel | ^13.0 |
| PHP extension | ext-curl, ext-openssl, ext-json |
ext-curl is required: it backs the outbound federation transport and the
SsrfGuard connection pin (CURLOPT_RESOLVE) that closes the DNS-rebinding
window between the pre-flight SSRF check and the actual connect.
The PHP 8.5 and Laravel 13 floor is intentional, not an oversight. This package targets current language and framework features and does not backport to older releases. Older PHP and Laravel versions are unsupported: bug reports and pull requests whose only purpose is to re-enable older runtimes will not be accepted.
Features
Protocol
- Actor profiles (Person, ActivityStreams JSON-LD) with stable, absolute actor IRIs
- Collection endpoints (inbox, outbox, followers, following, featured) return a top-level
OrderedCollection; the items live onOrderedCollectionPagedocuments reachable through?page=N(real, spec-compliant pagination) - WebFinger discovery (JRD format), NodeInfo 2.0 and host-meta endpoints completed (scheme, profile-page/subscribe links)
- Featured/endorsed collections
- Mastodon spec compliance (discoverable, published fields)
- Content fidelity: flat
to/ccaddressing,tag/mention objects, noteurl, opt-in object-levelsensitiveand custom emoji tags - Accept activity always delivered even when federation is disabled
Federation
- Outbound delivery of Create, Update, Delete, Follow, Accept, Reject, Like, Announce, Undo, Block, Undo(Block) and Move
- Address resolution to the real object author (
deliverToObjectAuthor) instead of short-circuiting to the shared inbox @user@hostmentions resolved and delivered to each mention's inbox- Queue-driven delivery via the
DeliverActivityjob with exponential backoff and per-inboxactivity_deliveriestracking (reschedule and dead-letter handling) - Shared inbox optimization: delivery grouped by
shared_inbox_url, prefers the shared inbox over the personal inbox - Follower management with Accept/Block/Reject handling and locked-account approval (
approveFollower/rejectFollower) - Following model for outbound follows
- Post forwarding on first follow from a domain (
ForwardPostsToNewServerJob) - Block domains and users with auto-rejection of follows, and outbound
Block/Undo(Block) Update(Person)profile federation andMoveaccount migration withalsoKnownAsActivitymodel tracks all federated activities with a backedActivityStatusenum (pending, delivered, received, failed)- Queued inbox processing by default (
ProcessInboxActivity), central normalized block enforcement and activity-id idempotency
Interaction Tracking
- Polymorphic
Interactionmodel for likes, boosts, bookmarks, quotes, comments InteractionTypeenum (Like, Announce, Bookmark, Quote, Comment)InteractionStatusenum (Active, Undone) for undo support- Incoming Like/Announce automatically tracked as interactions
- Incoming Undo(Like)/Undo(Announce) properly reverses interactions
- The
ActivityInteractionshelper deduplicates repeated local like/boost/bookmark calls and reactivates a previously undone interaction - Static
ActivityInteractionsAPI for integrating clients (count, check, mutate, query) - Favorites tab in the Fediverse UI
HTTP Signatures
- RSA-SHA256 incoming verification and outbound signing
- Minimum signed-component enforcement (required covered headers) and RFC 9421 (
signature-input/ structured fields) support - Digest header required for non-empty POST bodies
- Signature replay cache with 120s TTL for idempotency (constant-time comparison)
- keyId HTTPS scheme validation
- Canonical host from config used in signing strings
- Parsing and verification split into
SignatureParserandSignatureVerifier
Strategy Pattern Handlers
12 handler classes (Follow, Like, Announce, Undo, Create, Delete, Update, Block, Accept, Reject, Flag, Move) implement ActivityHandler, tagged via the service container, and dispatched by InboxProcessor. Custom handlers can be registered through HandlerRegistry.
Events
7 event classes: ActivityDelivered, ActivityDeliveryFailed, FollowReceived, FollowRemoved, BlockReceived, InboxActivityReceived, FlagReceived.
Moderation
- Incoming
Flagactivities are recorded asReportrows and surface on/fediverse/status - Incoming
Flagalso fires theFlagReceivedevent for application hooks
Security
- SSRF hardening (
SsrfGuard): outbound fetches are HTTPS-only and must resolve exclusively to public IPs;security.allowed_hostsis the explicit escape hatch - Unknown signers are fetched off the verification path (
http_signatures.queue_unknown_actors) - Central normalized block enforcement for every inbound activity (not just follows)
- Inbox allowlist (normalized domains) and windowed per-actor/per-domain quotas that fail open
- Always-on rejected-request audit log (
inbox_logs) with configurable retention - Admin gate (
fediverse.admin_gate) required for mutating Fediverse routes - Per-IP rate limiting on inbox endpoints (60 requests/min)
- Fediverse interaction rate limiting (30 requests/min)
- Content-Length limit of 1 MB on inbox POST bodies
- Accept header negotiation via
RespondsToAccepttrait (browsers get 406)
Operations
- Queued inbox processing by default (
queue.process_inbox): the request returns202immediately and remote resolution runs on a worker - Per-inbox delivery records (
activity_deliveries) with reschedule and dead-letter handling from/fediverse/status - Always-on inbox audit log with configurable retention windows
activitypub:prune-activitieshonoursretention.*for delivered/received activities, inbox logs and interactions
Extension points
- Swap the actor model via
activitypub.actor_model(bound in the container and used for route binding) - Register custom activity handlers through
HandlerRegistry - Swap the activity builder (
ActivityBuilderContract), dispatcher (ActivityDispatcher) and recorder (ActivityRecorder) bindings
Performance
- Chunked follower delivery (
chunk(200)) instead of loading all into memory - Memoized actor resolution via request-level caches
- Subquery-based timeline queries instead of pluck+whereIn
- Composite indexes for feed performance
- Eager-loading of
remoteActorrelation on followers (N+1 fix)
Web UI
- Blade-based Fediverse dashboard with 12 views (dashboard, timeline, inbox, discover, profile, outbox, following, followers, servers, status, favorites, layout)
- Federated servers page (/fediverse/servers) showing all remote domains with follower counts
- Block/unblock servers and users from the UI
- Status/diagnostics page (/fediverse/status) showing queue health, federation status, activity counts
- Delivery status badges (Pending/Delivered/Failed) on outbox and dashboard
- Pinned and Published badges on activities
- Followers page with domain and status filters
- Profile editing, follow/unfollow, like, boost, and reply interactions
Content Forwarding
- Automatic post forwarding to new servers on first follow from a domain
- Configurable via
federatable_modelsconfig option - Delivers 3 newest published posts from registered content models
isActivityPubPinned()method on FederatableContentContract
Blocking
- Block entire domains — every inbound activity from that domain is dropped centrally by
InboxProcessor; incoming follow requests are additionally auto-rejected with aRejectactivity and no follower is created - Block individual remote actors — every inbound activity from that actor is dropped centrally; incoming follow requests are auto-rejected
- Outbound
BlockandUndo(Block)activities are delivered to the blocked actor's inbox - Blocks are stored in the
blocked_domainsandblocked_remote_actorsdatabase tables and managed from the Fediverse UI
Blocking is enforced centrally for all inbound activity types, through normalized domain matching (
DomainNormalizer), so casing, trailing dots, ports,www.and IPv4-mapped IPv6 hosts cannot be used to bypass it. It does not retroactively remove existing followers.
NodeInfo 2.0
- Full NodeInfo 2.0 compliance with software name, version, homepage, repository
usage.localPosts— count of outbound Create activitiesusage.users.activeMonth— actors active in last 30 dayslast_active_attracking on outbound activities
Artisan Commands
activitypub:create-actor— creates a local actor with RSA key pairactivitypub:deliver-content— manually delivers content with--debugmode for synchronous delivery with response tableactivitypub:prune-activities— prunes old federation data (delivered/received activities, the inbox audit log and stale interactions) using theretention.*windows (all default to 30 days); per-run overrides via--days,--received-days,--inbox-logs-days,--interactions-daysand--dry-run. Registered on Laravel's scheduler to run daily (withoutOverlapping()), so it is enforced automatically oncephp artisan schedule:runis wired to cron.
Installation
composer require danielpetrica/activitypub-for-laravel
Publish the configuration, migrations, and views:
php artisan vendor:publish --tag=activitypub-config php artisan vendor:publish --tag=activitypub-migrations php artisan vendor:publish --tag=activitypub-views php artisan migrate
Configuration
# Identity / federation ACTIVITYPUB_DOMAIN=https://your-domain.com # Actor identifiers domain (defaults to APP_URL) ACTIVITYPUB_FEDERATION_ENABLED=true # Enable outbound federation ACTIVITYPUB_FEDIVERSE_ENABLED=true # Enable Blade-based fediverse web UI ACTIVITYPUB_RESOLVE_TIMEOUT=10 # Timeout for WebFinger/actor resolution (seconds) # Queue (delivery + inbound processing) ACTIVITYPUB_QUEUE_CONNECTION=redis # Queue connection (null = app default) ACTIVITYPUB_QUEUE_NAME=default # Queue name for activity delivery ACTIVITYPUB_QUEUE_PROCESS_INBOX=true # Process inbox activities on the queue (recommended in production) # Cache / logging / debug ACTIVITYPUB_CACHE_ENABLED=true # Cache-Control headers on ActivityPub responses ACTIVITYPUB_LOGGING_ENABLED=true # Enable detailed federation logging ACTIVITYPUB_LOG_CHANNEL=activitypub # Log channel (optional, defaults to app default) ACTIVITYPUB_LOG_LEVEL=info # Log level: debug, info, warning, error ACTIVITYPUB_DEBUG_DISPLAY=false # Show debug info in inbox/outbox views # Fediverse dashboard authorization ACTIVITYPUB_FEDIVERSE_GATE=null # Gate for dashboard access (null = any authenticated user) ACTIVITYPUB_FEDIVERSE_ADMIN_GATE=activitypub.fediverse.admin # Gate required for mutating routes # Inbox audit log ACTIVITYPUB_INBOX_LOG_ENABLED=true # Record rejected inbox requests (always-on audit trail) # Inbox admission control ACTIVITYPUB_INBOX_ALLOWLIST_ENABLED=false # Restrict inbound federation to allowed domains ACTIVITYPUB_INBOX_QUOTAS_ENABLED=true # Windowed per-actor/per-domain inbox quotas ACTIVITYPUB_INBOX_PER_ACTOR_PER_MINUTE=120 ACTIVITYPUB_INBOX_PER_DOMAIN_PER_MINUTE=600 ACTIVITYPUB_INBOX_PER_ACTOR_PER_DAY=5000 ACTIVITYPUB_INBOX_INTERACTIONS_PER_ACTOR_PER_DAY=1000 # Retention (used by activitypub:prune-activities, scheduled daily by default) ACTIVITYPUB_RETENTION_DELIVERED_DAYS=30 ACTIVITYPUB_RETENTION_RECEIVED_DAYS=30 ACTIVITYPUB_RETENTION_INBOX_LOGS_DAYS=30 ACTIVITYPUB_RETENTION_INTERACTIONS_DAYS=30 # Outbound fetch hardening ACTIVITYPUB_MAX_RESPONSE_BYTES=2000000 # Max remote fetch response size (bytes)
A few of the new settings are config-file only (no environment variable), because they are lists or security policy rather than scalars:
security.allowed_hosts— hosts exempt from DNS resolution checks during outbound fetches (private IP literals are still rejected). Intended for trusted internal test targets only.http_signatures.queue_unknown_actors(defaulttrue) — queue aFetchRemoteActorjob instead of fetching synchronously on the signature-verification request path.inbox.allowlist.domains— the allowed domain list, matched throughDomainNormalizer. Enabling the allowlist with an empty list is treated as "not configured" and allows traffic.activitypub.actor_model— the actor model class used for DI and route binding.
The full configuration is published to config/activitypub.php and includes settings for routes, HTTP signatures, federation timeouts, and the actor model class.
⚠️ Warning: The plugin encrypts actor private keys using your
APP_KEY. If you change or rotate yourAPP_KEY, all existing actors' HTTP signatures will break and federation will stop working. Back up yourAPP_KEYbefore rotating it.
Authorization
The Fediverse dashboard is protected by Laravel's gate system. By default, any authenticated user can access it. To restrict access, define a gate in your AuthServiceProvider and configure it:
// app/Providers/AuthServiceProvider.php public function boot(): void { Gate::define('viewFediverse', fn ($user) => $user->isAdmin); }
ACTIVITYPUB_FEDIVERSE_GATE=viewFediverse
If ACTIVITYPUB_FEDIVERSE_GATE is null (default), all authenticated users can access the dashboard.
Admin gate for mutating routes
Mutating Fediverse routes that change instance-wide state (blocking domains and actors, pruning, rescheduling deliveries, refreshing followed accounts and forwarding posts) require an additional admin authorization. By default the package checks the activitypub.fediverse.admin gate:
// app/Providers/AuthServiceProvider.php Gate::define('activitypub.fediverse.admin', fn ($user) => $user->isAdmin());
Configure a different ability with ACTIVITYPUB_FEDIVERSE_ADMIN_GATE (config key fediverse.admin_gate). When the configured ability is not defined, the mutating routes deny by default (403), so a fresh install must define the ability before those controls can be used.
Debugging
Federation issues (failed follows, unreachable remote actors) can be hard to diagnose in production. Enable detailed logging to trace the full resolution flow:
ACTIVITYPUB_LOGGING_ENABLED=true ACTIVITYPUB_LOG_CHANNEL=activitypub # optional — uses app default if omitted ACTIVITYPUB_LOG_LEVEL=info # debug|info|warning|error
When enabled, the package logs every step of remote actor resolution:
- WebFinger discovery requests and results
- HTTP status codes and response bodies from remote servers
- SSRF protection blocks
- HTTP signature signing decisions
- Activity delivery outcomes
Example log output:
[2026-09-06 12:00:00] local.INFO: ActivityPub: Resolving handle {"handle":"user@mastodon.social"}
[2026-09-06 12:00:00] local.INFO: ActivityPub: WebFinger lookup {"resource":"acct:user@mastodon.social"}
[2026-09-06 12:00:01] local.INFO: ActivityPub: WebFinger resolved {"href":"https://mastodon.social/users/user"}
[2026-09-06 12:00:01] local.INFO: ActivityPub: Fetching remote actor {"actorUri":"https://mastodon.social/users/user"}
[2026-09-06 12:00:02] local.WARNING: ActivityPub: Remote actor HTTP fetch failed {"actorUri":"...","statusCode":403}
Point your log channel to a file or external service to capture these in production:
// config/logging.php 'channels' => [ 'activitypub' => [ 'driver' => 'daily', 'path' => storage_path('logs/activitypub.log'), 'days' => 14, ], ],
Usage
Creating an Actor
php artisan activitypub:create-actor --username=yourname --name="Your Name"
Federating Content
Have your Eloquent model implement FederatableContentContract and use the FederatesContent trait:
use DanielPetrica\LaravelActivityPub\Contracts\ActorContract; use DanielPetrica\LaravelActivityPub\Contracts\FederatableContentContract; use DanielPetrica\LaravelActivityPub\Traits\FederatesContent; class Post extends Model implements FederatableContentContract { use FederatesContent; public function shouldFederate(): bool { return $this->status === 'published'; } public function activityPubActor(): ActorContract { return $this->author; // must implement ActorContract } public function getActivityPubId(): string { return url("/posts/{$this->id}"); } // ... implement remaining contract methods }
Sending federated activities is handled automatically by the trait's model events, or you can use the facade:
ActivityPub::sendCreate($post); ActivityPub::sendUpdate($post); ActivityPub::sendDelete($post->getActivityPubId(), $post->activityPubActor());
The facade also exposes the interaction and moderation surface:
use DanielPetrica\LaravelActivityPub\ActivityPub; // Interactions ActivityPub::sendLike($actor, $objectUrl); ActivityPub::sendAnnounce($actor, $objectUrl); ActivityPub::sendNote($actor, $content, $inReplyToUrl); // Block / unblock (delivered to the target inbox) ActivityPub::sendBlock($actor, $remoteActorUrl); // Locked account: approve or reject a pending follower ActivityPub::approveFollower($actor, $follower); ActivityPub::rejectFollower($actor, $follower); // Profile change and account migration ActivityPub::updateActorProfile($actor); // emits Update(Person) to followers ActivityPub::sendMove($actor, $newActorUrl, $alsoKnownAs); // emits Move to followers and target
@user@host mentions found in published content are resolved automatically and delivered to each mentioned actor's inbox; no extra call is required.
Content fidelity (sensitive and custom emoji)
Object-level sensitive (NSFW) and custom emoji are opt-in: the built object is unchanged unless your content model (or actor) explicitly exposes them, so existing models keep byte-identical wire output.
class Post extends Model implements FederatableContentContract { // Object-level NSFW flag. Any of these is detected: // getActivityPubSensitive(): bool // isActivityPubSensitive(): bool // the `sensitive` or `activitypub_sensitive` attribute public function isActivityPubSensitive(): bool { return $this->is_nsfw; } // Shortcode => URL map. Any of these is detected: // getActivityPubEmoji(): array // getActivityPubEmojis(): array // the `activitypub_emoji` attribute public function getActivityPubEmoji(): array { return ['blobcat' => 'https://example.com/emoji/blobcat.png']; } }
Custom emoji are emitted as ActivityStreams Emoji tags ({"type":"Emoji","name":":blobcat:","icon":{"type":"Image","url":"..."}}) merged after any tags returned by getActivityPubTags(). Malformed or empty entries are skipped, never thrown.
Activity Interactions
The package tracks likes, boosts, bookmarks, and other interactions via a polymorphic Interaction model. This data is available for both the Fediverse UI and integrating clients (e.g., your blog's frontend).
Static API
use DanielPetrica\LaravelActivityPub\Services\ActivityInteractions; use DanielPetrica\LaravelActivityPub\Enums\InteractionType; // ── Counts ── ActivityInteractions::likeCount($post); // int ActivityInteractions::boostCount($post); // int ActivityInteractions::bookmarkCount($post); // int ActivityInteractions::commentCount($post); // int ActivityInteractions::quoteCount($post); // int // ── Boolean checks ── ActivityInteractions::hasLiked($post, $user); // bool ActivityInteractions::hasBoosted($post, $user); // bool ActivityInteractions::hasBookmarked($post, $user); // bool // ── Mutations ── ActivityInteractions::like($user, $post); // Interaction ActivityInteractions::unlike($user, $post); // void ActivityInteractions::boost($user, $post); // Interaction ActivityInteractions::unboost($user, $post); // void ActivityInteractions::bookmark($user, $post); // Interaction ActivityInteractions::unbookmark($user, $post); // void // ── Queries ── ActivityInteractions::likers($post); // Collection<Actor> ActivityInteractions::boosters($post); // Collection<Actor> ActivityInteractions::bookmarks($user); // Collection<Interaction> ActivityInteractions::recentForModel($post, InteractionType::Like, 20); // Collection
Displaying counts in your app
{{-- In a Blade view for a blog post --}} @php use DanielPetrica\LaravelActivityPub\Services\ActivityInteractions; @endphp <button> ❤️ {{ ActivityInteractions::likeCount($post) }} </button> <button> 🔁 {{ ActivityInteractions::boostCount($post) }} </button> <button> 🔖 {{ ActivityInteractions::bookmarkCount($post) }} </button>
How incoming interactions work
When a remote user likes or boosts your content, the package:
- Receives the Like/Announce activity via the inbox
- Resolves the remote actor
- Creates an
Interactionrecord (type=Like/Announce, status=Active) - Stores it alongside the Activity log record
When a remote user undoes a like/boost:
- Receives the Undo activity
- Finds the matching active Interaction
- Sets its status to
Undone(soft undo, preserves history)
Object linking
Interactions link to content via polymorphic columns:
object_type— the model class (e.g.,App\Models\Post) for local content, or'remote_url'for remote contentobject_id— the model's primary key for local content, or the full URL string for remote content
This means likeCount($post) works for both local models and remote URLs referenced in the timeline.
Favorites
The Fediverse UI includes a /fediverse/favorites page that shows all bookmarked posts. Use ActivityInteractions::bookmarks($user) to build custom favorites views.
Post Forwarding
When the first user from a remote server follows your account, the plugin can automatically forward your newest posts to populate their server with your content.
Setup
- Register your content models in the config:
// config/activitypub.php 'federatable_models' => [ \App\Models\Post::class, \App\Models\Article::class, ],
-
Each model must implement
FederatableContentContractand use theFederatesContenttrait (see above). -
Enable federation:
ACTIVITYPUB_FEDERATION_ENABLED=true
How it works
- When the first follower from a domain follows you,
ForwardPostsToNewServerJobis dispatched - It queries your registered
federatable_modelsfor the 3 newest published posts (shouldFederate() == true) - Recreates fresh
Createactivities and delivers them to the remote server's inbox - Only fires once per domain (first follower trigger)
Manual forwarding
You can also manually trigger post forwarding from the Fediverse dashboard:
- Go to
/fediverse/status - Click "Forward Posts" in the Maintenance section
- This sends your posts to all current follower servers
Pinned posts
Implement isActivityPubPinned() on your model to mark posts as pinned:
public function isActivityPubPinned(): bool { return $this->is_pinned; }
Pinned posts are displayed with a "Pinned" badge in the outbox view.
Architecture
src/
Actions/
InboxProcessor.php -- Central admission control + dispatches to handlers by activity type
Handlers/ -- 12 ActivityHandler implementations
Contracts/ -- ActorContract, FederatableContentContract,
| ActivityBuilderContract, ActorProfileContract,
| FederatedActorContract
Enums/ -- ActivityStatus, ActivityType, ActivityObjectType,
| FollowerStatus, InteractionType, InteractionStatus
Events/ -- 7 event classes
Handlers/
HandlerRegistry.php -- Registry for activity handlers (extension seam)
Helpers/
SsrfGuard.php -- HTTPS/public-IP fetch guard
DomainNormalizer.php -- Normalized domain matching
InboxLogger.php -- Rejected-request audit log
PostStats.php -- Aggregate interaction/activity counts
Http/
Controllers/ -- Actor, Inbox, Outbox, Followers, Following,
| WebFinger, NodeInfo, HostMeta, Featured, FederatedServersController
| Concerns/RespondsToAccept.php
| Fediverse/ -- Blade UI controllers (StatusController, FollowersController, ...)
Middleware/
VerifyHttpSignature.php -- Incoming signature verification (Draft Cavage + RFC 9421)
Signatures/
SignatureParser.php -- Parse Signature / Signature-Input headers
SignatureVerifier.php -- Build signing strings, verify digests/signatures
Requests/ -- InboxRequest, WebFingerRequest, ProfileUpdateRequest
Resources/ -- Actor, Activity, WebFinger resources, OrderedCollection
Jobs/
DeliverActivity.php -- Queued outbound delivery with backoff + per-inbox records
ProcessInboxActivity.php -- Queued inbound activity processing
FetchRemoteActor.php -- Remote actor resolution
PruneOldActivities.php -- Retention cleanup job
ForwardPostsToNewServerJob.php -- Forward posts to new follower's server
Models/
Actor.php -- Local actor with RSA key pair
RemoteActor.php -- Cached remote actor data
Follower.php -- Follower relationships
Following.php -- Outbound follow tracking
Activity.php -- Federated activity log
ActivityDelivery.php -- Per-inbox delivery attempts (reschedule/dead-letter)
Interaction.php -- Polymorphic interaction tracking
Report.php -- Moderation reports (incoming Flag)
InboxLog.php -- Rejected inbox-request audit trail
BlockedDomain.php -- Domain blocklist
BlockedRemoteActor.php -- Actor blocklist
Services/
ActivityPubService.php -- Main service (facade backed)
ActivityDispatcher.php -- Fan-out + inbox dispatch
ActivityRecorder.php -- Activity persistence + resolution failures
HttpSignatureService.php -- Outbound request signing
DeliveryClient.php -- Shared HTTP + signing delivery
RemoteActorResolver.php -- Fetch/upsert remote actors
WebFingerService.php -- Remote WebFinger resolution
ActivityBuilder.php -- Build ActivityStreams payloads
ActivityInteractions.php -- Static API for interaction queries
Traits/
FederatesContent.php -- Model event hooks for auto-federation
ResolvesLocalActor.php
Console/Commands/ -- 3 artisan commands
Testing
vendor/bin/pest
356 Pest tests (1029 assertions) across the Feature and Unit suites, covering actors, inbox processing, WebFinger, NodeInfo, console commands, content delivery, Mastodon spec compliance, HTTP signatures (including RFC 9421), blocking/moderation, allowlist and quotas, mentions, pagination, post forwarding, and unit-tested activity building. Both suites run by default.
Roadmap
- Tailwind CSS build step (currently CDN)
- JSON-LD compaction for Pleroma/Akkoma compatibility
- Filament-based admin dashboard
License
MIT