Commit Graph

367 Commits

Author SHA1 Message Date
Sven
53102a03b0 fix: fall back to create when updateFile gets 404 from Gitea
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m15s
Posts that exist in MongoDB but not in Gitea (e.g. due to a previous
failed write) caused HTTP 500 on re-publish: updateFile() tried to read
the file's SHA, got 404, and threw instead of creating. Now detects
Not Found and falls through to a create-style PUT (no sha field).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 16:37:53 +02:00
Sven
263e6e081a feat: dispatch Gitea workflow_dispatch after each Micropub action
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m15s
Gitea Contents API commits don't trigger on:push CI workflows.
Patches action.js to fire a workflow_dispatch to giersig.eu/indiekit-blog
after every create/update/delete/undelete so the Eleventy build runs
immediately after a post is published.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 16:24:39 +02:00
373e0c4be8 docs: add Gitea store, dispatch, and server push technique from blog CLAUDE.md
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m16s
2026-03-31 16:12:45 +02:00
78fbd8de8a fix: wire GITEA_BASE_URL into store-github config 2026-03-31 16:06:43 +02:00
Sven
0bac69090d fix: rewrite gitea.giersig.eu to internal IP before npm install
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m15s
npm fetches git deps before node_modules exist, so git URL rewriting
must happen in preinstall. Detects jail env via INDIEKIT_BIND_HOST /
INTERNAL_FETCH_URL — no-ops on local dev.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 15:09:14 +02:00
Sven
2d5b713b7d chore: point svemagie fork deps and docs at Gitea
Some checks failed
Deploy Indiekit Server / deploy (push) Failing after 7s
- Switch 4 svemagie fork deps from github: shorthand to git+https://gitea.giersig.eu/svemagie/...
- Add patch-store-github-error-message.mjs to replace hardcoded github.com token URL with gitea.giersig.eu
- Update CLAUDE.md and README.md fork dependency docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 15:05:49 +02:00
Sven
18c1b3aca1 fix: read store-github user/repo from env for Gitea
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m14s
Hardcoded user: githubUsername (svemagie) and repo: "blog" were wrong
for Gitea where the org is giersig.eu and the repo is indiekit-blog.
Now reads from GITEA_CONTENT_USER / GITEA_CONTENT_REPO env vars with
sensible defaults.

Set in .env on server:
  GITEA_CONTENT_USER=giersig.eu
  GITEA_CONTENT_REPO=indiekit-blog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 13:33:01 +02:00
Sven
3f63e84cd6 ci: use internal host IP 10.100.0.1 for SSH (hairpin NAT fix)
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m11s
Runner is on the internal network — connecting to the public domain
fails due to hairpin NAT, same as the syndication webhook.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 13:21:41 +02:00
Sven
9cbf574b15 ci: use SSH_PORT secret instead of hardcoded port 222
All checks were successful
Deploy Indiekit Server / deploy (push) Successful in 1m16s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 13:20:07 +02:00
Sven
59ea628595 ci: replace GitHub Actions deploy with Gitea FreeBSD runner
Some checks failed
Deploy Indiekit Server / deploy (push) Failing after 3s
- runs-on: freebsd (act_runner host label) instead of ubuntu-latest
- Drop appleboy/ssh-action; use plain ssh in a run step (same pattern as
  indiekit-blog deploy.yml)
- Drop actions/setup-node; no build step on runner side
- On deploy: set git remote to internal Gitea URL, fetch, reset --hard
- npm ci --legacy-peer-deps (postinstall applies all patches automatically)
- .env and SECRET preflight checks; preflight-production-security and
  preflight-mongo-connection before restart
- Async restart via nohup + poll loop (avoids SSH hanging on open stdout)
- add workflow_dispatch trigger

Required repo secrets: SSH_PRIVATE_KEY, SSH_USER, SSH_HOST
(copy values from giersig.eu/indiekit-blog repo secrets)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 13:17:33 +02:00
Sven
6372a9f422 fix: startup and funkwhale listening 2026-03-31 13:01:53 +02:00
a55868be52 README.md aktualisiert 2026-03-31 10:21:46 +02:00
Sven
7ce1112c4e docs(readme): document all patch scripts, production deployment, and env vars
Adds documentation for 14 previously undocumented patch scripts:
- AP threading patches (federation-bridge-base-url, compose-default-checked,
  mastodon-reply-threading)
- Conversations Bluesky patches (cursor-fix, self-filter)
- Micropub session token fix
- Microsub compose draft guard and AP dispatch (fixed // comment prefix)
- Indiekit endpoint URLs protocol fix
- Webmention sender hentry syntax fix
- Endpoint posts fetch diagnostic

Adds Production deployment section covering:
- start.example.sh structure and webmention poller architecture
- FreeBSD rc.d service script (indiekit.rcd.example)
- Prometheus metrics shim (metrics-shim.cjs, port 9209)

Extends Setup section with required and key optional env var tables.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 09:05:16 +02:00
Sven
1628b47cc8 docs(claude): rewrite CLAUDE.md as agent working guide
Rewrote from scratch based on a full read of README.md.
Focus: actionable rules and pitfalls for the AI agent, not human overview.

Covers: patch pattern + rules, two-jail architecture, internal URL
rewriting, nginx/Fedify requirements, MongoDB collections, post-type
discovery table, three compose paths and their checkbox mechanics,
ap_timeline insertion timing, status ID format, JF2→AS2 mapping,
visibility mapping, fork update commands, debugging starting points
(12 symptoms), and full env var reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 08:55:31 +02:00
Sven
5e50d7aceb docs: add CLAUDE.md and memory files for AP threading context
CLAUDE.md covers patch authoring rules, post-type discovery, the two
reply compose paths, ap_timeline insertion timing, fork dependencies,
and common debugging entry points.

memory/ contains three files:
- project_activitypub.md — data flows, syndicator config, all AP patches
- project_architecture.md — FreeBSD jails, MongoDB collections, actor URLs
- feedback_patches.md — patch pattern, known fragile points, threading gotchas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 08:30:32 +02:00
Sven
97d99976ea fix(ap): fix reply threading — pre-check AP syndication and resolve in_reply_to_id immediately
Two bugs caused replies-to-replies to be posted as 'note' type without
ActivityPub federation:

1. patch-ap-compose-default-checked: The AP reader compose form had
   defaultChecked hardcoded to '@rick@rmendes.net' (original dev's handle),
   so the AP syndication checkbox was never pre-checked. Fixed to use
   target.checked from the Micropub q=config response, which already
   carries checked: true for the AP syndicator.

2. patch-ap-mastodon-reply-threading: POST /api/v1/statuses deferred
   ap_timeline insertion until the Eleventy build webhook fired (30–120 s).
   If the user replied to their own new post before the build finished,
   findTimelineItemById returned null → inReplyTo = null → no in-reply-to
   in JF2 → post-type-discovery returned 'note' → reply saved at /notes/
   and sent without inReplyTo in the AP activity, breaking thread display
   on remote servers. Fixed by eagerly inserting the provisional timeline
   item immediately after postContent.create() ($setOnInsert — idempotent;
   syndicator upsert later is a no-op).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 08:12:43 +02:00
Sven
b4fc7ffb4f fix(bluesky): guard uploadMedia() against non-image HTTP responses
uploadMedia() had no content-type check, so an HTML login-redirect response
from an auth-protected internal endpoint was uploaded to Bluesky as a blob
with encoding "text/html". uploadBlob() accepts it, but record validation
rejects the post with 'Expected "image/*" (got "text/html")'.

The patch mirrors the guard already present in uploadImageFromUrl() and also
wraps per-photo uploads in try/catch so one bad photo doesn't abort the
entire syndication — other photos and the post text are still published.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 09:53:46 +02:00
Sven
4aa1554f3a fix(ap): patch federation-bridge to use publication URL as Fedify base URL
Without nginx forwarding Host/X-Forwarded-Proto headers, fromExpressRequest()
builds a wrong URL (e.g. http://127.0.0.1:3000/...) that Fedify doesn't
recognise as its own base URL — so it calls next() and requests fall through
to auth middleware, returning 302 to the login page. This breaks webfinger,
actor lookups, and AP inbox delivery.

The patch overrides the URL construction in createFedifyMiddleware() and
fromExpressRequest() to use the configured publicationUrl as the base,
bypassing the dependency on proxy headers entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 09:51:00 +02:00
Sven
bc76e25361 fix(ap): use photo attachment URL for OG image on photo posts (v2)
/og/{slug}.png is only generated by Eleventy for replies, bookmarks, and
articles — not for photo post types. Photo posts now use properties.photo[0]
directly as the ActivityPub image field; all other post types keep the
/og/{slug}.png fallback. Patch handles all known file states (original
upstream, v1 patched, already v2).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 09:05:37 +02:00
Sven
0886b4b01e Merge remote-tracking branch 'origin/claude/fix-activitypub-og-image-CrCGI' 2026-03-28 19:52:53 +01:00
Claude
f19f7e1741 fix(ap): correct webfinger patch snippet to match current fork (318720c)
The OLD_SNIPPET had wrong indentation (8 spaces vs 6) and was missing the
method guard line added in the fork:
  if (req.method !== "GET" && req.method !== "HEAD") return next();

Without this fix the patch silently skips patching and webfinger continues
to return 302 → 401 on fediverse delivery.

https://claude.ai/code/session_0124D41vdLYE3DkJxhPqYthX
2026-03-28 18:51:16 +00:00
svemagie
7dab6d6ed8 Merge pull request #3 from svemagie/claude/fix-activitypub-og-image-CrCGI
fix(ap): wire og-image and webfinger-before-auth patches into postins…
2026-03-28 19:40:53 +01:00
Claude
7c404a19ed fix(ap): add og-image and webfinger patches dropped during conflict resolution
The Gitea conflict resolution kept main's prom-client/metrics-shim/microsub
changes but dropped our two new AP patch registrations. Re-add them to both
postinstall and serve.

https://claude.ai/code/session_0124D41vdLYE3DkJxhPqYthX
2026-03-28 18:35:29 +00:00
svemagie
bf9af6ff30 Merge branch 'main' into claude/fix-activitypub-og-image-CrCGI 2026-03-28 19:30:46 +01:00
Claude
ebf17341ee fix(ap): wire og-image and webfinger-before-auth patches into postinstall/serve
- Add scripts/patch-ap-og-image.mjs to both postinstall and serve so OG
  preview images are included in ActivityPub activities (flat URL slug
  extraction instead of date-based regex).
- Add scripts/patch-ap-webfinger-before-auth.mjs (new file) to both
  postinstall and serve. Extends contentNegotiationRoutes Fedify delegation
  to also forward /.well-known/* paths, ensuring webfinger is served by
  Fedify before indiekit auth middleware can issue a 302 redirect. Fixes
  401 Unauthorized errors from remote fediverse instances.

https://claude.ai/code/session_0124D41vdLYE3DkJxhPqYthX
2026-03-28 18:25:59 +00:00
Sven
fc77e72a97 chore: add prom-client to package-lock.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 16:12:51 +01:00
Sven
c9084f0586 feat: add Prometheus metrics shim for Indiekit process monitoring
Preloads metrics-shim.cjs via `node --require` into the Indiekit process
so heap, GC, event loop lag, CPU and handle metrics are exposed at
:9209/metrics for Prometheus scraping. Uses prom-client collectDefaultMetrics.

- Add metrics-shim.cjs (prom-client HTTP server, port 9209)
- Add prom-client ^15.1.3 to dependencies
- Wire --require ./metrics-shim.cjs into start.example.sh and npm serve script
- Grafana: NodeJS Application Dashboard (11159) at console.giersig.eu

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 16:06:21 +01:00
Sven
78f0b80dbd chore: update AP fork lockfile pin to 318720c (upstream timeline content synthesis) 2026-03-27 20:33:55 +01:00
svemagie
54d0e46ff8 Merge pull request #2 from svemagie/claude/fix-activitypub-og-image-CrCGI
Claude/fix activitypub og image cr cgi
2026-03-27 20:00:34 +01:00
svemagie
034e944c9d Merge branch 'main' into claude/fix-activitypub-og-image-CrCGI 2026-03-27 20:00:25 +01:00
Sven
e34decb59e docs: document inbox signature suppression and OAuth state fix 2026-03-27 16:55:54 +01:00
Sven
5d7789ead6 fix(oauth): update lockfile pin to b54146c (echo OAuth state parameter) 2026-03-27 16:47:57 +01:00
Sven
9126058df7 fix(docs): INTERNAL_FETCH_URL must point to Indiekit directly, not nginx
nginx HTTP/80 returns 301 → HTTPS; pf has no hairpin NAT for jail traffic,
so following the redirect causes UND_ERR_SOCKET on every internal POST.
Correct value: http://10.100.0.20:3000 (direct to node jail).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 10:53:11 +01:00
Sven
0cc5187990 fix(media-browser): fix mixed-content error causing 'Browse media' to fail
`getEndpointUrls()` resolved relative endpoint paths (e.g. `/media`) using
`getUrl(request)`, which returns `http://` because Express sees HTTP from nginx
without trust proxy. This produced `http://blog.giersig.eu/media` as the
endpoint attribute in the file-input component, causing Safari to block the
fetch as mixed content ('Load failed').

Fix: prefer `application.url` (the configured HTTPS base URL) over
`getUrl(request)` when resolving relative endpoint paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 10:29:02 +01:00
Sven
128ed58e57 chore: update AP fork lockfile pin to 9b6db98 (suppress inbox signature noise) 2026-03-27 10:15:38 +01:00
Sven
02f7db46e1 fix(media): fix image upload size limit and session token bug
- nginx: add client_max_body_size 20M to blog vhost (default 1MB was
  silently killing uploads, returning 413 as a JSON parse error in UI)
- patch: fix session.token → session.access_token in micropub action
  controller; caused Bearer undefined on internal /media fetch, giving
  500s for Micropub clients that upload files directly (OwnYourSwarm)
- npmrc: add sharp_from_source=true to survive future npm install on
  FreeBSD without breaking sharp's native bindings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 10:13:30 +01:00
Sven
88c988947b docs: document livefetch v6, poller direct-connect fix, nginx 444 root cause
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 09:57:31 +01:00
Sven
ea20d10501 chore: build sharp from source for FreeBSD compatibility
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 09:54:44 +01:00
Sven
8e7527ff7f fix(start): webmention poller connects directly to Indiekit, not nginx
nginx port 80 returns 444 (no response) for requests with an unrecognised
Host header. The poller's curl sends Host: 10.100.0.10 (the IP) which
doesn't match any server_name, causing the 180s readiness timeout and
"empty reply from server" on every poll.

Since livefetch v6 builds synthetic HTML from stored properties and no
longer fetches live pages, the poller no longer needs to go through nginx.
It now connects directly to Indiekit on INDIEKIT_BIND_HOST:PORT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 09:52:24 +01:00
Sven
8464e2140c docs: document 2026-03-27 upstream merge and post-merge fixes 2026-03-27 09:49:18 +01:00
Sven
7cca6a605f chore: update AP fork lockfile pin to 69ae731 (fix rate-limit trust proxy error) 2026-03-27 09:43:27 +01:00
Sven
2670cfc9fa chore: update AP fork lockfile pin to 6f76ec4 (fix resolveAuthor import) 2026-03-27 09:40:12 +01:00
Sven
e4da0f99af chore: update AP fork lockfile pin to b595734 (fix missing tokenRequired imports) 2026-03-27 09:35:54 +01:00
Sven
ad58bc45db chore: update AP fork lockfile pin to 230bfd1 (upstream v3.9.x merge)
Fedify 2.1.0, 5 FEPs (Tombstone, Activity Intents, indexable, NodeInfo,
Collection Sync), security audit fixes, architecture refactor (syndicator.js,
batch-broadcast.js, init-indexes.js, CSS split). All fork patches retained:
DM support, pin/unpin, edit post, timeout guard, signed fetch, ap_timeline mirror.
2026-03-27 09:31:51 +01:00
Sven
77442ec837 chore: update AP fork lockfile pin to 42f8c2d (own posts in ap_timeline)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 20:46:14 +01:00
Sven
3b2925d764 docs: document linkify trailing punctuation fix (bd3a623)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 19:57:48 +01:00
Sven
b28443c844 chore: update AP fork lockfile pin to bd3a623 (linkify trailing punct fix)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 19:57:15 +01:00
Sven
e63734ee2a fix(start): kill node process on service stop to prevent orphaned port binding
Trap previously only killed the webmention poller, leaving the node process
orphaned on service stop. On restart, the new node instance would fail to bind
port 3000 (EADDRINUSE) causing 502s with clean logs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 12:12:56 +01:00
Sven
a7b48a2606 docs: document edit post implementation (e319c34)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 11:33:41 +01:00
Sven
0a973266fc chore: update AP fork lockfile pin to e319c34 (edit post)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 11:33:12 +01:00