From 35ab840a561b945219e9ab37484fb056b64e4b1b Mon Sep 17 00:00:00 2001 From: Ricardo Date: Thu, 26 Mar 2026 17:33:28 +0100 Subject: [PATCH] feat: upgrade Fedify to 2.1.0 + implement 5 FEPs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fedify 2.1.0 upgrade: - Upgrade @fedify/fedify, @fedify/redis, @fedify/debugger to ^2.1.0 - Remove as:Endpoints type-stripping workaround (fixed upstream, fedify#576) - Wire onUnverifiedActivity handler for Delete from actors with gone keys FEP implementations: - FEP-5feb: Add indexable + discoverable to actor (search indexing consent) - FEP-f1d5/0151: Enrich NodeInfo 2.1 with metadata, staff accounts, repo info - FEP-4f05: Soft delete with Tombstone — deleted posts serve 410 + Tombstone JSON-LD with formerType, published, deleted timestamps. New ap_tombstones collection + lib/storage/tombstones.js - FEP-3b86: Activity Intents — WebFinger links for Follow/Create/Like/Announce intents, authorize_interaction routes by intent parameter - FEP-8fcf: Collection Sync outbound via Fedify syncCollection (documented that receiving side is not yet implemented) --- CLAUDE.md | 16 ++- README.md | 22 ++-- index.js | 32 +++++ lib/controllers/authorize-interaction.js | 32 +++-- lib/federation-bridge.js | 6 - lib/federation-setup.js | 95 ++++++++++++++- lib/init-indexes.js | 6 + lib/storage/tombstones.js | 52 +++++++++ package-lock.json | 142 +++++++++-------------- package.json | 8 +- 10 files changed, 291 insertions(+), 120 deletions(-) create mode 100644 lib/storage/tombstones.js diff --git a/CLAUDE.md b/CLAUDE.md index 1051728..d5a6d54 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -161,6 +161,7 @@ processing pipeline via item-processing.js: | `ap_blocked_servers` | Blocked server domains | `hostname` (unique) | | `ap_key_freshness` | Remote actor key verification timestamps | `actorUrl` (unique), `lastVerifiedAt` | | `ap_inbox_queue` | Persistent async inbox queue | `activityId`, `status`, `enqueuedAt` | +| `ap_tombstones` | Tombstone records for soft-deleted posts (FEP-4f05) | `url` (unique) | | `ap_oauth_apps` | Mastodon API client registrations | `clientId` (unique), `clientSecret`, `redirectUris` | | `ap_oauth_tokens` | OAuth2 authorization codes + access tokens | `code` (unique sparse), `accessToken` (unique sparse) | | `ap_markers` | Read position markers (Mastodon API) | `userId`, `timeline` | @@ -219,12 +220,11 @@ Express 5 removed the `"back"` magic keyword from `response.redirect()`. It's tr JSON-LD compaction collapses single-element arrays to plain objects. Mastodon's `update_account_fields` checks `attachment.is_a?(Array)` and silently skips if it's not an array. `sendFedifyResponse()` in `federation-bridge.js` forces `attachment` to always be an array. -### 10. WORKAROUND: Endpoints `as:Endpoints` Type Stripping +### 10. REMOVED: Endpoints `as:Endpoints` Type Stripping (Fixed in Fedify 2.1.0) -**File:** `lib/federation-bridge.js` (in `sendFedifyResponse()`) **Upstream issue:** [fedify#576](https://github.com/fedify-dev/fedify/issues/576) — FIXED in Fedify 2.1.0 -**Workaround:** `delete json.endpoints.type` strips the invalid `"type": "as:Endpoints"` from actor JSON. -**Remove when:** Upgrading to Fedify ≥ 2.1.0. +**Previous workaround** in `federation-bridge.js` — **REMOVED**. +Fedify 2.1.0 now omits the invalid `"type": "as:Endpoints"` from serialized actor JSON. No workaround needed. ### 11. KNOWN ISSUE: PropertyValue Attachment Type Validation @@ -624,6 +624,14 @@ curl -s "https://rmendes.net/nodeinfo/2.1" | jq . - `@_followback@tags.pub` does not send Follow activities back despite accepting ours - Both suggest tags.pub's outbound delivery is broken — zero inbound requests from `activitypub-bot` user-agent have been observed +### 37. Unverified Delete Activities (Fedify 2.1.0+) + +`onUnverifiedActivity()` in `federation-setup.js` handles Delete activities from actors whose signing keys return 404/410. When an account is permanently deleted, the remote server sends a Delete activity but the actor's key endpoint is gone, so HTTP Signature verification fails. The handler checks `reason.type === "keyFetchError"` with status 404/410, cleans up the actor's data (followers, timeline items, notifications), and returns 202 Accepted. + +### 38. FEP-8fcf Collection Synchronization — Outbound Only + +We pass `syncCollection: true` to Fedify's `sendActivity()` for outbound activities, which attaches `Collection-Synchronization` headers with partial follower digests (XOR'd SHA-256 hashes). However, the **receiving side** (parsing inbound headers, digest comparison, reconciliation) is NOT implemented by Fedify or by us. Remote servers that send Collection-Synchronization headers to us will have them ignored. Full FEP-8fcf compliance would require a `/followers-sync` endpoint and a reconciliation scheduler. + ## Form Handling Convention Two form patterns are used in this plugin. New forms should follow the appropriate pattern. diff --git a/README.md b/README.md index 69c644e..39a128c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # @rmdes/indiekit-endpoint-activitypub -ActivityPub federation endpoint for [Indiekit](https://getindiekit.com), built on [Fedify](https://fedify.dev) 2.0. Makes your IndieWeb site a full fediverse actor — discoverable, followable, and interactive from Mastodon, Misskey, Pixelfed, and any ActivityPub-compatible platform. Includes a Mastodon-compatible Client API so you can use Phanpy, Elk, Moshidon, Fedilab, and other Mastodon clients with your own AP instance. +ActivityPub federation endpoint for [Indiekit](https://getindiekit.com), built on [Fedify](https://fedify.dev) 2.1. Makes your IndieWeb site a full fediverse actor — discoverable, followable, and interactive from Mastodon, Misskey, Pixelfed, and any ActivityPub-compatible platform. Includes a Mastodon-compatible Client API so you can use Phanpy, Elk, Moshidon, Fedilab, and other Mastodon clients with your own AP instance. ## Features @@ -109,10 +109,18 @@ ActivityPub federation endpoint for [Indiekit](https://getindiekit.com), built o - Follower and following lists with source tracking - Federation management page with moderation overview (blocked servers, blocked accounts, muted) +**Standards Compliance** +- FEP-5feb: Search Indexing Consent — actor advertises `indexable` and `discoverable` properties +- FEP-f1d5/0151: Enhanced NodeInfo 2.1 — rich metadata including software repository, node name, staff accounts +- FEP-4f05: Soft Delete with Tombstone — deleted posts return 410 with Tombstone JSON-LD including `formerType` and timestamps +- FEP-3b86: Activity Intents — WebFinger links for Follow, Create, Like, Announce intents with authorize_interaction routing +- FEP-8fcf: Collection Synchronization — outbound follower digest headers via Fedify `syncCollection` +- FEP-044f: Quote Posts — rendered as embedded cards (via Fedify's `quoteUrl` support) + ## Requirements - [Indiekit](https://getindiekit.com) v1.0.0-beta.25+ -- [Fedify](https://fedify.dev) 2.0+ (bundled as dependency) +- [Fedify](https://fedify.dev) 2.1+ (bundled as dependency) - Node.js >= 22 - MongoDB (used by Indiekit) - Redis (recommended for production delivery queue; in-process queue available for development) @@ -322,6 +330,7 @@ The plugin creates these collections automatically: | `ap_blocked_servers` | Blocked server domains (instance-level blocks) | | `ap_key_freshness` | Tracks when remote actor keys were last verified | | `ap_inbox_queue` | Persistent async inbox processing queue | +| `ap_tombstones` | Tombstone records for soft-deleted posts (FEP-4f05) | | `ap_oauth_apps` | Mastodon API client app registrations | | `ap_oauth_tokens` | OAuth2 authorization codes and access tokens | | `ap_markers` | Read position markers for Mastodon API clients | @@ -342,7 +351,7 @@ Categories are converted to `Hashtag` tags. Bookmarks include a bookmark emoji a ## Fedify Workarounds and Implementation Notes -This plugin uses [Fedify](https://fedify.dev) 2.0 but carries several workarounds for issues in Fedify or its Express integration. These are documented here so they can be revisited when Fedify upgrades. +This plugin uses [Fedify](https://fedify.dev) 2.1 but carries several workarounds for issues in Fedify or its Express integration. These are documented here so they can be revisited when Fedify upgrades. ### Custom Express Bridge (instead of `@fedify/express`) @@ -364,14 +373,11 @@ Mastodon's `update_account_fields` checks `attachment.is_a?(Array)` and silently **Revisit when:** Fedify adds an option to preserve arrays during JSON-LD serialization, or Mastodon fixes their array check. -### Endpoints `as:Endpoints` Type Stripping +### Endpoints `as:Endpoints` Type Stripping — REMOVED -**File:** `lib/federation-bridge.js` (in `sendFedifyResponse()`) **Upstream issue:** [fedify#576](https://github.com/fedify-dev/fedify/issues/576) — FIXED in Fedify 2.1.0 -Fedify serializes the `endpoints` object with `"type": "as:Endpoints"`, which is not a valid ActivityStreams type. browser.pub rejects this. The bridge strips the `type` field from the `endpoints` object before sending. - -**Remove when:** Upgrading to Fedify ≥ 2.1.0. +This workaround has been removed. Fedify 2.1.0 now omits the invalid `"type": "as:Endpoints"` from serialized actor JSON. ### PropertyValue Attachment Type (Known Issue) diff --git a/index.js b/index.js index 7dc9631..9f43818 100644 --- a/index.js +++ b/index.js @@ -435,6 +435,20 @@ export default class ActivityPubEndpoint { }); if (!post || post.properties?.deleted) { + // FEP-4f05: Serve Tombstone for deleted posts + const { getTombstone } = await import("./lib/storage/tombstones.js"); + const tombstone = await getTombstone(self._collections, requestUrl); + if (tombstone) { + res.status(410).set("Content-Type", "application/activity+json").json({ + "@context": "https://www.w3.org/ns/activitystreams", + type: "Tombstone", + id: requestUrl, + formerType: tombstone.formerType, + published: tombstone.published || undefined, + deleted: tombstone.deleted, + }); + return; + } return next(); } @@ -811,6 +825,21 @@ export default class ActivityPubEndpoint { * @param {string} url - Full URL of the deleted post */ async delete(url) { + // Record tombstone for FEP-4f05 + try { + const { addTombstone } = await import("./lib/storage/tombstones.js"); + const postsCol = this._collections.posts; + const post = postsCol ? await postsCol.findOne({ "properties.url": url }) : null; + await addTombstone(this._collections, { + url, + formerType: post?.properties?.["post-type"] === "article" ? "Article" : "Note", + published: post?.properties?.published || null, + deleted: new Date().toISOString(), + }); + } catch (error) { + console.warn(`[ActivityPub] Tombstone creation failed for ${url}: ${error.message}`); + } + await this.broadcastDelete(url).catch((err) => console.warn(`[ActivityPub] broadcastDelete failed for ${url}: ${err.message}`) ); @@ -927,6 +956,8 @@ export default class ActivityPubEndpoint { Indiekit.addCollection("ap_oauth_apps"); Indiekit.addCollection("ap_oauth_tokens"); Indiekit.addCollection("ap_markers"); + // Tombstones for soft-deleted posts (FEP-4f05) + Indiekit.addCollection("ap_tombstones"); // Store collection references (posts resolved lazily) const indiekitCollections = Indiekit.collections; @@ -964,6 +995,7 @@ export default class ActivityPubEndpoint { ap_oauth_apps: indiekitCollections.get("ap_oauth_apps"), ap_oauth_tokens: indiekitCollections.get("ap_oauth_tokens"), ap_markers: indiekitCollections.get("ap_markers"), + ap_tombstones: indiekitCollections.get("ap_tombstones"), get posts() { return indiekitCollections.get("posts"); }, diff --git a/lib/controllers/authorize-interaction.js b/lib/controllers/authorize-interaction.js index 2d71d8a..8196e70 100644 --- a/lib/controllers/authorize-interaction.js +++ b/lib/controllers/authorize-interaction.js @@ -2,18 +2,21 @@ * Authorize Interaction controller — handles the remote follow / authorize * interaction flow for ActivityPub federation. * - * When a remote server (WordPress AP, Misskey, etc.) discovers our WebFinger - * subscribe template, it redirects the user here with ?uri={actorOrPostUrl}. + * Supports: + * - OStatus subscribe template (legacy remote follow via ?uri=...) + * - FEP-3b86 Activity Intents (via ?uri=...&intent=follow|create|like|announce) * * Flow: * 1. Missing uri → render error page * 2. Unauthenticated → redirect to login, then back here - * 3. Authenticated → redirect to the reader's remote profile page + * 3. Authenticated → route to appropriate page based on intent */ export function authorizeInteractionController(plugin) { return async (req, res) => { const uri = req.query.uri || req.query.acct; + const intent = req.query.intent || ""; + if (!uri) { return res.status(400).render("activitypub-authorize-interaction", { title: "Authorize Interaction", @@ -29,17 +32,28 @@ export function authorizeInteractionController(plugin) { // then back to this page after auth const session = req.session; if (!session?.access_token) { - const returnUrl = `${plugin.options.mountPath}/authorize_interaction?uri=${encodeURIComponent(uri)}`; + const params = `uri=${encodeURIComponent(uri)}${intent ? `&intent=${intent}` : ""}`; + const returnUrl = `${plugin.options.mountPath}/authorize_interaction?${params}`; return res.redirect( `/session/login?redirect=${encodeURIComponent(returnUrl)}`, ); } - // Authenticated — redirect to the remote profile viewer in our reader - // which already has follow/unfollow/like/boost functionality + const mp = plugin.options.mountPath; const encodedUrl = encodeURIComponent(resource); - return res.redirect( - `${plugin.options.mountPath}/admin/reader/profile?url=${encodedUrl}`, - ); + + // Route based on intent (FEP-3b86) + switch (intent) { + case "follow": + return res.redirect(`${mp}/admin/reader/profile?url=${encodedUrl}`); + case "create": + return res.redirect(`${mp}/admin/reader/compose?replyTo=${encodedUrl}`); + case "like": + case "announce": + return res.redirect(`${mp}/admin/reader/post?url=${encodedUrl}`); + default: + // Default: resolve to remote profile page + return res.redirect(`${mp}/admin/reader/profile?url=${encodedUrl}`); + } }; } diff --git a/lib/federation-bridge.js b/lib/federation-bridge.js index 5b39051..1295745 100644 --- a/lib/federation-bridge.js +++ b/lib/federation-bridge.js @@ -89,12 +89,6 @@ async function sendFedifyResponse(res, response, request) { if (json.attachment && !Array.isArray(json.attachment)) { json.attachment = [json.attachment]; } - // WORKAROUND: Fedify serializes endpoints with "type": "as:Endpoints" - // which is not a valid AS2 type. The endpoints object should be a plain - // object with just sharedInbox/proxyUrl etc. Strip the invalid type. - if (json.endpoints && json.endpoints.type) { - delete json.endpoints.type; - } const patched = JSON.stringify(json); res.setHeader("content-length", Buffer.byteLength(patched)); res.end(patched); diff --git a/lib/federation-setup.js b/lib/federation-setup.js index 3fd9269..acef28b 100644 --- a/lib/federation-setup.js +++ b/lib/federation-setup.js @@ -286,10 +286,48 @@ export function setupFederation(options) { // Add OStatus subscribe template so remote servers (WordPress AP, Misskey, etc.) // can redirect users to our authorize_interaction page for remote follow. federation.setWebFingerLinksDispatcher((_ctx, _resource) => { + const interactionBase = `${publicationUrl}${mountPath.replace(/^\//, "")}/authorize_interaction`; return [ + // OStatus subscribe template (legacy remote follow) { rel: "http://ostatus.org/schema/1.0/subscribe", - template: `${publicationUrl}${mountPath.replace(/^\//, "")}/authorize_interaction?uri={uri}`, + template: `${interactionBase}?uri={uri}`, + }, + // FEP-3b86 Activity Intents — Follow + { + rel: "https://w3id.org/fep/3b86", + template: `${interactionBase}?uri={uri}&intent=follow`, + properties: { + "https://w3id.org/fep/3b86#intent": + "https://www.w3.org/ns/activitystreams#Follow", + }, + }, + // FEP-3b86 Activity Intents — Create (reply) + { + rel: "https://w3id.org/fep/3b86", + template: `${interactionBase}?uri={uri}&intent=create`, + properties: { + "https://w3id.org/fep/3b86#intent": + "https://www.w3.org/ns/activitystreams#Create", + }, + }, + // FEP-3b86 Activity Intents — Like + { + rel: "https://w3id.org/fep/3b86", + template: `${interactionBase}?uri={uri}&intent=like`, + properties: { + "https://w3id.org/fep/3b86#intent": + "https://www.w3.org/ns/activitystreams#Like", + }, + }, + // FEP-3b86 Activity Intents — Announce (boost) + { + rel: "https://w3id.org/fep/3b86", + template: `${interactionBase}?uri={uri}&intent=announce`, + properties: { + "https://w3id.org/fep/3b86#intent": + "https://www.w3.org/ns/activitystreams#Announce", + }, }, ]; }); @@ -305,6 +343,43 @@ export function setupFederation(options) { storeRawActivities, }); + // Handle Delete activities from actors whose signing keys are gone. + // When an account is deleted, the remote server sends Delete but the + // actor's key endpoint returns 404/410, so signature verification fails. + // Fedify 2.1.0 lets us inspect these instead of auto-rejecting. + inboxChain + .onUnverifiedActivity(async (_ctx, activity, reason) => { + // Handle Delete activities from actors whose signing keys are gone. + // When an account is deleted, the remote server sends Delete but the + // actor's key endpoint returns 404/410, so signature verification fails. + // Fedify 2.1.0 lets us inspect these instead of auto-rejecting. + if (reason.type === "keyFetchError") { + const status = reason.result?.status; + if (status === 404 || status === 410) { + const actorId = activity.actorId?.href; + if (actorId) { + const activityType = activity.constructor?.name || ""; + if (activityType === "Delete") { + console.info( + `[ActivityPub] Processing unverified Delete from ${actorId} (key ${status})`, + ); + try { + await collections.ap_followers.deleteOne({ actorUrl: actorId }); + await collections.ap_timeline.deleteMany({ "author.url": actorId }); + await collections.ap_notifications.deleteMany({ actorUrl: actorId }); + console.info(`[ActivityPub] Cleaned up data for deleted actor ${actorId}`); + } catch (error) { + console.warn(`[ActivityPub] Cleanup for ${actorId} failed: ${error.message}`); + } + return new Response(null, { status: 202 }); + } + } + } + } + // All other unverified activities: return null for default 401 + return null; + }); + // Enable authenticated fetches for the shared inbox. // Without this, Fedify can't verify incoming HTTP Signatures from servers // that require authorized fetch (e.g. hachyderm.io returns 401 on unsigned GETs). @@ -337,17 +412,33 @@ export function setupFederation(options) { ? await collections.posts.countDocuments() : 0; + const profile = await getProfile(collections); + return { software: { name: "indiekit", version: softwareVersion, + repository: new URL("https://github.com/getindiekit/indiekit"), + homepage: new URL("https://getindiekit.com"), }, protocols: ["activitypub"], + services: { + inbound: [], + outbound: [], + }, + openRegistrations: false, usage: { users: { total: 1, activeMonth: 1, activeHalfyear: 1 }, localPosts: postsCount, localComments: 0, }, + metadata: { + nodeName: profile.name || handle, + nodeDescription: profile.summary || "", + staffAccounts: [ + `${publicationUrl}${mountPath.replace(/^\//, "")}/users/${handle}`, + ], + }, }; }); @@ -740,6 +831,8 @@ export async function buildPersonActor( featuredTags: ctx.getFeaturedTagsUri(identifier), endpoints: new Endpoints({ sharedInbox: ctx.getInboxUri() }), manuallyApprovesFollowers: profile.manuallyApprovesFollowers || false, + indexable: true, + discoverable: true, }; if (profile.summary) { diff --git a/lib/init-indexes.js b/lib/init-indexes.js index f7a108a..607f287 100644 --- a/lib/init-indexes.js +++ b/lib/init-indexes.js @@ -244,6 +244,12 @@ export function createIndexes(collections, options) { { userId: 1, timeline: 1 }, { unique: true, background: true }, ); + + // Tombstone indexes (FEP-4f05) + collections.ap_tombstones?.createIndex( + { url: 1 }, + { unique: true, background: true }, + ); } catch { // Index creation failed — collections not yet available. // Indexes already exist from previous startups; non-fatal. diff --git a/lib/storage/tombstones.js b/lib/storage/tombstones.js new file mode 100644 index 0000000..79d6cfe --- /dev/null +++ b/lib/storage/tombstones.js @@ -0,0 +1,52 @@ +/** + * Tombstone storage for soft-deleted posts (FEP-4f05). + * When a post is deleted, a tombstone record is created so remote servers + * fetching the URL get a proper Tombstone response instead of 404. + * @module storage/tombstones + */ + +/** + * Record a tombstone for a deleted post. + * @param {object} collections - MongoDB collections + * @param {object} data - { url, formerType, published, deleted } + */ +export async function addTombstone(collections, { url, formerType, published, deleted }) { + const { ap_tombstones } = collections; + if (!ap_tombstones) return; + + await ap_tombstones.updateOne( + { url }, + { + $set: { + url, + formerType: formerType || "Note", + published: published || null, + deleted: deleted || new Date().toISOString(), + }, + }, + { upsert: true }, + ); +} + +/** + * Remove a tombstone (post re-published). + * @param {object} collections - MongoDB collections + * @param {string} url - Post URL + */ +export async function removeTombstone(collections, url) { + const { ap_tombstones } = collections; + if (!ap_tombstones) return; + await ap_tombstones.deleteOne({ url }); +} + +/** + * Look up a tombstone by URL. + * @param {object} collections - MongoDB collections + * @param {string} url - Post URL + * @returns {Promise} Tombstone record or null + */ +export async function getTombstone(collections, url) { + const { ap_tombstones } = collections; + if (!ap_tombstones) return null; + return ap_tombstones.findOne({ url }); +} diff --git a/package-lock.json b/package-lock.json index 455819b..72fe5a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "@rmdes/indiekit-endpoint-activitypub", - "version": "3.8.7", + "version": "3.9.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rmdes/indiekit-endpoint-activitypub", - "version": "3.8.7", + "version": "3.9.4", "license": "MIT", "dependencies": { - "@fedify/debugger": "^2.0.0", - "@fedify/fedify": "^2.0.0", - "@fedify/redis": "^2.0.0", + "@fedify/debugger": "^2.1.0", + "@fedify/fedify": "^2.1.0", + "@fedify/redis": "^2.1.0", "@js-temporal/polyfill": "^0.5.0", "express": "^5.0.0", "express-rate-limit": "^7.5.1", @@ -515,12 +515,12 @@ } }, "node_modules/@fedify/debugger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fedify/debugger/-/debugger-2.0.0.tgz", - "integrity": "sha512-2ZGXKQa+BqAO9F+tuZcDiLHDr193cUqKlnX1Z7yDn1ICPL1gPxxwgKAa1b540pBBWSfDCXBSrJlZ3DYK9f52GA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fedify/debugger/-/debugger-2.1.0.tgz", + "integrity": "sha512-4s3L3/NkofZCUXR1jADq5ukSbWybpWqgqF4TEg3PHxlXkC3bT/LI4as8zFxTpkkCvM5fE6tXCi5z56rJ9tXzag==", "dependencies": { "@js-temporal/polyfill": "^0.5.1", - "@logtape/logtape": "^2.0.0", + "@logtape/logtape": "^2.0.5", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^2.5.0", "@opentelemetry/core": "^2.5.0", @@ -528,24 +528,24 @@ "hono": "^4.0.0" }, "peerDependencies": { - "@fedify/fedify": "^2.0.0" + "@fedify/fedify": "^2.1.0" } }, "node_modules/@fedify/fedify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fedify/fedify/-/fedify-2.0.0.tgz", - "integrity": "sha512-ZejBFXfILViuIHYhI1BWEk1Pewt9hNO70u6GVaWYKWwU3IVc1/HEsGA/kK9IxJKYZBPqLcCtoI2BZfeOg8I/Hg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fedify/fedify/-/fedify-2.1.0.tgz", + "integrity": "sha512-CMGlL9HEaqyuQL4Ma0Jv+9/QgtLjj+HLmjNrg1e/WUQrEwZg9p5WYKk4iNKXF4aIG3XJkAv5UGJlHKF09HifNA==", "funding": [ "https://opencollective.com/fedify", "https://github.com/sponsors/dahlia" ], "license": "MIT", "dependencies": { - "@fedify/vocab": "2.0.0", - "@fedify/vocab-runtime": "2.0.0", - "@fedify/webfinger": "2.0.0", + "@fedify/vocab": "2.1.0", + "@fedify/vocab-runtime": "2.1.0", + "@fedify/webfinger": "2.1.0", "@js-temporal/polyfill": "^0.5.1", - "@logtape/logtape": "^2.0.0", + "@logtape/logtape": "^2.0.5", "@opentelemetry/api": "^1.9.0", "@opentelemetry/core": "^2.5.0", "@opentelemetry/sdk-trace-base": "^2.5.0", @@ -554,7 +554,6 @@ "es-toolkit": "1.43.0", "json-canon": "^1.0.1", "jsonld": "^9.0.0", - "multicodec": "^3.2.1", "structured-field-values": "^2.0.4", "uri-template-router": "^1.0.0", "url-template": "^3.1.1", @@ -567,9 +566,9 @@ } }, "node_modules/@fedify/redis": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fedify/redis/-/redis-2.0.0.tgz", - "integrity": "sha512-WHUhEHZ0BAbcmITgDTdspeolfl4bHpRx+BlmdVRsGScaoQODvvohBfRcTCGbMz2RfQmkhz4l297Nk8Nlqyvg7Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fedify/redis/-/redis-2.1.0.tgz", + "integrity": "sha512-Fqud46FIEBFXDFad029rS46ZlVlWZU2zT6yhBs63jtat7QwMIHDSisizvoVyky4a41TX0ItBNqiAdYELLv/0NQ==", "funding": [ "https://opencollective.com/fedify", "https://github.com/sponsors/dahlia" @@ -577,34 +576,33 @@ "license": "MIT", "dependencies": { "@js-temporal/polyfill": "^0.5.1", - "@logtape/logtape": "^2.0.0" + "@logtape/logtape": "^2.0.5" }, "peerDependencies": { - "@fedify/fedify": "^2.0.0", + "@fedify/fedify": "^2.1.0", "ioredis": "^5.8.2" } }, "node_modules/@fedify/vocab": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fedify/vocab/-/vocab-2.0.0.tgz", - "integrity": "sha512-sjw51UltqefhGPxkcSrnwdmBHO5Zm3hOlOHFvzsLg1pbl53KKETcJ8TG6OcMaD0ZiaUqFVKkGAlpDG3FD9O4nw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fedify/vocab/-/vocab-2.1.0.tgz", + "integrity": "sha512-tGCgo8kCj6Zwf1JxYsXtEwReujzgitndf59Pdo1BY21UgpAlAe0daY8vdpRM+NybZ4JbOBtM4bH473LVtJlVEA==", "funding": [ "https://opencollective.com/fedify", "https://github.com/sponsors/dahlia" ], "license": "MIT", "dependencies": { - "@fedify/vocab-runtime": "2.0.0", - "@fedify/vocab-tools": "2.0.0", - "@fedify/webfinger": "2.0.0", + "@fedify/vocab-runtime": "2.1.0", + "@fedify/vocab-tools": "2.1.0", + "@fedify/webfinger": "2.1.0", "@js-temporal/polyfill": "^0.5.1", - "@logtape/logtape": "^2.0.0", + "@logtape/logtape": "^2.0.5", "@multiformats/base-x": "^4.0.1", "@opentelemetry/api": "^1.9.0", "asn1js": "^3.0.6", "es-toolkit": "1.43.0", "jsonld": "^9.0.0", - "multicodec": "^3.2.1", "pkijs": "^3.3.3" }, "engines": { @@ -614,21 +612,21 @@ } }, "node_modules/@fedify/vocab-runtime": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fedify/vocab-runtime/-/vocab-runtime-2.0.0.tgz", - "integrity": "sha512-Cdcbhki75kBi20Eq0Dkpf1XXXVKVwnOzK4O/b4MKH6kmUPEcVyNVb9L3+ZZElViE+kAZ0bmYLlFNp42E7mjjLQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fedify/vocab-runtime/-/vocab-runtime-2.1.0.tgz", + "integrity": "sha512-rISQFJbuRrt1OX9yG+xVUn7DwBTajpOOvy5jdx2ZuRUMvtlD7bgDEUSSS5a7pFuYliKVbR5ZFk6BPAkJC1OnAw==", "funding": [ "https://opencollective.com/fedify", "https://github.com/sponsors/dahlia" ], "license": "MIT", "dependencies": { - "@logtape/logtape": "^2.0.0", + "@logtape/logtape": "^2.0.5", "@multiformats/base-x": "^4.0.1", "@opentelemetry/api": "^1.9.0", "asn1js": "^3.0.6", "byte-encodings": "^1.0.11", - "multicodec": "^3.2.1", + "jsonld": "^9.0.0", "pkijs": "^3.3.3" }, "engines": { @@ -638,9 +636,9 @@ } }, "node_modules/@fedify/vocab-tools": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fedify/vocab-tools/-/vocab-tools-2.0.0.tgz", - "integrity": "sha512-AQ1zjGt4wjaTjTOHCgDroNITiSeZ1z99ygNEkmukg5EwgjF7+DVoPV+OTrmVVW/2A6t1blJzfOS0wrlzsn5lqQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fedify/vocab-tools/-/vocab-tools-2.1.0.tgz", + "integrity": "sha512-Gn07LbMoDRVDjklDZH9y/fZ2nwH7ryjillgLpw8qsbjUeVaTViR1Oz/oG2N7S13UqyKttYPzK8hH/utKr1LbXg==", "funding": [ "https://opencollective.com/fedify", "https://github.com/sponsors/dahlia" @@ -659,17 +657,17 @@ } }, "node_modules/@fedify/webfinger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fedify/webfinger/-/webfinger-2.0.0.tgz", - "integrity": "sha512-D66ZdyUUM9BNQU7OGEWmNLU+FIHApUiYnEogOa5oNj9fy0vjOfxm9hynA+0SCm3emrKrLt6Gm120Re+T5Us5Yg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fedify/webfinger/-/webfinger-2.1.0.tgz", + "integrity": "sha512-G5yrCPw1oWijvkGOMjWZFOWohmljQ4pmHgK7BuESshcAizpKRU0t5GcOGMyPzcNrO4+diaddGNg48GFzZ9mK/g==", "funding": [ "https://opencollective.com/fedify", "https://github.com/sponsors/dahlia" ], "license": "MIT", "dependencies": { - "@fedify/vocab-runtime": "2.0.0", - "@logtape/logtape": "^2.0.0", + "@fedify/vocab-runtime": "2.1.0", + "@logtape/logtape": "^2.0.5", "@opentelemetry/api": "^1.9.0", "es-toolkit": "1.43.0" }, @@ -1264,9 +1262,9 @@ } }, "node_modules/@logtape/logtape": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@logtape/logtape/-/logtape-2.0.2.tgz", - "integrity": "sha512-cveUBLbCMFkvkLycP/2vNWvywl47JcJbazHIju94/QNGboZ/jyYAgZIm0ZXezAOx3eIz8OG1EOZ5CuQP3+2FQg==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@logtape/logtape/-/logtape-2.0.5.tgz", + "integrity": "sha512-UizDkh20ZPJVOddRxG1F77WhHdlNl/sbQgoO8T534R7XvUBMAJ9En9f35u+meW2tRsNLvjz6R87Zanwf53tspQ==", "funding": [ "https://github.com/sponsors/dahlia" ], @@ -2970,23 +2968,6 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/multicodec": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-3.2.1.tgz", - "integrity": "sha512-+expTPftro8VAW8kfvcuNNNBgb9gPeNYV9dn+z1kJRWF2vih+/S79f2RVeIwmrJBUJ6NT9IUPWnZDQvegEh5pw==", - "deprecated": "This module has been superseded by the multiformats module", - "license": "MIT", - "dependencies": { - "uint8arrays": "^3.0.0", - "varint": "^6.0.0" - } - }, - "node_modules/multiformats": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", - "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", - "license": "(Apache-2.0 AND MIT)" - }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -3157,9 +3138,9 @@ "license": "ISC" }, "node_modules/pkijs": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.3.3.tgz", - "integrity": "sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz", + "integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==", "license": "BSD-3-Clause", "dependencies": { "@noble/hashes": "1.4.0", @@ -3656,19 +3637,10 @@ "license": "MIT", "peer": true }, - "node_modules/uint8arrays": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.1.1.tgz", - "integrity": "sha512-+QJa8QRnbdXVpHYjLoTpJIdCTiw9Ir62nocClWuXIq2JIh4Uta0cQsTSpFL678p2CN8B+XSApwcU+pQEqVpKWg==", - "license": "MIT", - "dependencies": { - "multiformats": "^9.4.2" - } - }, "node_modules/undici": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", - "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", + "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", "license": "MIT", "engines": { "node": ">=18.17" @@ -3741,12 +3713,6 @@ "integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==", "license": "MIT" }, - "node_modules/varint": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz", - "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==", - "license": "MIT" - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -3785,9 +3751,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index 1e5fa18..b560a14 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rmdes/indiekit-endpoint-activitypub", - "version": "3.9.4", + "version": "3.10.0", "description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.", "keywords": [ "indiekit", @@ -37,9 +37,9 @@ "url": "https://github.com/rmdes/indiekit-endpoint-activitypub/issues" }, "dependencies": { - "@fedify/debugger": "^2.0.0", - "@fedify/fedify": "^2.0.0", - "@fedify/redis": "^2.0.0", + "@fedify/debugger": "^2.1.0", + "@fedify/fedify": "^2.1.0", + "@fedify/redis": "^2.1.0", "@js-temporal/polyfill": "^0.5.0", "express": "^5.0.0", "express-rate-limit": "^7.5.1",