Retry behavior is already covered by the livefetch patch, but keeping
this patch ensures the skip-on-failure guard applies even if livefetch
is removed or the upstream code changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When a post was processed but had no discoverable external links, the
expanded detail row rendered completely blank — result.details was
truthy ({}) so the 'noDetails' fallback never fired, but all three
arrays were empty so no tables rendered either.
Adds a patch script for the template that shows "No external links
discovered in this post." in that case, and wires it into both
postinstall and serve scripts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
uploadMedia, uploadImageFromUrl, and fetchOpenGraphData all fetch from
the blog's public URL which is unreachable behind the nginx jail. Rewrite
own-domain URLs to http://localhost:PORT, same as micropub-fetch-internal-url.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The webmention sender was using stored post content (just the body text)
instead of the live page, missing template-rendered links like
u-in-reply-to, u-like-of, u-bookmark-of. This caused reply/like/bookmark
posts to be marked as sent with 0 webmentions. Bump reset-stale migration
to v4 so affected posts are retried.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Node can't reach its own public HTTPS URL (ECONNREFUSED 127.0.0.1:443)
because port 443 only exists on the nginx jail. Rewrite self-referential
fetch URLs to http://localhost:3000 in endpoint-posts, endpoint-syndicate,
and endpoint-share.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Re-add PeerTube View activity patches that were prematurely removed in
e52e98c5c — the upstream fork doesn't reliably include these fixes on
all server deployments, causing noisy "Unsupported activity type" errors.
Also add fetch diagnostic patch to surface the real cause of
"TypeError: fetch failed" when posting articles via the form controller.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tags like `on/art/music` are reduced to their last segment (`#music`)
in both buildPlainTags and buildFedifyTags so ActivityPub hashtags are
valid on Mastodon and other platforms.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
blog.giersig.eu resolves to 10.100.0.10 (private LAN) from the server,
causing Fedify's SSRF guard to block lookupObject() and WebFinger calls
for own posts when processing incoming ActivityPub activities.
Adds patch-ap-allow-private-address.mjs which sets allowPrivateAddress: true
on createFederation(), wired into both postinstall and serve scripts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Before the raw-body digest fix, every Mastodon inbox delivery was
rejected with a Digest mismatch. Mastodon queued those activities for
retry. After the digest fix, the retried deliveries arrive with their
original HTTP Signatures which are now > 1 hour old. Fedify's default
signatureTimeWindow: { hours: 1 } rejects them with "Date is too far
in the past", logged as "Failed to verify the request's HTTP Signatures."
Extending to 12 hours allows those retried deliveries to be accepted.
The signature still must be cryptographically valid — only the replay
window is relaxed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The Fedify object dispatcher constructs the post lookup URL from the
{+id} path variable (e.g. "replies/bd78a"), which has no trailing slash.
Posts in MongoDB store their URL with a trailing slash, so the exact
findOne() match was silently returning null → Fedify serving 404 →
mountains.social showing "Could not connect to the given address".
Fix uses $in to try both variants so the dispatcher works regardless
of whether the request URL has a trailing slash or not.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- patch-ap-inbox-raw-body-digest: preserve raw request bytes through the
AP inbox buffer guard so Fedify's HTTP Signature Digest verification
passes (JSON.stringify re-encoding broke SHA-256 digest check, causing
Mastodon likes/replies/boosts to be silently rejected)
- patch-ap-url-lookup-api: add GET /activitypub/api/ap-url endpoint that
maps a blog post URL to its Fedify-served AP object URL, enabling
reliable content negotiation for authorize_interaction redirects
- wire both patches into postinstall and serve scripts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When the backend Syndicate button is pressed on a post with no
mp-syndicate-to and no prior syndication URLs, fall back to targets
with checked:true (e.g. ActivityPub) instead of no-oping.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- patch-endpoint-micropub-source-filter: support ?category= and ?search=
query params in the Micropub ?q=source endpoint, filtering MongoDB
documents by properties.category and a case-insensitive regex across
name/content fields
- patch-endpoint-posts-search-tags: forward category/search params from
the posts controller to Micropub, expose tagLinks on each item, and
replace the posts.njk cardGrid with a custom loop that renders clickable
tag chips and a search form above the grid
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace `contentRoot ?? $` with `contentRoot ?? $.root()` in webmention.js.
When a post has no .h-entry, <article>, or <main>, contentRoot is null and
`$` is a Cheerio constructor function — not a Cheerio object — so .find()
throws "scope.find is not a function". $.root() returns the document root
as a proper Cheerio object that supports .find().
Rewrites patch-webmention-sender-content-scope.mjs for the new 1.0.7 package
shape (content-scope logic already baked in, only the $.root() fix needed)
and registers it in postinstall + serve.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Blog is now a native ActivityPub actor; Mastodon syndication via
troet.cafe is no longer needed. Removes the syndicator package,
config vars, patch script, and env example entries.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The .on(View) inbox handler from the previous commit is never reached
because Fedify crashes parsing PeerTube's View activity before dispatch —
its JSON-LD deserialiser doesn't recognise Schema.org InteractionCounter,
throwing "Failed to parse activity: TypeError: Invalid type".
Add a guard at the top of createFedifyMiddleware that short-circuits any
POST with body.type === "View" and returns 200 immediately, bypassing
federation.fetch() entirely.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PeerTube broadcasts non-standard View (WatchAction) activities to all
followers on every video watch. Fedify has no handler for this type,
causing noisy "Unsupported activity type" errors in the federation log.
Adds a no-op .on(View, ...) handler at the end of the inbox listener
chain via the existing patch script mechanism.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a bookmarked URL is an HTML page whose feed is not at a common
path (/feed, /rss.xml etc.), fetchAndParseFeed would throw and store
no items in microsub_items. Sites like econsoc.mpifg.de or signal.org
post pages advertise their feed via a standard
<link rel="alternate" type="application/rss+xml" href="...">
element, which discoverFeeds() already parses but was never called
from the fetch/parse pipeline.
Now, before probing common paths, fetchAndParseFeed calls discoverFeeds()
on the fetched HTML and uses any typed RSS/Atom/JSONFeed link it finds.
Common-path probing remains as the final fallback.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
share-post.js opens /posts/create?type=like&url=<link>&name=<title>
but postData.create only reads request.body, ignoring the query params.
Patch postData.create: when properties is empty and ?url= is present,
seed properties with the correct field name per post type:
like → like-of
bookmark → bookmark-of (also seeds name from ?name=)
reply → in-reply-to
repost → repost-of
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
getPostProperties queries ?q=source (no filter, default limit=40) then
scans items for a uid match. Posts outside the 40 most recent return
undefined → IndiekitError.notFound (404).
Fix:
- Patch micropub query controller: when ?q=source&uid=<objectId> is
present, findOne({ _id: getObjectId(uid) }) directly and return
{ items: [mf2] } so it is compatible with getPostProperties.
- Patch getPostProperties to append uid= to the micropub source URL,
so any post can be fetched regardless of recency.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The github: source was pinned to 2.8.0 in the lock file but the repo
HEAD is now at 2.8.2, causing npm ci to fail. Switch to the versioned
npm package to keep the lock file stable.
Lock file needs regeneration: run npm install locally and commit
the updated package-lock.json.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The /news/ page fetches /rssapi/api/items, /rssapi/api/feeds and
/rssapi/api/status. The blogroll endpoint was mounted at /blogrollapi,
so all three requests returned a 404 HTML page — causing the
"Unexpected token '<'" JSON parse error.
- Change blogroll mountPath from /blogrollapi to /rssapi
- Add patch-endpoint-blogroll-feeds-alias.mjs: injects a /api/feeds
route alias pointing to listBlogs (page expects /feeds, endpoint
only had /blogs)
- Wire new patch into postinstall and serve scripts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deleted scripts (logic now built into the fork):
- patch-endpoint-activitypub-docloader-loglevel.mjs
- patch-endpoint-activitypub-migrate-alias-clear.mjs
- patch-endpoint-activitypub-like-boost-methods.mjs
Trimmed patch-federation-unlisted-guards.mjs to only cover
endpoint-syndicate (separate package). AP unlisted guards are
now in the fork's federation-setup.js.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use svemagie/indiekit-endpoint-activitypub instead of the upstream npm
package to get direct message receive and native AP reply support.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Serve like/repost posts as Note objects for AP content negotiation.
Returning a bare Like/Announce activity broke Mastodon's
authorize_interaction because it expects a content object (Note/Article).
Now like posts are served as ❤️ Note and reposts as 🔁 Note.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add likePost() and boostPost() programmatic methods to the ActivityPub
plugin, sending native Like and Announce activities as @svemagie@blog.giersig.eu
- Wire up AP dispatch in submitCompose (microsub reader) after a successful
Micropub post creation — fire-and-forget, non-blocking
- Fix detectProtocol to recognise troet.cafe, hachyderm.io, infosec.exchange,
chaos.social and other fediverse domains not in the original pattern list
- Fix Mastodon syndication target auto-selection to match by service.name
("mastodon") and by configured instance hostname, not just uid string
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The v3 patch bug allowed Micropub to update MongoDB with aiTextLevel/
aiCodeLevel values but write post files without the ai: frontmatter
block (supportsAiDisclosure was always false). Re-saving with the same
values correctly returned "no properties changed" — but the file on disk
remained stale.
New patch (patch-micropub-ai-block-resync.mjs) adds _aiBlockVersion to
each post document in MongoDB. On update, if a post has AI fields but
_aiBlockVersion != "v4", the no-change check is bypassed and the file
is force-rewritten exactly once. Subsequent no-change saves behave
normally.
Also adds AI transparency section to README documenting the full
implementation, patch chain, v4 root cause, and re-save instructions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switches to github:svemagie/indiekit-endpoint-microsub#bookmarks-import
which adds a contentNegotiationRoutes hook to auto-follow bookmarked
URLs as Microsub feed subscriptions when a bookmark-of post is created.
Mirrors the same pattern already in place for the blogroll fork.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Indiekit's getPostTemplateProperties() explicitly removes the post-type
property before passing JF2 to postTemplate(). The v3 patch relied on
post-type to set supportsAiDisclosure, which was therefore always false —
causing the ai: frontmatter block to never be written regardless of what
was selected in the backend form.
v4 patch falls back to permalink URL pattern (/articles/, /notes/) to
correctly detect the post type when post-type is absent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a micropub bookmark post is created, the bookmarked site's
origin URL is discovered for RSS/Atom feeds and added to the blogroll
under category "bookmarks".
Integration is self-contained in the blogroll plugin via
contentNegotiationRoutes — no micropub patches needed.
Source: github:svemagie/indiekit-endpoint-blogroll#bookmark-import
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds Microsub social reader endpoint at /microsub, enabling feed
subscription and management via the Microsub protocol.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>