feat: upgrade Fedify to 2.1.0 + implement 5 FEPs

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)
This commit is contained in:
Ricardo
2026-03-26 17:33:28 +01:00
parent 47fe21c681
commit 35ab840a56
10 changed files with 291 additions and 120 deletions

View File

@@ -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.

View File

@@ -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)

View File

@@ -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");
},

View File

@@ -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}`);
}
};
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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.

52
lib/storage/tombstones.js Normal file
View File

@@ -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<object|null>} Tombstone record or null
*/
export async function getTombstone(collections, url) {
const { ap_tombstones } = collections;
if (!ap_tombstones) return null;
return ap_tombstones.findOne({ url });
}

142
package-lock.json generated
View File

@@ -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"

View File

@@ -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",