Commit Graph

379 Commits

Author SHA1 Message Date
Claude
9511b96a89 fix(ap): fix OG image not included in ActivityPub activities
Root cause: jf2-to-as2.js (both 842fc5af and 45f8ba9) uses a regex
expecting date-based URLs like /articles/YYYY/MM/DD/slug/ to extract
the post date for the OG image filename. This blog uses flat URLs like
/articles/slug/ so the regex never matches — no `image` property is
set on Note/Article ActivityPub objects and fediverse clients show no
preview card.

Fix:
- Add scripts/patch-ap-og-image.mjs: replaces the URL-pattern regex with
  slug-from-last-path-segment + date-from-properties.published, producing
  /og/{year}-{month}-{day}-{slug}.png matching the Eleventy OG filenames.
  Handles both 842fc5af (slug-only URL) and 45f8ba9 (date+slug) variants.
  Applied to both jf2ToActivityStreams() and jf2ToAS2Activity().
- Register patch in package.json postinstall and serve after patch-ap-repost-commentary.
- Bump package-lock.json to 45f8ba9 fork commit (correct date+slug filename
  format, likes-as-bookmarks, announce cc reverted).

https://claude.ai/code/session_0124D41vdLYE3DkJxhPqYthX
2026-03-21 20:18:09 +00:00
svemagie
ad05bf3f04 Merge pull request #1 from svemagie/claude/fix-activitypub-og-image-CrCGI
fix(ap): fix OG image not included in ActivityPub activities
2026-03-21 21:17:13 +01:00
svemagie
58478f1885 Merge branch 'main' into claude/fix-activitypub-og-image-CrCGI 2026-03-21 21:16:59 +01:00
Claude
769720b33f fix(ap): use slug-only OG image path /og/{slug}.png
The previous fix incorrectly generated /og/{year}-{month}-{day}-{slug}.png
but the Eleventy blog generates OG images at /og/{slug}.png (e.g. /og/2615b.png).
Remove the unnecessary date extraction and simplify to slug-only.

https://claude.ai/code/session_0124D41vdLYE3DkJxhPqYthX
2026-03-21 20:04:19 +00:00
Sven
c0b9878033 fix(webmention): silence retry noise for all livefetch versions, bump stale migration to v10
retry patch: regex now matches [patched:livefetch] and [patched:livefetch:vN]
so it silently skips for any livefetch version, not just v2.

reset-stale: bump to v10 to retry posts stuck during v5 rollout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 20:56:02 +01:00
Sven
bf97dab2ef docs: document upstream activitypub v3.7.1–3.7.5 merge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 20:23:55 +01:00
Sven
bcbc2a284d docs: document 2026-03-19–21 changes (webmention, AP likes, repost commentary, AI patches)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 17:35:51 +01:00
Sven
2548825793 chore: silence github contribution log 2026-03-21 17:31:01 +01:00
Sven
109d39dd25 fix(activitypub): remove federation-diag inbox logging
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 15:47:23 +01:00
Sven
fad383dfee chore(deps): update activitypub fork to v3.6.8 (Mastodon Client API merge)
Pulls the merged upstream feat/mastodon-client-api branch into our fork
(svemagie/indiekit-endpoint-activitypub@f029c31). The fork now tracks
upstream v3.6.8 while retaining our custom additions.

What's new in the installed package:
- lib/mastodon/ — full Mastodon Client API compatibility layer (entities,
  middleware, routes, helpers, backfill-timeline, router)
- 13 additional locale files (es, fr, de, hi, id, it, nl, pt, sr, sv, zh, …)
- signatureTimeWindow and allowPrivateAddress built into federation-setup.js
  (patch-ap-allow-private-address now cleanly detects "already up to date")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 15:27:04 +01:00
Sven
535e6f5e9c fix(activitypub): add Like vocab import in activity dispatcher patch
On fresh installs where the old wrong patch was never applied, Like was
absent from the @fedify/fedify/vocab imports, causing a ReferenceError
at startup. The dispatcher patch now adds Like to the import block if
missing, making it self-contained and install-order independent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 09:05:34 +01:00
Sven
99d2e38066 fix(activitypub): serve AP-likes with canonical id and proper Like dispatcher
Replace the semantically incorrect fake-Note approach with strict AP protocol compliance:

- patch-ap-like-note-dispatcher: rewritten to revert the fake-Note block
- patch-ap-like-activity-id: adds canonical id URI to Like activities (AP §6.2.1)
- patch-ap-like-activity-dispatcher: registers setObjectDispatcher(Like, ...) so
  /activitypub/activities/like/{id} is dereferenceable (AP §3.1)
- patch-ap-url-lookup-api-like: /api/ap-url now returns the likeOf URL for AP-likes
  so the "Also on: Fediverse" widget's authorize_interaction flow opens the
  original Mastodon post on the remote instance

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 08:58:15 +01:00
Sven
34d5fde54d fix(syndicate): normalize syndication property to array before dedup check
Micropub's replaceEntries() stores single-value arrays as plain strings
(JF2 normalization). Spreading a string into [...str] gives individual
characters, so hasSyndicationUrl() never matches existing syndication URLs
and alreadySyndicated is always false — causing re-syndication on every
webhook trigger.

Fix: use [].concat() which safely handles both string and array values.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 07:49:18 +01:00
Sven
9668485b57 revert(deploy): remove syndication webhook (moved to blog repo)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 07:39:39 +01:00
Sven
b16c60adec feat(deploy): trigger syndication webhook after successful deployment
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 07:33:39 +01:00
Sven
396bd5ab6f chore: del script 2026-03-20 22:34:59 +01:00
Sven
17b93b3a2a fix(webmention): build synthetic h-entry from stored properties, drop live fetch
Root cause: blog.giersig.eu DNS resolves internally to 10.100.0.10 (the
indiekit admin nginx), which returns the login page for post URLs of
certain types (notes, photos, replies). Live page fetching is inherently
unreliable in this split-DNS / jailed setup.

The fix: indiekit already stores all microformat target URLs in MongoDB
(in-reply-to, like-of, bookmark-of, repost-of) and content.html has
inline links. We can build a synthetic h-entry HTML snippet directly
from post.properties — no network fetch required for the source post.

Bumps livefetch patch to v5:
- Replace live page fetch with synthetic HTML built from post.properties
- Handles string values, mf2 objects ({properties.url[0]}), and plain
  value strings for each microformat property
- Simplifies patch script: single full-block replacement handles all
  prior versions (v1–v4) via marker detection

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 22:23:30 +01:00
Sven
7f9f02bc36 fix(webmention): fetch live pages from public URL, not INTERNAL_FETCH_URL
Root cause: INTERNAL_FETCH_URL (10.100.0.10) points to the nginx reverse
proxy in front of indiekit's admin interface. Post URLs like /bookmarks/…
require authentication there, so the fetch returned the login page
("Anmelden - Indiekit") which has no .h-entry.

The blog HTML is served by an external host (GitHub Pages), reachable
from the jail over the public URL. INTERNAL_FETCH_URL should only be
used for indiekit API calls, not for fetching blog post pages.

Bumps livefetch patch to v4:
- Remove INTERNAL_FETCH_URL rewrite for live page fetches
- Fetch from postUrl (public URL) directly by default
- Add WEBMENTION_LIVEFETCH_URL env var as opt-in override for setups
  where a local static server can serve blog pages faster
- Add v3→v4 in-place upgrade logic to the patch script

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 22:04:10 +01:00
Claude
1cf5b01788 fix(ap): fix OG image not included in ActivityPub activities
Root cause: jf2-to-as2.js (both 842fc5af and 45f8ba9) uses a regex
expecting date-based URLs like /articles/YYYY/MM/DD/slug/ to extract
the post date for the OG image filename. This blog uses flat URLs like
/articles/slug/ so the regex never matches — no `image` property is
set on Note/Article ActivityPub objects and fediverse clients show no
preview card.

Fix:
- Add scripts/patch-ap-og-image.mjs: replaces the URL-pattern regex with
  slug-from-last-path-segment + date-from-properties.published, producing
  /og/{year}-{month}-{day}-{slug}.png matching the Eleventy OG filenames.
  Handles both 842fc5af (slug-only URL) and 45f8ba9 (date+slug) variants.
  Applied to both jf2ToActivityStreams() and jf2ToAS2Activity().
- Register patch in package.json postinstall and serve after patch-ap-repost-commentary.
- Bump package-lock.json to 45f8ba9 fork commit (correct date+slug filename
  format, likes-as-bookmarks, announce cc reverted).

https://claude.ai/code/session_0124D41vdLYE3DkJxhPqYthX
2026-03-20 14:39:21 +00:00
Sven
11d600058d fix(webmention): send Host header on internal fetches, add fetchUrl diagnostics
Without a Host header, nginx routes internal fetches to the wrong vhost
(sees the internal IP as Host), returning a page with no .h-entry and
causing all posts to retry indefinitely.

Bumps livefetch patch to v3:
- Sends `Host: blog.giersig.eu` when fetching via internal URL so nginx
  routes to the correct virtual host
- Logs the actual fetchUrl on every internal fetch
- Logs the first 200 chars of the response body when h-entry check fails
  so the root cause (wrong vhost, indiekit page, etc.) is visible
- Adds v2→v3 in-place upgrade logic to the patch script

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 14:48:58 +01:00
Sven
750267b175 fix: removed double quote on "h-entry\"" 2026-03-20 14:45:35 +01:00
Sven
fe0f347e49 chore(ai): remove custom AI patches superseded by upstream endpoint-posts@beta.44
- Remove patch-preset-eleventy-ai-frontmatter: upstream now writes AI
  frontmatter natively using hyphenated keys (ai-text-level etc.)
- Remove patch-endpoint-posts-ai-cleanup: upstream beta.44 natively
  removes empty ai-text-level/ai-code-level/ai-tools/ai-description fields
- Remove patch-endpoint-posts-ai-fields: upstream beta.44 has AI form UI
  inline in post-form.njk; our separate templates would have duplicated fields
- Remove patch-micropub-ai-block-resync: one-time stale-block migration,
  no longer relevant
- Remove patch-endpoint-posts-prefill-url: upstream beta.44 has native
  prefill from query params; our patch would have conflicted
- Remove patch-endpoint-posts-search-tags: upstream beta.44 has native
  search/filter/sort UI; patch already detected this and was a no-op
- Bump @rmdes/indiekit-endpoint-posts beta.25→beta.44,
  override beta.41→beta.44
- Update indiekit.config.mjs: remove camelCase ai field names from
  all postTypes.fields (ai-* fields now rendered inline by upstream)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 14:37:04 +01:00
Sven
b53afe2ed3 fix(ap): include commentary in repost ActivityPub activities
Reposts with a body (commentary) were silently broken in two ways:

1. jf2ToAS2Activity() always emitted a bare Announce pointing at the
   external URL (e.g. fromjason.xyz). That URL doesn't serve AP JSON,
   so Mastodon couldn't fetch the object and dropped the activity from
   followers' timelines — the post only appeared when explicitly searched.

2. jf2ToActivityStreams() (content negotiation / search) hard-coded the
   Note content to just '🔁 <url>', completely ignoring properties.content.

Fix via patch-ap-repost-commentary.mjs (4 targeted replacements):
- jf2ToAS2Activity(): skip the Announce early-return when commentary is
  present and fall through to the existing Create(Note) path instead.
  Pure reposts (no body) keep the Announce behaviour unchanged.
- jf2ToAS2Activity() content block: add a repost branch that formats
  the Note as '<commentary><br><br>🔁 <url>' (mirrors bookmark/like).
- jf2ToActivityStreams(): extract commentary and prepend it to the Note
  content when present.

Patch registered in both postinstall and serve chains.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 12:02:16 +01:00
svemagie
c6b0e7025a fix: patch webmention-sender syntax error (h-entry double quote)
Adds patch-webmention-sender-hentry-syntax.mjs to fix a typo in
@rmdes/indiekit-endpoint-webmention-sender@1.0.8 that prevents the
module from loading:

  _html.includes("h-entry"")  →  _html.includes("h-entry")

The extra closing quote causes a SyntaxError at startup, which means
the webmention sender never runs and the background sync never starts.
Patch runs before the other webmention-sender patches in both
postinstall and serve so the file is valid JS before further transforms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:33:37 +01:00
Sven
c2f05deda4 docs: document webmention livefetch v2, reset-stale v9, and poller fix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 21:53:15 +01:00
Sven
711958b8a9 fix(patches): silence retry noise, tighten h-entry check, fix stale comment
- retry: silently skip when livefetch:v2 marker is present instead of
  logging a misleading "target snippet not found (package updated?)"
  warning on every startup
- livefetch: match `h-entry"` or `h-entry ` instead of bare `h-entry`
  to avoid false positives from body text containing the string
- reset-stale: update comment to reference livefetch v2 (not retry)
  as the patch that prevents recurrence

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 21:46:35 +01:00
Sven
c4f654fe9a fix(webmention): validate live page has .h-entry before processing
Root cause: when the livefetch got a 200 response that was actually an
error page (nginx 502 HTML, login redirect, error template), it had no
.h-entry so extractLinks found 0 links — permanently marking the post
as sent with empty results.

Changes:
- livefetch v2: check fetched HTML contains "h-entry" before using it;
  if missing, skip and retry next poll instead of falling back to stored
  content (which also lacks microformat links for likes/reposts/bookmarks)
- livefetch v2: can detect and upgrade from v1 patch in-place
- reset-stale v9: also matches the v1.0.6+ detail format (empty arrays)
  to catch posts stuck by the error-page bug

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 14:02:51 +01:00
Sven
42ddf89f90 chore: cleanup 2026-03-19 10:47:02 +01:00
Sven
868be4905c chore(deps): update indiekit-endpoint-activitypub to 842fc5a
Pulls AP like detection — likes of Mastodon/AP URLs are now sent as
proper Like activities; likes of regular URLs remain bookmark-style.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 08:50:27 +01:00
Sven
e193c20a50 chore(deps): update indiekit-endpoint-activitypub to 03c4ba4
Pulls fix for OG image URL — slug-only path (/og/{slug}.png)
instead of date-prefixed path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 06:38:07 +01:00
Sven
8e304e4fc1 docs: document likes-as-bookmarks, OG images, and announce revert
- Update README: likes delivered as bookmarks, announces use upstream
  addressing, OG images added to AP objects
- Update fork reference to 45f8ba9
- Remove unused patch-ap-like-announce-addressing.mjs (now in fork)
- Update package-lock.json for new fork commit

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 01:39:08 +01:00
Sven
7419e37832 docs: add blog theme soft-delete and content-warning to changelog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 01:00:43 +01:00
Sven
e7bc37f282 chore: merge upstream AP fork v2.13–v2.15.4, remove 5 obsolete patches
Upstream features merged into svemagie/indiekit-endpoint-activitypub:
- v2.13.0: FEP-8fcf/fe34, custom emoji, manual follow approval
- v2.14.0: server blocking, Redis caching, key refresh, async inbox
- v2.15.0-4: outbox failure handling, reply forwarding, CW property,
  soft-delete filtering, as:Endpoints stripping

Patches removed (now baked into fork):
- patch-ap-object-url-trailing-slash
- patch-ap-normalize-nested-tags
- patch-ap-like-announce-addressing
- patch-inbox-skip-view-activity-parse
- patch-inbox-ignore-view-activity

Patches updated:
- patch-ap-allow-private-address: match v2.15 comment style

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 00:49:13 +01:00
Sven
e0e78c6296 docs: document Like/Announce addressing patch and update fork reference
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 00:17:52 +01:00
Sven
bee8df214d fix: add cc:followers addressing to AP Like/Announce activities
Like and Announce activities were missing the followers collection in
their to/cc addressing. Mastodon shared inboxes silently drop activities
without cc:followers, so likes and reposts were delivered but never
appeared on remote instances.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 00:11:48 +01:00
Sven
bd255cc03f docs: document YouTube likes store integration and draft behavior
- Update sync flow diagram to show store.createFile step
- Note draft status and "Title - Author" content format
- Add reset route to routes table

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 23:01:16 +01:00
Sven
5925d712a1 chore: update youtube endpoint fork (store fix + draft support)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 22:49:16 +01:00
Sven
817145e84a chore: update package-lock for youtube endpoint fork (c0563cf)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 22:09:10 +01:00
Sven
c13b8a02dd docs: add YouTube likes sync section to README
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 21:43:31 +01:00
Sven
4dabb32ca1 chore: update package-lock.json for forked youtube endpoint
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 21:07:27 +01:00
Sven
f7e8eba46a feat: integrate YouTube likes sync via forked endpoint
Switch @rmdes/indiekit-endpoint-youtube to forked repo with OAuth 2.0
liked-videos sync. Add OAuth client config and likes sync settings.
Also document outgoing webmentions architecture in README.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 21:05:15 +01:00
Sven
e5b0db3d00 chore: update README.md with activitpub implementation 2026-03-18 20:07:21 +01:00
Sven
3c8c027f5d fix(patches): force unlisted visibility on OwnYourSwarm checkin posts
OwnYourSwarm sends visibility:"private" which Indiekit doesn't recognise,
causing checkin posts to slip through syndication and AP federation guards.
Remove the !hasVisibility condition so checkins are always forced to
"unlisted" regardless of what OwnYourSwarm sends.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 19:49:10 +01:00
Sven
1ad3bae3fc fix(patches): restore lost AP patches and fix broken patch chain
The signatureTimeWindow patch was deleted in e52e98c5c (assumed fixed
in fork), but the lockfile still pins the fork to v2.10.1 which lacks
it. This broke the patch-ap-allow-private-address patch chain: it
expected signatureTimeWindow in its OLD_SNIPPET, never matched, and
silently skipped — leaving the server without both signatureTimeWindow
AND allowPrivateAddress. Without allowPrivateAddress, Fedify's SSRF
guard blocks own-site URL resolution (blog.giersig.eu → 10.100.0.10),
breaking federation delivery.

- Fix patch-ap-allow-private-address to handle fresh v2.10.1 (adds
  both signatureTimeWindow and allowPrivateAddress in one step)
- Restore patch-ap-object-url-trailing-slash (also lost in e52e98c5c)
- Add both patches to postinstall and serve scripts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 19:18:40 +01:00
Sven
533557ad43 fix(config): change CV editor mountPath to /cv-editor to avoid nginx conflict
The static Eleventy CV page at /cv was being served by nginx try_files
before the request could fall through to the @indiekit proxy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 18:50:42 +01:00
Sven
95f42df216 fix(patches): preserve raw body bytes for Fedify HTTP Signature verification
The inbox View-skip patch buffers the request stream but was not saving
the original bytes as req._rawBody. Without them, fromExpressRequest
falls back to JSON.stringify(req.body) which can alter byte layout,
breaking the Digest check in Fedify's HTTP Signature verification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 18:34:02 +01:00
Sven
3b5d4f1243 fix(start): probe webmention-sender endpoint for readiness, not /status
The root /status responds before MongoDB collections and plugin routes
are fully initialised, causing 502 on the first poll. Now probes the
plugin's own /api/status which only responds once everything is ready.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 18:00:40 +01:00
Sven
8f8c632e87 fix(start): add 5s delay after readiness check before first poll
The /status endpoint becomes available before all plugin routes are
fully mounted, causing a 502 on the first webmention-sender POST.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 17:56:43 +01:00
Sven
ad1d82fc6f fix(patches): bump stale-reset migration to v8
Posts were marked as webmention-sent with 0/0/0 results during the
SyntaxError period. The v7 migration already ran before those posts
were processed, so bump to v8 to reset them for retry now that the
livefetch patch is fixed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 17:52:56 +01:00
Sven
9662db1ec3 fix(patches): correct template literal escaping in livefetch patch
The nested template literal in the patch output had over-escaped regex
and backticks (\\\\/ instead of \\/, \\\` instead of \`), producing
invalid JS that caused a SyntaxError at runtime.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 17:41:47 +01:00