Commit Graph

255 Commits

Author SHA1 Message Date
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
Sven
1f4f55d4fc fix(patches): add internal URL rewrite to webmention livefetch
The livefetch patch was fetching postUrl (the public HTTPS URL) which
fails behind the nginx jail. Now rewrites to INTERNAL_FETCH_URL so the
live page fetch goes through nginx on the internal network. Without
this, likes/reposts/bookmarks fall back to stored content which has no
microformat links, resulting in 0 webmentions sent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 17:23:12 +01:00
Sven
c1197608ef fix(patches): make livefetch handle both original and retry-patched code
The retry and livefetch patches both target the same upstream code block.
If retry runs first (current postinstall order), it transforms the code
into a variant that livefetch couldn't match — silently losing the
"always fetch live page" behavior. Now livefetch detects both the
original upstream code and the retry-patched variant.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 12:05:44 +01:00
Sven
af683d8bb4 chore(patches): restore webmention-sender-retry patch and add to postinstall
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>
2026-03-18 11:57:52 +01:00
Sven
c9c958e9f5 chore: point blogroll dependency to main branch
The bookmark-import branch has been merged to main.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 10:38:34 +01:00
Sven
f275fdef67 fix(webmention-sender): drop internal URL rewriting, fetch public URL directly
The public URL (blog.giersig.eu) is reachable from inside the jail, so
the INTERNAL_FETCH_URL rewriting approach was unnecessary and caused 400/502
errors because 10.100.0.10 does not serve the static blog pages.

Simplify the livefetch patch to fetch postUrl directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 10:28:49 +01:00
Sven
b522662199 fix(webmention-sender): send Host header when fetching via internal URL
When rewriting the public URL to INTERNAL_FETCH_URL for jailed setups,
the Host header becomes the internal IP (e.g. 10.100.0.10) instead of
the public hostname (blog.giersig.eu). nginx uses the Host header for
virtual host routing and returns 400 without the correct value.

Fix: extract the host from the original public postUrl and set it as the
Host header on the internal fetch, so nginx routes to the correct vhost.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 10:02:12 +01:00
Sven
fd045d9c0e fix(webmention-sender): fall back to siteUrl for public base URL rewriting
The livefetch patch used `process.env.PUBLICATION_URL || process.env.SITE_URL`
to determine the public base URL for rewriting to INTERNAL_FETCH_URL. Neither
env var is set — the publication URL comes from indiekit.config.mjs which has
a hardcoded fallback. Without a publicBase, URL rewriting never fired, the
public HTTPS URL was fetched directly, failed inside the FreeBSD jail, and fell
back to stored content.

For interaction posts (repost/bookmark/reply/like), stored content is just the
body text — the target URL (repostOf, bookmarkOf, inReplyTo, likeOf) is only
rendered in the live page via reply-context.njk. So 0 webmentions were sent.

Fix: add `|| siteUrl` as fallback. siteUrl is already in scope (derived from
`publication.me`) and is the correct value when env vars are absent.

Also bump reset-stale migration to v7 so interaction posts incorrectly marked
with 0 results (e.g. 342a5 repost) are reset and retried on next startup.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 09:49:23 +01:00
Sven
059f36bf06 fix(webmention-sender): bump reset-stale migration to v6
Posts processed before the livefetch patch was applied were re-processed
without live page fetching, leaving them with empty results again.
Bumping the migration ID triggers another reset on next startup so the
poller retries them with livefetch active.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 08:48:47 +01:00
Sven
2bfaa27702 fix(webmention-sender): show 'no external links' message for empty detail rows
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>
2026-03-18 08:33:29 +01:00
Sven
4a81b5afab fix: unfurl corrupted self-filter patch script, drop webmention-sender backend patch
The patch script was corrupted (nested snippets embedded inside template
literals). Reconstructed it cleanly with only the two original specs
(scheduler + conversations controller). The webmention-sender backend
patch is removed — self-Bluesky filtering belongs in the blog sidebar
widget, not the backend dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-18 08:04:27 +01:00
Sven
8b06894c01 patch: filter Bluesky self-interactions from webmention dashboard widget 2026-03-18 07:42:08 +01:00
Sven
c5c4d06184 fix: FreeBSD Jails self-linking config 2026-03-17 17:33:57 +01:00
Sven
8b35fc13fd fix(startup): route webmention poller through nginx via INTERNAL_FETCH_URL
Direct connections from the node jail to itself cause empty replies in
FreeBSD jails. Route through nginx (INTERNAL_FETCH_URL=http://10.100.0.10)
instead, which also provides correct Host and X-Forwarded-Proto headers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 16:45:12 +01:00
Sven
421a2944f8 docs: fix nginx proxy Host header to blog.giersig.eu
Internal requests arrive with Host: 10.100.0.10 which Indiekit doesn't
recognize, causing auth redirects. Hardcode the public hostname so
Indiekit's session/token handling works correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 16:34:45 +01:00
Sven
e98c704e78 docs: add server architecture, jail setup, and nginx config to README
Documents the two-jail FreeBSD setup (web + node), INTERNAL_FETCH_URL
mechanism, nginx port 80 internal listener config, and static file
paths at /usr/local/www/blog. Also documents the bluesky-syndicator
and micropub-fetch-internal-url patches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 16:21:58 +01:00
Sven
cc1edd81a8 fix(startup): use configurable bind host instead of hardcoded 127.0.0.1
FreeBSD jails often don't have loopback available. The webmention poller
and readiness check now use INDIEKIT_BIND_HOST (defaults to 127.0.0.1
for backward compat). Set INDIEKIT_BIND_HOST=10.100.0.20 in .env.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 16:09:57 +01:00
Sven
42db9d85b6 fix(patches): rewrite Bluesky syndicator fetch URLs to localhost for jailed setup
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>
2026-03-17 13:19:24 +01:00
Sven
3beac515cd feat: add indiekit-endpoint-readlater 2026-03-17 12:26:40 +01:00
Sven
4955863bb8 fix(startup): add empty body and timeout to webmention poller curl
The bodyless POST (no Content-Type/Content-Length) caused curl error 52
(empty reply from server). Adding `-d ""` sends proper headers and
`--max-time 300` prevents hangs on long-running webmention processing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 12:18:20 +01:00
Sven
c1f9d64401 fix(patches): rewrite livefetch URL to localhost and add timeout
The webmention sender livefetch patch was fetching the public HTTPS URL
which hangs in the jailed setup (port 443 is on the nginx jail). Rewrite
to localhost like all other internal-fetch patches, and add a 15s
AbortController timeout. Bump reset-stale migration to v5 so posts
incorrectly marked as sent with 0/0/0 get retried.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 11:58:35 +01:00
Sven
d501d70e1d fix(patches): restore livefetch patch so webmentions use live page HTML
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>
2026-03-17 09:56:35 +01:00
Sven
fc9afa9bff fix(rssapi): flatten feedTitle, sourceTitle and author in item response
The /rssapi transformation was missing feedTitle, sourceTitle and author
fields that the /news page expects. Map them from the nested blog object.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 09:21:23 +01:00
Sven
cb7b525368 fix(patches): extend internal URL rewrite to auth, token, and media endpoints
Add localhost rewrite for three more self-referential fetches:

- indieauth.js: token exchange during login
- token.js: token introspection on every authenticated request
- media.js: file uploads via media endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:40:59 +01:00
Sven
a2e2764108 fix(patches): extend internal URL rewrite to microsub, activitypub, and @rmdes posts
The localhost rewrite only covered endpoint-syndicate and endpoint-share.
Add coverage for 4 more files that also self-fetch via the public URL:

- @rmdes/indiekit-endpoint-microsub reader.js (2 fetch calls)
- @rmdes/indiekit-endpoint-activitypub compose.js (2 fetch calls)
- @rmdes/indiekit-endpoint-posts utils.js and endpoint.js

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 20:35:50 +01:00