Add AsyncLocalStorage to LogTape configure() to fix the repeated
"Context-local storage is not configured" warning that appeared
before every Fedify log entry. Also remove unused getLogger import.
Two critical fixes for ActivityPub federation:
1. Call federation.startQueue() — without this, ctx.sendActivity() enqueues
delivery tasks but the InProcessMessageQueue never processes them, so
activities are never actually POSTed to follower inboxes.
2. Add setSharedKeyDispatcher on the shared inbox — enables Fedify to make
signed/authenticated GET requests when verifying incoming HTTP Signatures.
Servers with authorized fetch (e.g. hachyderm.io) return 401 on unsigned
requests, which prevented Fedify from fetching sender public keys and
caused all incoming activities to be rejected.
Configure LogTape to route Fedify's internal logs (federation, vocab,
delivery) to console at info level. This makes activity delivery
attempts, HTTP signature issues, and queue processing visible in
container logs.
- Add followActor() and unfollowActor() methods for sending Follow/Undo(Follow) activities
- Add shared activity-log.js utility for logging to ap_activities collection
- Log all outbound activities (syndication, follow, unfollow) with success/failure details
- Update inbox Create listener to store timeline items from followed accounts
- Add Microsub collection accessors for cross-plugin timeline integration
- Refactor inbox-listeners to use shared activity logging utility
Notes and articles syndicated to ActivityPub now include a clickable
link back to the canonical post URL at the end of the content body.
This ensures fediverse clients display a visible permalink, since the
Note url property alone is not shown inline by most implementations.
- inbox-listeners.js: Store `targetUrl` (inReplyTo) and `content` (HTML)
on Reply activities for the conversations plugin AP adapter
- activitypub-followers.njk: Fix photo property name (`src` → `url`)
to match the card component's expected interface, fixing TypeError
crash on followers page when avatars are present
- Bump to v1.0.4
The contentNegotiationRoutes getter is mounted at root / and was passing
ALL requests through Fedify, including POST requests to admin routes.
fromExpressRequest() calls Readable.toWeb(req) which consumes the body
stream, causing "response body object should not be distributed or locked"
errors when admin controllers try to read req.body.
The v1.0.2 fix only protected routesPublic (mounted at /activitypub).
This fixes the actual culprit by skipping non-GET/HEAD methods in
contentNegotiationRoutes, since content negotiation and NodeInfo are
both GET-only concerns.
POST to /admin/migrate was going through Fedify's federation.fetch()
which consumed the already-parsed request body stream, causing
"response body object should not be distributed or locked" errors.
Admin routes (/admin/*) are UI routes handled by authenticated
Express handlers, not federation endpoints.
- Return 405 for GET on inbox endpoints instead of falling through
to Indiekit's auth middleware (which redirects to login)
- Add handlers for Update (refresh follower data), Block (remove
follower), Add and Remove (Mastodon pin/unpin — ignored)
- Bump to 1.0.1
Express's app-level body parser has a 100KB default limit that
runs before any route-level overrides. A 3K-line CSV at 113KB
exceeds this. Instead of sending raw CSV, the client now extracts
handles (first column only) and sends just the array — typically
under 90KB for 3000 accounts.
The app-level Express urlencoded parser (100KB limit) runs before
route-level middleware, so overriding the limit on the route doesn't
help. Solution: POST the CSV as JSON via fetch() to a dedicated
/admin/migrate/import endpoint with its own express.json({ limit: '5mb' }).
- Import button now shows "Importing..." while working
- Results appear inline without page reload
- Failed handles shown in a collapsible details element
- Import button disabled until a file is selected
- Alias form remains a regular POST (small payload, no issue)
- Add express.urlencoded({ limit: '5mb' }) to migration POST route
to handle large CSV files (default 100KB was too small)
- Add per-handle progress logging to console for monitoring imports
- Log failed handles with reasons (WebFinger failure, no AP link, etc.)
- Show failed handles in the UI result notification
- Use error notification type when all imports fail
Multipart form uploads fail because Indiekit has no multipart parsing
middleware. Instead, read the CSV file client-side with FileReader and
submit the text content as a hidden form field. Shows file name and
line count after selection for user confidence.
- Show current alias value on the page (persists across GET/POST)
- Pre-fill alias input with current value
- Add fieldset legend and per-item hints to import checkboxes
- Add intro paragraph explaining the migration flow
- Rewrite copy to be clearer and more reassuring
- Note irreversibility of step 3 explicitly
Indiekit's endpoint-posts accesses target.options.checked directly on
syndicator objects. Upstream syndicators are class instances with
this.options from the constructor. Our plain-object syndicator lacked
this property, causing a 500 TypeError on post creation.
The i18n system resolves dots as nested path separators, but migrate
keys were flat strings with dots in the key name. Restructure migrate
as a nested object with a title sub-key.
Rename all views to activitypub-*.njk to prevent collisions with other
plugins that have dashboard.njk (podroll). Fix all new Date() calls to
use .toISOString() per Indiekit convention. Add try-catch in syndicator
to prevent delivery failures from crashing post creation.
Express 5 uses path-to-regexp v8 which requires named wildcards.
Bare "*" is no longer valid — use "{*path}" instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Indiekit's getInstalledPlugins() assigns plugin.filePath via require.resolve().
Our getter made the property read-only, causing:
TypeError: Cannot set property filePath which has only a getter
Let Indiekit set it instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements full ActivityPub federation as an Indiekit plugin:
- Actor document (Person) with RSA key pair for HTTP Signatures
- WebFinger discovery (acct:rick@rmendes.net)
- Inbox: handles Follow, Undo, Like, Announce, Create, Delete, Move
- Outbox: serves published posts as ActivityStreams 2.0
- Content negotiation: AS2 JSON for AP clients, passthrough for browsers
- JF2-to-AS2 converter for all Indiekit post types
- Syndicator integration (pre-ticked checkbox for delivery to followers)
- Mastodon migration: alias config, CSV import for followers/following
- Admin UI: dashboard, followers, following, activity log, migration page
- Data retention: configurable TTL on activities, optional raw JSON storage
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>