patch-endpoint-comments-locales: remove viewReplacements and sourceOverrides
since comments.njk was rewritten with Nunjucks macros and badge() — the old
HTML snippets no longer exist. Also drop obsolete locale keys (hiddenBadge,
targetPrefix, paginationLabel, page, of) that the new template doesn't use.
patch-webmention-sender-livefetch: delete — was orphaned (not in postinstall)
and the behavior is intentionally accepted as-is.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The previous patch checked req.body?.type === "View" but Express's JSON
body parser ignores application/activity+json, so req.body was always
undefined and the guard never fired.
Fix in two parts:
1. In createFedifyMiddleware: manually buffer and JSON-parse the raw
request stream for activity+json/ld+json POSTs, storing the result on
req.body before the type check.
2. In fromExpressRequest: extend the content-type check to include
activity+json/ld+json so non-View activities are correctly forwarded
to Fedify with the buffered body.
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>
- gardenStage: added to all post types (article, note, bookmark, like,
repost, photo, reply, page) so Indiekit preserves it in frontmatter
- aiTextLevel/aiCodeLevel/aiTools/aiDescription: extended from article+note
to all content post types (bookmark, repost, photo, reply, page)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
u-in-reply-to, u-like-of, u-repost-of etc. are rendered in an aside
before the .e-content div, not inside it. Scoping to .h-entry .e-content
caused these microformat links to be missed entirely.
Also bump reset-stale migration to v3 so posts already marked sent with
zero results (like /replies/88feb/) are retried with the corrected scope.
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 blogroll index.js is an ES module so require() is not defined.
Replace `const { Router } = require("express")` with `express.Router()`
which is already in scope from the module's own top-level import.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The /news page template uses item.link, item.feedId, item.sourceUrl,
and feedsRes.feeds — but the blogroll API returns item.url, item.blog.id,
item.blog.siteUrl, and {items:[...]} respectively.
Add a response-transformer middleware to the /rssapi alias router that:
- maps url -> link on each item
- maps blog.id -> feedId, blog.siteUrl -> sourceUrl on each item
- renames items -> feeds for the /api/feeds endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/blogroll static page hardcodes /blogrollapi/api/* calls.
/news static page hardcodes /rssapi/api/* calls.
Both must work simultaneously.
- Revert mountPath to /blogrollapi (restores /blogroll page)
- Patch init() to also register publicRouter at /rssapi via a
thin Indiekit.addEndpoint() alias (fixes /news page)
- /api/feeds alias retained for /news page (maps to listBlogs)
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>
Adds patch-webmention-sender-content-scope.mjs to restrict extractLinks()
to .h-entry .e-content / .e-content / article / main — preventing sidebar,
nav, and footer links from the live-fetched full page from being included.
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>
createPublicationAwareDocumentLoader and rawDocumentLoader wrapping
are built into the fork's compose.js; the patch was re-injecting the
function and causing a duplicate-declaration SyntaxError at startup.
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>
The site is its own fediverse actor (@svemagie@blog.giersig.eu). Likes and
reposts send native AP Like/Announce activities directly — troet.cafe has
no role in site-level interaction actions. Mastodon is now only auto-selected
in the compose form for fediverse *replies*, not likes or reposts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AP endpoint already sends native Like/Announce activities to fediverse
authors, making the "❤️ URL" / "🔁 URL" Mastodon status posts redundant.
Disabling syndicateExternalLikes + syndicateExternalReposts prevents the
"did not return a URL" error from the scope-limited Mastodon token.
Native same-instance favourites/reblogs still route through Mastodon
(requires write:favourites + write:statuses scope on the token).
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>
Both plugins now implement the intended bookmark→feed pipeline:
Microsub:
- tag → find/create channel (no more fallback-only to "Bookmarks")
- stores micropubPostUrl on feed for update/delete tracking
- calls notifyBlogroll() after bookmark-import (blogroll via microsub, not independently)
- moves feed when tag changes (delete from old channel, create in new)
- handles micropub update action: detects tag change or bookmark-of removal
Blogroll:
- skips direct bookmark import when microsub is installed (avoids duplicates)
- acts as fallback importer when microsub is absent
- updates category in-place when existing entry's tag changes
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>
The core bug: stored post content (post.properties.content.html) is only
the post body text. Template-rendered microformat links (u-in-reply-to,
u-like-of, u-bookmark-of, u-repost-of) live in the 11ty HTML output, not
in MongoDB. So replies, likes, bookmarks and reposts never had their target
URLs extracted — webmentions were silently skipped.
- patch-webmention-sender-livefetch: always fetch the live page; fall back
to stored content only if the page is unavailable; skip (don't mark sent)
when no content is available so the next poll retries it. Handles both
original upstream code and the older retry-patch variant.
- patch-webmention-sender-reset-stale: bump to v2 so posts incorrectly
marked as sent today (empty results due to the content bug) get reset
and retried on next deploy.
- Remove patch-webmention-sender-retry: superseded by livefetch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- start.example.sh: replace fixed sleep 30 with /status poll loop (up to 2min)
so the webmention poller waits exactly until indiekit is ready, not longer
- indiekit.config.mjs: remove redundant webmentionIoMountPath variable and
mountPath from webmention-io config (package default /webmentions is correct)
- .env.example: remove all stale proxy and unused WEBMENTION_SENDER_* vars
(HOST, PORT, ENDPOINT, READY_TIMEOUT, STOP_TIMEOUT, AUTO_POLL) that were
never read by start.example.sh; keep only WEBMENTION_SENDER_POLL_INTERVAL
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The poller fired immediately after indiekit was backgrounded, before
the HTTP server finished initializing — causing curl error 52 (empty
reply). A 30s initial sleep gives indiekit time to become ready.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Two startup scripts:
patch-webmention-sender-retry: patches the package controller so that
when a post has no stored content and the live page fetch fails (page not
yet deployed), the post is skipped instead of being permanently marked as
webmention-sent with zero results. On the next poll the page will be live
and webmentions will be sent correctly.
patch-webmention-sender-reset-stale: one-time migration (guarded by a
migrations collection entry) that resets all posts already incorrectly
marked as webmention-sent with all-zero results, so they are retried on
the next poll.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove @rmdes/indiekit-endpoint-webmentions-proxy (redundant with
webmention-io which already provides a public JSON API from MongoDB)
- Remove proxy-related variables (webmentionsProxyMountPath, cacheTtl)
- Add explicit mountPath to webmention-io config to avoid future surprises
- Increase WEBMENTION_SENDER_POLL_INTERVAL to 600s so deploys complete
before the poller fires, preventing silent data loss where posts with
no stored content get marked webmention-sent before the page is live
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove complex PID tracking, readiness probe, graceful-shutdown and
daemon-parent machinery. Replace with the minimal polling loop from
the @rmdes/indiekit-endpoint-webmention-sender README: generate JWT,
POST to 127.0.0.1 (local), sleep 300s. Also drop the related tuning
vars from .env (WEBMENTION_SENDER_READY_TIMEOUT, STOP_TIMEOUT,
INDIEKIT_STOP_TIMEOUT, KILL_DAEMON_PARENT_ON_SHUTDOWN).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>