mirror of
https://github.com/svemagie/indiekit-endpoint-activitypub.git
synced 2026-04-02 15:44:58 +02:00
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:
16
CLAUDE.md
16
CLAUDE.md
@@ -161,6 +161,7 @@ processing pipeline via item-processing.js:
|
|||||||
| `ap_blocked_servers` | Blocked server domains | `hostname` (unique) |
|
| `ap_blocked_servers` | Blocked server domains | `hostname` (unique) |
|
||||||
| `ap_key_freshness` | Remote actor key verification timestamps | `actorUrl` (unique), `lastVerifiedAt` |
|
| `ap_key_freshness` | Remote actor key verification timestamps | `actorUrl` (unique), `lastVerifiedAt` |
|
||||||
| `ap_inbox_queue` | Persistent async inbox queue | `activityId`, `status`, `enqueuedAt` |
|
| `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_apps` | Mastodon API client registrations | `clientId` (unique), `clientSecret`, `redirectUris` |
|
||||||
| `ap_oauth_tokens` | OAuth2 authorization codes + access tokens | `code` (unique sparse), `accessToken` (unique sparse) |
|
| `ap_oauth_tokens` | OAuth2 authorization codes + access tokens | `code` (unique sparse), `accessToken` (unique sparse) |
|
||||||
| `ap_markers` | Read position markers (Mastodon API) | `userId`, `timeline` |
|
| `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.
|
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
|
**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.
|
**Previous workaround** in `federation-bridge.js` — **REMOVED**.
|
||||||
**Remove when:** Upgrading to Fedify ≥ 2.1.0.
|
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
|
### 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
|
- `@_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
|
- 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
|
## Form Handling Convention
|
||||||
|
|
||||||
Two form patterns are used in this plugin. New forms should follow the appropriate pattern.
|
Two form patterns are used in this plugin. New forms should follow the appropriate pattern.
|
||||||
|
|||||||
22
README.md
22
README.md
@@ -1,6 +1,6 @@
|
|||||||
# @rmdes/indiekit-endpoint-activitypub
|
# @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
|
## Features
|
||||||
|
|
||||||
@@ -109,10 +109,18 @@ ActivityPub federation endpoint for [Indiekit](https://getindiekit.com), built o
|
|||||||
- Follower and following lists with source tracking
|
- Follower and following lists with source tracking
|
||||||
- Federation management page with moderation overview (blocked servers, blocked accounts, muted)
|
- 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
|
## Requirements
|
||||||
|
|
||||||
- [Indiekit](https://getindiekit.com) v1.0.0-beta.25+
|
- [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
|
- Node.js >= 22
|
||||||
- MongoDB (used by Indiekit)
|
- MongoDB (used by Indiekit)
|
||||||
- Redis (recommended for production delivery queue; in-process queue available for development)
|
- 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_blocked_servers` | Blocked server domains (instance-level blocks) |
|
||||||
| `ap_key_freshness` | Tracks when remote actor keys were last verified |
|
| `ap_key_freshness` | Tracks when remote actor keys were last verified |
|
||||||
| `ap_inbox_queue` | Persistent async inbox processing queue |
|
| `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_apps` | Mastodon API client app registrations |
|
||||||
| `ap_oauth_tokens` | OAuth2 authorization codes and access tokens |
|
| `ap_oauth_tokens` | OAuth2 authorization codes and access tokens |
|
||||||
| `ap_markers` | Read position markers for Mastodon API clients |
|
| `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
|
## 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`)
|
### 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.
|
**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
|
**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.
|
This workaround has been removed. Fedify 2.1.0 now omits the invalid `"type": "as:Endpoints"` from serialized actor JSON.
|
||||||
|
|
||||||
**Remove when:** Upgrading to Fedify ≥ 2.1.0.
|
|
||||||
|
|
||||||
### PropertyValue Attachment Type (Known Issue)
|
### PropertyValue Attachment Type (Known Issue)
|
||||||
|
|
||||||
|
|||||||
32
index.js
32
index.js
@@ -435,6 +435,20 @@ export default class ActivityPubEndpoint {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!post || post.properties?.deleted) {
|
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();
|
return next();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -811,6 +825,21 @@ export default class ActivityPubEndpoint {
|
|||||||
* @param {string} url - Full URL of the deleted post
|
* @param {string} url - Full URL of the deleted post
|
||||||
*/
|
*/
|
||||||
async delete(url) {
|
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) =>
|
await this.broadcastDelete(url).catch((err) =>
|
||||||
console.warn(`[ActivityPub] broadcastDelete failed for ${url}: ${err.message}`)
|
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_apps");
|
||||||
Indiekit.addCollection("ap_oauth_tokens");
|
Indiekit.addCollection("ap_oauth_tokens");
|
||||||
Indiekit.addCollection("ap_markers");
|
Indiekit.addCollection("ap_markers");
|
||||||
|
// Tombstones for soft-deleted posts (FEP-4f05)
|
||||||
|
Indiekit.addCollection("ap_tombstones");
|
||||||
|
|
||||||
// Store collection references (posts resolved lazily)
|
// Store collection references (posts resolved lazily)
|
||||||
const indiekitCollections = Indiekit.collections;
|
const indiekitCollections = Indiekit.collections;
|
||||||
@@ -964,6 +995,7 @@ export default class ActivityPubEndpoint {
|
|||||||
ap_oauth_apps: indiekitCollections.get("ap_oauth_apps"),
|
ap_oauth_apps: indiekitCollections.get("ap_oauth_apps"),
|
||||||
ap_oauth_tokens: indiekitCollections.get("ap_oauth_tokens"),
|
ap_oauth_tokens: indiekitCollections.get("ap_oauth_tokens"),
|
||||||
ap_markers: indiekitCollections.get("ap_markers"),
|
ap_markers: indiekitCollections.get("ap_markers"),
|
||||||
|
ap_tombstones: indiekitCollections.get("ap_tombstones"),
|
||||||
get posts() {
|
get posts() {
|
||||||
return indiekitCollections.get("posts");
|
return indiekitCollections.get("posts");
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,18 +2,21 @@
|
|||||||
* Authorize Interaction controller — handles the remote follow / authorize
|
* Authorize Interaction controller — handles the remote follow / authorize
|
||||||
* interaction flow for ActivityPub federation.
|
* interaction flow for ActivityPub federation.
|
||||||
*
|
*
|
||||||
* When a remote server (WordPress AP, Misskey, etc.) discovers our WebFinger
|
* Supports:
|
||||||
* subscribe template, it redirects the user here with ?uri={actorOrPostUrl}.
|
* - OStatus subscribe template (legacy remote follow via ?uri=...)
|
||||||
|
* - FEP-3b86 Activity Intents (via ?uri=...&intent=follow|create|like|announce)
|
||||||
*
|
*
|
||||||
* Flow:
|
* Flow:
|
||||||
* 1. Missing uri → render error page
|
* 1. Missing uri → render error page
|
||||||
* 2. Unauthenticated → redirect to login, then back here
|
* 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) {
|
export function authorizeInteractionController(plugin) {
|
||||||
return async (req, res) => {
|
return async (req, res) => {
|
||||||
const uri = req.query.uri || req.query.acct;
|
const uri = req.query.uri || req.query.acct;
|
||||||
|
const intent = req.query.intent || "";
|
||||||
|
|
||||||
if (!uri) {
|
if (!uri) {
|
||||||
return res.status(400).render("activitypub-authorize-interaction", {
|
return res.status(400).render("activitypub-authorize-interaction", {
|
||||||
title: "Authorize Interaction",
|
title: "Authorize Interaction",
|
||||||
@@ -29,17 +32,28 @@ export function authorizeInteractionController(plugin) {
|
|||||||
// then back to this page after auth
|
// then back to this page after auth
|
||||||
const session = req.session;
|
const session = req.session;
|
||||||
if (!session?.access_token) {
|
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(
|
return res.redirect(
|
||||||
`/session/login?redirect=${encodeURIComponent(returnUrl)}`,
|
`/session/login?redirect=${encodeURIComponent(returnUrl)}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authenticated — redirect to the remote profile viewer in our reader
|
const mp = plugin.options.mountPath;
|
||||||
// which already has follow/unfollow/like/boost functionality
|
|
||||||
const encodedUrl = encodeURIComponent(resource);
|
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}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,12 +89,6 @@ async function sendFedifyResponse(res, response, request) {
|
|||||||
if (json.attachment && !Array.isArray(json.attachment)) {
|
if (json.attachment && !Array.isArray(json.attachment)) {
|
||||||
json.attachment = [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);
|
const patched = JSON.stringify(json);
|
||||||
res.setHeader("content-length", Buffer.byteLength(patched));
|
res.setHeader("content-length", Buffer.byteLength(patched));
|
||||||
res.end(patched);
|
res.end(patched);
|
||||||
|
|||||||
@@ -286,10 +286,48 @@ export function setupFederation(options) {
|
|||||||
// Add OStatus subscribe template so remote servers (WordPress AP, Misskey, etc.)
|
// Add OStatus subscribe template so remote servers (WordPress AP, Misskey, etc.)
|
||||||
// can redirect users to our authorize_interaction page for remote follow.
|
// can redirect users to our authorize_interaction page for remote follow.
|
||||||
federation.setWebFingerLinksDispatcher((_ctx, _resource) => {
|
federation.setWebFingerLinksDispatcher((_ctx, _resource) => {
|
||||||
|
const interactionBase = `${publicationUrl}${mountPath.replace(/^\//, "")}/authorize_interaction`;
|
||||||
return [
|
return [
|
||||||
|
// OStatus subscribe template (legacy remote follow)
|
||||||
{
|
{
|
||||||
rel: "http://ostatus.org/schema/1.0/subscribe",
|
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,
|
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.
|
// Enable authenticated fetches for the shared inbox.
|
||||||
// Without this, Fedify can't verify incoming HTTP Signatures from servers
|
// Without this, Fedify can't verify incoming HTTP Signatures from servers
|
||||||
// that require authorized fetch (e.g. hachyderm.io returns 401 on unsigned GETs).
|
// 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()
|
? await collections.posts.countDocuments()
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
|
const profile = await getProfile(collections);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
software: {
|
software: {
|
||||||
name: "indiekit",
|
name: "indiekit",
|
||||||
version: softwareVersion,
|
version: softwareVersion,
|
||||||
|
repository: new URL("https://github.com/getindiekit/indiekit"),
|
||||||
|
homepage: new URL("https://getindiekit.com"),
|
||||||
},
|
},
|
||||||
protocols: ["activitypub"],
|
protocols: ["activitypub"],
|
||||||
|
services: {
|
||||||
|
inbound: [],
|
||||||
|
outbound: [],
|
||||||
|
},
|
||||||
|
openRegistrations: false,
|
||||||
usage: {
|
usage: {
|
||||||
users: { total: 1, activeMonth: 1, activeHalfyear: 1 },
|
users: { total: 1, activeMonth: 1, activeHalfyear: 1 },
|
||||||
localPosts: postsCount,
|
localPosts: postsCount,
|
||||||
localComments: 0,
|
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),
|
featuredTags: ctx.getFeaturedTagsUri(identifier),
|
||||||
endpoints: new Endpoints({ sharedInbox: ctx.getInboxUri() }),
|
endpoints: new Endpoints({ sharedInbox: ctx.getInboxUri() }),
|
||||||
manuallyApprovesFollowers: profile.manuallyApprovesFollowers || false,
|
manuallyApprovesFollowers: profile.manuallyApprovesFollowers || false,
|
||||||
|
indexable: true,
|
||||||
|
discoverable: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (profile.summary) {
|
if (profile.summary) {
|
||||||
|
|||||||
@@ -244,6 +244,12 @@ export function createIndexes(collections, options) {
|
|||||||
{ userId: 1, timeline: 1 },
|
{ userId: 1, timeline: 1 },
|
||||||
{ unique: true, background: true },
|
{ unique: true, background: true },
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Tombstone indexes (FEP-4f05)
|
||||||
|
collections.ap_tombstones?.createIndex(
|
||||||
|
{ url: 1 },
|
||||||
|
{ unique: true, background: true },
|
||||||
|
);
|
||||||
} catch {
|
} catch {
|
||||||
// Index creation failed — collections not yet available.
|
// Index creation failed — collections not yet available.
|
||||||
// Indexes already exist from previous startups; non-fatal.
|
// Indexes already exist from previous startups; non-fatal.
|
||||||
|
|||||||
52
lib/storage/tombstones.js
Normal file
52
lib/storage/tombstones.js
Normal 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
142
package-lock.json
generated
@@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "@rmdes/indiekit-endpoint-activitypub",
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
||||||
"version": "3.8.7",
|
"version": "3.9.4",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@rmdes/indiekit-endpoint-activitypub",
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
||||||
"version": "3.8.7",
|
"version": "3.9.4",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fedify/debugger": "^2.0.0",
|
"@fedify/debugger": "^2.1.0",
|
||||||
"@fedify/fedify": "^2.0.0",
|
"@fedify/fedify": "^2.1.0",
|
||||||
"@fedify/redis": "^2.0.0",
|
"@fedify/redis": "^2.1.0",
|
||||||
"@js-temporal/polyfill": "^0.5.0",
|
"@js-temporal/polyfill": "^0.5.0",
|
||||||
"express": "^5.0.0",
|
"express": "^5.0.0",
|
||||||
"express-rate-limit": "^7.5.1",
|
"express-rate-limit": "^7.5.1",
|
||||||
@@ -515,12 +515,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fedify/debugger": {
|
"node_modules/@fedify/debugger": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fedify/debugger/-/debugger-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fedify/debugger/-/debugger-2.1.0.tgz",
|
||||||
"integrity": "sha512-2ZGXKQa+BqAO9F+tuZcDiLHDr193cUqKlnX1Z7yDn1ICPL1gPxxwgKAa1b540pBBWSfDCXBSrJlZ3DYK9f52GA==",
|
"integrity": "sha512-4s3L3/NkofZCUXR1jADq5ukSbWybpWqgqF4TEg3PHxlXkC3bT/LI4as8zFxTpkkCvM5fE6tXCi5z56rJ9tXzag==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@js-temporal/polyfill": "^0.5.1",
|
"@js-temporal/polyfill": "^0.5.1",
|
||||||
"@logtape/logtape": "^2.0.0",
|
"@logtape/logtape": "^2.0.5",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@opentelemetry/context-async-hooks": "^2.5.0",
|
"@opentelemetry/context-async-hooks": "^2.5.0",
|
||||||
"@opentelemetry/core": "^2.5.0",
|
"@opentelemetry/core": "^2.5.0",
|
||||||
@@ -528,24 +528,24 @@
|
|||||||
"hono": "^4.0.0"
|
"hono": "^4.0.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@fedify/fedify": "^2.0.0"
|
"@fedify/fedify": "^2.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fedify/fedify": {
|
"node_modules/@fedify/fedify": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fedify/fedify/-/fedify-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fedify/fedify/-/fedify-2.1.0.tgz",
|
||||||
"integrity": "sha512-ZejBFXfILViuIHYhI1BWEk1Pewt9hNO70u6GVaWYKWwU3IVc1/HEsGA/kK9IxJKYZBPqLcCtoI2BZfeOg8I/Hg==",
|
"integrity": "sha512-CMGlL9HEaqyuQL4Ma0Jv+9/QgtLjj+HLmjNrg1e/WUQrEwZg9p5WYKk4iNKXF4aIG3XJkAv5UGJlHKF09HifNA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://opencollective.com/fedify",
|
"https://opencollective.com/fedify",
|
||||||
"https://github.com/sponsors/dahlia"
|
"https://github.com/sponsors/dahlia"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fedify/vocab": "2.0.0",
|
"@fedify/vocab": "2.1.0",
|
||||||
"@fedify/vocab-runtime": "2.0.0",
|
"@fedify/vocab-runtime": "2.1.0",
|
||||||
"@fedify/webfinger": "2.0.0",
|
"@fedify/webfinger": "2.1.0",
|
||||||
"@js-temporal/polyfill": "^0.5.1",
|
"@js-temporal/polyfill": "^0.5.1",
|
||||||
"@logtape/logtape": "^2.0.0",
|
"@logtape/logtape": "^2.0.5",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@opentelemetry/core": "^2.5.0",
|
"@opentelemetry/core": "^2.5.0",
|
||||||
"@opentelemetry/sdk-trace-base": "^2.5.0",
|
"@opentelemetry/sdk-trace-base": "^2.5.0",
|
||||||
@@ -554,7 +554,6 @@
|
|||||||
"es-toolkit": "1.43.0",
|
"es-toolkit": "1.43.0",
|
||||||
"json-canon": "^1.0.1",
|
"json-canon": "^1.0.1",
|
||||||
"jsonld": "^9.0.0",
|
"jsonld": "^9.0.0",
|
||||||
"multicodec": "^3.2.1",
|
|
||||||
"structured-field-values": "^2.0.4",
|
"structured-field-values": "^2.0.4",
|
||||||
"uri-template-router": "^1.0.0",
|
"uri-template-router": "^1.0.0",
|
||||||
"url-template": "^3.1.1",
|
"url-template": "^3.1.1",
|
||||||
@@ -567,9 +566,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fedify/redis": {
|
"node_modules/@fedify/redis": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fedify/redis/-/redis-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fedify/redis/-/redis-2.1.0.tgz",
|
||||||
"integrity": "sha512-WHUhEHZ0BAbcmITgDTdspeolfl4bHpRx+BlmdVRsGScaoQODvvohBfRcTCGbMz2RfQmkhz4l297Nk8Nlqyvg7Q==",
|
"integrity": "sha512-Fqud46FIEBFXDFad029rS46ZlVlWZU2zT6yhBs63jtat7QwMIHDSisizvoVyky4a41TX0ItBNqiAdYELLv/0NQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://opencollective.com/fedify",
|
"https://opencollective.com/fedify",
|
||||||
"https://github.com/sponsors/dahlia"
|
"https://github.com/sponsors/dahlia"
|
||||||
@@ -577,34 +576,33 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@js-temporal/polyfill": "^0.5.1",
|
"@js-temporal/polyfill": "^0.5.1",
|
||||||
"@logtape/logtape": "^2.0.0"
|
"@logtape/logtape": "^2.0.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@fedify/fedify": "^2.0.0",
|
"@fedify/fedify": "^2.1.0",
|
||||||
"ioredis": "^5.8.2"
|
"ioredis": "^5.8.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fedify/vocab": {
|
"node_modules/@fedify/vocab": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fedify/vocab/-/vocab-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fedify/vocab/-/vocab-2.1.0.tgz",
|
||||||
"integrity": "sha512-sjw51UltqefhGPxkcSrnwdmBHO5Zm3hOlOHFvzsLg1pbl53KKETcJ8TG6OcMaD0ZiaUqFVKkGAlpDG3FD9O4nw==",
|
"integrity": "sha512-tGCgo8kCj6Zwf1JxYsXtEwReujzgitndf59Pdo1BY21UgpAlAe0daY8vdpRM+NybZ4JbOBtM4bH473LVtJlVEA==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://opencollective.com/fedify",
|
"https://opencollective.com/fedify",
|
||||||
"https://github.com/sponsors/dahlia"
|
"https://github.com/sponsors/dahlia"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fedify/vocab-runtime": "2.0.0",
|
"@fedify/vocab-runtime": "2.1.0",
|
||||||
"@fedify/vocab-tools": "2.0.0",
|
"@fedify/vocab-tools": "2.1.0",
|
||||||
"@fedify/webfinger": "2.0.0",
|
"@fedify/webfinger": "2.1.0",
|
||||||
"@js-temporal/polyfill": "^0.5.1",
|
"@js-temporal/polyfill": "^0.5.1",
|
||||||
"@logtape/logtape": "^2.0.0",
|
"@logtape/logtape": "^2.0.5",
|
||||||
"@multiformats/base-x": "^4.0.1",
|
"@multiformats/base-x": "^4.0.1",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"asn1js": "^3.0.6",
|
"asn1js": "^3.0.6",
|
||||||
"es-toolkit": "1.43.0",
|
"es-toolkit": "1.43.0",
|
||||||
"jsonld": "^9.0.0",
|
"jsonld": "^9.0.0",
|
||||||
"multicodec": "^3.2.1",
|
|
||||||
"pkijs": "^3.3.3"
|
"pkijs": "^3.3.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -614,21 +612,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fedify/vocab-runtime": {
|
"node_modules/@fedify/vocab-runtime": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fedify/vocab-runtime/-/vocab-runtime-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fedify/vocab-runtime/-/vocab-runtime-2.1.0.tgz",
|
||||||
"integrity": "sha512-Cdcbhki75kBi20Eq0Dkpf1XXXVKVwnOzK4O/b4MKH6kmUPEcVyNVb9L3+ZZElViE+kAZ0bmYLlFNp42E7mjjLQ==",
|
"integrity": "sha512-rISQFJbuRrt1OX9yG+xVUn7DwBTajpOOvy5jdx2ZuRUMvtlD7bgDEUSSS5a7pFuYliKVbR5ZFk6BPAkJC1OnAw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://opencollective.com/fedify",
|
"https://opencollective.com/fedify",
|
||||||
"https://github.com/sponsors/dahlia"
|
"https://github.com/sponsors/dahlia"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@logtape/logtape": "^2.0.0",
|
"@logtape/logtape": "^2.0.5",
|
||||||
"@multiformats/base-x": "^4.0.1",
|
"@multiformats/base-x": "^4.0.1",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"asn1js": "^3.0.6",
|
"asn1js": "^3.0.6",
|
||||||
"byte-encodings": "^1.0.11",
|
"byte-encodings": "^1.0.11",
|
||||||
"multicodec": "^3.2.1",
|
"jsonld": "^9.0.0",
|
||||||
"pkijs": "^3.3.3"
|
"pkijs": "^3.3.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
@@ -638,9 +636,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fedify/vocab-tools": {
|
"node_modules/@fedify/vocab-tools": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fedify/vocab-tools/-/vocab-tools-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fedify/vocab-tools/-/vocab-tools-2.1.0.tgz",
|
||||||
"integrity": "sha512-AQ1zjGt4wjaTjTOHCgDroNITiSeZ1z99ygNEkmukg5EwgjF7+DVoPV+OTrmVVW/2A6t1blJzfOS0wrlzsn5lqQ==",
|
"integrity": "sha512-Gn07LbMoDRVDjklDZH9y/fZ2nwH7ryjillgLpw8qsbjUeVaTViR1Oz/oG2N7S13UqyKttYPzK8hH/utKr1LbXg==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://opencollective.com/fedify",
|
"https://opencollective.com/fedify",
|
||||||
"https://github.com/sponsors/dahlia"
|
"https://github.com/sponsors/dahlia"
|
||||||
@@ -659,17 +657,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@fedify/webfinger": {
|
"node_modules/@fedify/webfinger": {
|
||||||
"version": "2.0.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@fedify/webfinger/-/webfinger-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@fedify/webfinger/-/webfinger-2.1.0.tgz",
|
||||||
"integrity": "sha512-D66ZdyUUM9BNQU7OGEWmNLU+FIHApUiYnEogOa5oNj9fy0vjOfxm9hynA+0SCm3emrKrLt6Gm120Re+T5Us5Yg==",
|
"integrity": "sha512-G5yrCPw1oWijvkGOMjWZFOWohmljQ4pmHgK7BuESshcAizpKRU0t5GcOGMyPzcNrO4+diaddGNg48GFzZ9mK/g==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://opencollective.com/fedify",
|
"https://opencollective.com/fedify",
|
||||||
"https://github.com/sponsors/dahlia"
|
"https://github.com/sponsors/dahlia"
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fedify/vocab-runtime": "2.0.0",
|
"@fedify/vocab-runtime": "2.1.0",
|
||||||
"@logtape/logtape": "^2.0.0",
|
"@logtape/logtape": "^2.0.5",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"es-toolkit": "1.43.0"
|
"es-toolkit": "1.43.0"
|
||||||
},
|
},
|
||||||
@@ -1264,9 +1262,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@logtape/logtape": {
|
"node_modules/@logtape/logtape": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@logtape/logtape/-/logtape-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@logtape/logtape/-/logtape-2.0.5.tgz",
|
||||||
"integrity": "sha512-cveUBLbCMFkvkLycP/2vNWvywl47JcJbazHIju94/QNGboZ/jyYAgZIm0ZXezAOx3eIz8OG1EOZ5CuQP3+2FQg==",
|
"integrity": "sha512-UizDkh20ZPJVOddRxG1F77WhHdlNl/sbQgoO8T534R7XvUBMAJ9En9f35u+meW2tRsNLvjz6R87Zanwf53tspQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
"https://github.com/sponsors/dahlia"
|
"https://github.com/sponsors/dahlia"
|
||||||
],
|
],
|
||||||
@@ -2970,23 +2968,6 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.11",
|
"version": "3.3.11",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
|
||||||
@@ -3157,9 +3138,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/pkijs": {
|
"node_modules/pkijs": {
|
||||||
"version": "3.3.3",
|
"version": "3.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.4.0.tgz",
|
||||||
"integrity": "sha512-+KD8hJtqQMYoTuL1bbGOqxb4z+nZkTAwVdNtWwe8Tc2xNbEmdJYIYoc6Qt0uF55e6YW6KuTHw1DjQ18gMhzepw==",
|
"integrity": "sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==",
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@noble/hashes": "1.4.0",
|
"@noble/hashes": "1.4.0",
|
||||||
@@ -3656,19 +3637,10 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true
|
"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": {
|
"node_modules/undici": {
|
||||||
"version": "6.23.0",
|
"version": "6.24.1",
|
||||||
"resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz",
|
||||||
"integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==",
|
"integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.17"
|
"node": ">=18.17"
|
||||||
@@ -3741,12 +3713,6 @@
|
|||||||
"integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==",
|
"integrity": "sha512-IGjKp/o0NL3Bso1PymYURCJxMPNAf/ILOpendP9f5B6e1rTJgdgiOvgfoT8VxCAdY+Wisb9uhGaJJf3yZ2V9nw==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/vary": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||||
@@ -3785,9 +3751,9 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/yaml": {
|
"node_modules/yaml": {
|
||||||
"version": "2.8.2",
|
"version": "2.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
|
||||||
"integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
|
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"bin": {
|
"bin": {
|
||||||
"yaml": "bin.mjs"
|
"yaml": "bin.mjs"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rmdes/indiekit-endpoint-activitypub",
|
"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.",
|
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"indiekit",
|
"indiekit",
|
||||||
@@ -37,9 +37,9 @@
|
|||||||
"url": "https://github.com/rmdes/indiekit-endpoint-activitypub/issues"
|
"url": "https://github.com/rmdes/indiekit-endpoint-activitypub/issues"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fedify/debugger": "^2.0.0",
|
"@fedify/debugger": "^2.1.0",
|
||||||
"@fedify/fedify": "^2.0.0",
|
"@fedify/fedify": "^2.1.0",
|
||||||
"@fedify/redis": "^2.0.0",
|
"@fedify/redis": "^2.1.0",
|
||||||
"@js-temporal/polyfill": "^0.5.0",
|
"@js-temporal/polyfill": "^0.5.0",
|
||||||
"express": "^5.0.0",
|
"express": "^5.0.0",
|
||||||
"express-rate-limit": "^7.5.1",
|
"express-rate-limit": "^7.5.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user