svemagie 5f4d8ca5e8 fix: detect article/note post type via permalink for AI frontmatter
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>
2026-03-12 08:11:28 +01:00

Indieweb/kit Blog Server

Admin login

  • The IndieKit admin uses root auth/session paths (for example: /session/login, /auth, /auth/new-password).
  • Login uses PASSWORD_SECRET (bcrypt hash), not INDIEKIT_PASSWORD.
  • If no PASSWORD_SECRET exists yet, open /auth/new-password once to generate it.
  • If login is blocked because PASSWORD_SECRET is missing/invalid, set INDIEKIT_ALLOW_PASSWORD_SETUP=1 temporarily, restart, generate a new hash via /auth/new-password, set PASSWORD_SECRET to that hash, then remove INDIEKIT_ALLOW_PASSWORD_SETUP.
  • If login appears passwordless, first check for an existing authenticated session cookie. Use /session/logout to force a fresh login challenge.
  • Upstream IndieKit auto-authenticates in dev mode (NODE_ENV=development). This repository patches that behavior so dev auto-auth only works when INDIEKIT_ALLOW_DEV_AUTH=1 is explicitly set.
  • Production startup now fails closed when auth/session settings are unsafe (NODE_ENV not production, INDIEKIT_ALLOW_DEV_AUTH=1, weak SECRET, missing/invalid PASSWORD_SECRET, or empty-password hash).
  • Post management UI should use /posts (@indiekit/endpoint-posts.mountPath).
  • Do not set post-management mountPath to frontend routes like /blog, otherwise backend publishing can be shadowed by the public site.

Backend endpoints

  • Configured endpoint mount paths:
  • Posts management: /posts
  • Files: /files
  • Webmentions moderation + API: /webmentions
  • Webmentions proxy API: /webmentions-api
  • Webmention sender + API: /webmention-sender
  • Homepage builder UI + API: /homepage
  • Conversations + API: /conversations
  • GitHub activity + API: /github
  • Funkwhale activity + API: /funkwhale
  • Last.fm activity + API: /lastfmapi
  • Podroll dashboard + API: /podrollapi
  • ActivityPub federation + admin reader: /activitypub
  • ActivityPub discovery: /.well-known/webfinger, /nodeinfo/2.1

MongoDB

  • Preferred: set MONGO_USERNAME and MONGO_PASSWORD explicitly; config builds the URL from MONGO_USERNAME, MONGO_PASSWORD, MONGO_HOST, MONGO_PORT, MONGO_DATABASE, MONGO_AUTH_SOURCE.
  • You can still use a full MONGO_URL (example: mongodb://user:pass@host:27017/indiekit?authSource=admin).
  • If both MONGO_URL and MONGO_USERNAME/MONGO_PASSWORD are set, decomposed credentials take precedence by default to avoid stale URL mismatches. Set MONGO_PREFER_URL=1 to force MONGO_URL precedence.
  • Startup scripts now fail fast when MONGO_URL is absent and MONGO_USERNAME is missing, to avoid silent auth mismatches.
  • Startup now runs scripts/preflight-mongo-connection.mjs before boot. Preflight is strict by default and aborts start on Mongo auth/connect failures; set REQUIRE_MONGO=0 to bypass strict mode intentionally.
  • For MongoServerError: Authentication failed, first verify MONGO_PASSWORD, then try MONGO_AUTH_SOURCE=admin.

Content paths

  • This setup writes post files to the content repo blog under content/.
  • Photo upload binaries are written to images/{filename} and published at ${PUBLICATION_URL}/images/{filename}.
  • Current paths in publication.postTypes are:
  • content/articles/{slug}.md
  • content/notes/{slug}.md
  • content/bookmarks/{slug}.md
  • content/likes/{slug}.md
  • content/photos/{slug}.md
  • content/replies/{slug}.md
  • content/pages/{slug}.md
  • If these paths do not match the content repo structure, edit/delete actions can fail with GitHub Not Found.
  • Reposts are configured as a dedicated post type (repost) and stored at content/reposts/{slug}.md.

Post URLs

  • Current post URLs in publication.postTypes are:
  • https://blog.giersig.eu/articles/{slug}/
  • https://blog.giersig.eu/notes/{slug}/
  • https://blog.giersig.eu/bookmarks/{slug}/
  • https://blog.giersig.eu/likes/{slug}/
  • https://blog.giersig.eu/photos/{slug}/
  • https://blog.giersig.eu/replies/{slug}/
  • https://blog.giersig.eu/{slug}/ (page post type)

GitHub tokens

  • Recommended for two-repo setups:
  • GH_CONTENT_TOKEN: token for content repo (blog), used by @indiekit/store-github.
  • GH_ACTIVITY_TOKEN: token for GitHub dashboard/activity endpoint, used by @rmdes/indiekit-endpoint-github.
  • GITHUB_USERNAME: GitHub user/owner name.
  • Backward compatibility: if GH_CONTENT_TOKEN or GH_ACTIVITY_TOKEN are not set, config falls back to GITHUB_TOKEN.

Listening tokens

  • Funkwhale endpoint requirements:
  • FUNKWHALE_INSTANCE (for example https://your-funkwhale.example, root server URL only)
  • FUNKWHALE_USERNAME
  • FUNKWHALE_TOKEN (read API token)
  • Last.fm endpoint requirements:
  • LASTFM_API_KEY
  • LASTFM_USERNAME
  • Listening endpoint plugins target Node.js 20+; older runtimes can produce inconsistent fetch/JSON behavior.
  • If FUNKWHALE_INSTANCE points to a host that does not expose Funkwhale's API routes, API responses now degrade to empty data instead of repeated 500 errors.
  • If these variables are missing, the endpoints still exist but return empty activity until credentials are configured.

Podroll endpoint

  • Podroll endpoint is enabled via @rmdes/indiekit-endpoint-podroll and mounted at /podrollapi by default.
  • Optional environment variables:
  • PODROLL_MOUNT_PATH (default /podrollapi)
  • PODROLL_EPISODES_URL (FreshRSS greader endpoint URL used for episode sync)
  • PODROLL_OPML_URL (FreshRSS OPML export URL used for podcast source sync)
  • If PODROLL_EPISODES_URL and PODROLL_OPML_URL are not set, the endpoint still loads and can be configured from its admin dashboard.

Webmention sender

  • Webmention sender endpoint is enabled via @rmdes/indiekit-endpoint-webmention-sender and mounted at /webmention-sender by default.
  • Optional environment variables:
  • WEBMENTION_SENDER_MOUNT_PATH (default /webmention-sender)
  • WEBMENTION_SENDER_TIMEOUT (default 10000, endpoint discovery timeout in milliseconds)
  • WEBMENTION_SENDER_USER_AGENT (default ${SITE_NAME} Webmention Sender)
  • Startup polling loop variables (used by start.example.sh):
  • WEBMENTION_SENDER_AUTO_POLL (default 1, set 0 to disable)
  • WEBMENTION_SENDER_POLL_INTERVAL (default 300, seconds)
  • WEBMENTION_SENDER_HOST (default 127.0.0.1)
  • WEBMENTION_SENDER_PORT (default ${PORT} or 3000)
  • WEBMENTION_SENDER_ORIGIN (optional JWT me claim override, defaults PUBLICATION_URL -> SITE_URL)
  • WEBMENTION_SENDER_ENDPOINT (optional full URL override)
  • POST /webmention-sender requires authentication (update scope) and sends pending webmentions for unpublished targets.

Webmentions proxy

  • Webmentions proxy endpoint is enabled via @rmdes/indiekit-endpoint-webmentions-proxy and mounted at /webmentions-api by default.
  • Optional environment variables:
  • WEBMENTIONS_PROXY_MOUNT_PATH (default /webmentions-api)
  • WEBMENTIONS_PROXY_CACHE_TTL (default 60, cache TTL in seconds)
  • Uses existing WEBMENTION_IO_TOKEN and WEBMENTION_IO_DOMAIN configuration for upstream webmention.io requests.
  • Public JSON API route: GET /webmentions-api/api/mentions (supports page, per-page, target, wm-property query parameters).

ActivityPub

  • ActivityPub federation is enabled via @rmdes/indiekit-endpoint-activitypub.
  • Actor handle resolution order is: AP_HANDLE, then ACTIVITYPUB_HANDLE, then GITHUB_USERNAME, then publication hostname first label.
  • Actor profile seed values come from AUTHOR_NAME, AUTHOR_BIO, AUTHOR_AVATAR, and SITE_DESCRIPTION.
  • AUTHOR_AVATAR can be absolute (https://...) or slash-relative (/images/avatar.jpg); startup normalizes it to an absolute URL.
  • Optional ActivityPub variables:
  • AP_ALSO_KNOWN_AS (Mastodon migration alias URL)
  • AP_LOG_LEVEL (debug|info|warning|error|fatal, default info)
  • AP_DEBUG (1 or true enables debug dashboard)
  • AP_DEBUG_PASSWORD (required when debug dashboard is enabled)
  • REDIS_URL (recommended for production delivery queue durability)
  • Startup preflight scripts/preflight-activitypub-rsa-key.mjs ensures ap_keys contains a usable RSA key pair (publicKeyPem + privateKeyPem) so outgoing inbox deliveries are HTTP-signed and not rejected with Request not signed.
  • Startup preflight scripts/preflight-activitypub-profile-urls.mjs normalizes existing ActivityPub profile URL fields in MongoDB (url, icon, image, alsoKnownAs) so WebFinger/actor responses do not fail on invalid URL values.
  • The ActivityPub private-url docloader patch (scripts/patch-endpoint-activitypub-private-url-docloader.mjs) allows Fedify lookups for your own publication hostname when split-horizon DNS resolves it to a private jail IP.
  • The ActivityPub locale patch creates/repairs locales/de.json from locales/en.json so backend UI keys do not render as raw activitypub.* translation strings when SITE_LOCALE=de.
  • Quick verification commands:
  • curl -s "https://blog.giersig.eu/.well-known/webfinger?resource=acct:<handle>@blog.giersig.eu" | jq .
  • curl -s -H "Accept: application/activity+json" "https://blog.giersig.eu/" | jq .
  • curl -s "https://blog.giersig.eu/nodeinfo/2.1" | jq .
  • If a reverse proxy serves static HTML, ensure AP requests are proxied to Indiekit for /activitypub*, /.well-known/*, /nodeinfo/*, and content-negotiated Accept: application/activity+json / application/ld+json requests on / and post URLs.

Startup script

  • start.sh is intentionally ignored by Git (.gitignore) so server secrets are not committed.
  • Use start.example.sh as the tracked template and keep real credentials in environment variables (or .env on the server).
  • Startup scripts parse .env with the dotenv parser (not shell source), so values containing spaces are handled safely.
  • start.example.sh includes an optional background webmention sender polling loop for bare-metal deployments (including FreeBSD jails).
  • For FreeBSD service management, use indiekit.rcd.example as a template for /usr/local/etc/rc.d/indiekit.
  • Important: do not use daemon -r in the rc.d command args. Let service indiekit restart control restart behavior; -r can keep the supervisor alive during stop/restart.
  • The rc.d template uses daemon supervisor pidfile -P (and child pidfile -p) and supports indiekit_stop_timeout in rc.conf (default 20 seconds).
  • FreeBSD rc.d install example:
install -m 0555 /usr/local/indiekit/indiekit.rcd.example /usr/local/etc/rc.d/indiekit
sysrc indiekit_enable=YES
service indiekit restart
  • FreeBSD jail env example for auto-send polling:
SITE_URL=https://blog.example.net
PORT=3000

WEBMENTION_SENDER_AUTO_POLL=1
WEBMENTION_SENDER_POLL_INTERVAL=300
WEBMENTION_SENDER_HOST=127.0.0.1
WEBMENTION_SENDER_PORT=3000
WEBMENTION_SENDER_MOUNT_PATH=/webmention-sender

# Optional overrides
# WEBMENTION_SENDER_ORIGIN=https://blog.example.net
# WEBMENTION_SENDER_ENDPOINT=http://127.0.0.1:3000/webmention-sender
  • Startup scripts run preflight + patch helpers before boot (scripts/preflight-production-security.mjs, scripts/preflight-mongo-connection.mjs, scripts/preflight-activitypub-rsa-key.mjs, scripts/preflight-activitypub-profile-urls.mjs, scripts/patch-lightningcss.mjs, scripts/patch-endpoint-media-scope.mjs, scripts/patch-endpoint-media-sharp-runtime.mjs, scripts/patch-frontend-sharp-runtime.mjs, scripts/patch-endpoint-files-upload-route.mjs, scripts/patch-endpoint-files-upload-locales.mjs, scripts/patch-endpoint-activitypub-locales.mjs, scripts/patch-endpoint-activitypub-docloader-loglevel.mjs, scripts/patch-endpoint-activitypub-private-url-docloader.mjs, scripts/patch-endpoint-activitypub-migrate-alias-clear.mjs, scripts/patch-endpoint-homepage-locales.mjs, scripts/patch-frontend-serviceworker-file.mjs, scripts/patch-endpoint-comments-locales.mjs, scripts/patch-conversations-collection-guards.mjs, scripts/patch-indiekit-routes-rate-limits.mjs, scripts/patch-indiekit-error-production-stack.mjs, scripts/patch-indieauth-devmode-guard.mjs, scripts/patch-listening-endpoint-runtime-guards.mjs).
  • The production security preflight blocks startup on insecure auth/session configuration and catches empty-password bcrypt hashes.
  • One-time recovery mode is available with INDIEKIT_ALLOW_PASSWORD_SETUP=1 to bootstrap/reset PASSWORD_SECRET when locked out. Remove this flag after setting a valid hash.
  • The media scope patch fixes a known upstream issue where file uploads can fail if the token scope is create update delete without explicit media.
  • The ActivityPub RSA key preflight repairs or creates a usable type="rsa" key document in ap_keys, so outgoing federation requests can be signed and accepted by stricter inboxes.
  • The ActivityPub profile URL preflight repairs invalid URL fields in the ap_profile document (for example relative icon paths), preventing /.well-known/webfinger and actor responses from failing with TypeError: Invalid URL.
  • The media sharp runtime patch makes image transformation resilient on FreeBSD: if sharp cannot load, uploads continue without resize/rotation instead of crashing the server process.
  • The frontend sharp runtime patch makes icon generation non-fatal on FreeBSD when sharp cannot load, preventing startup crashes in asset controller imports.
  • The files upload route patch fixes browser multi-upload by posting to /files/upload (session-authenticated) instead of direct /media calls without bearer token.
  • The files upload locale patch adds missing files.upload.dropText/files.upload.browse/files.upload.submitMultiple labels in endpoint locale files so UI text does not render raw translation keys.
  • The ActivityPub locale patch backfills missing de locale keys from the endpoint's en locale and applies German admin title labels for notifications/profile.
  • The comments locale patch backfills missing comments endpoint locale files, adds translations for de/es/fr/nl/pt/sv, and localizes dashboard labels that were hardcoded in the comments template.
  • The frontend serviceworker patch ensures @indiekit/frontend/lib/serviceworker.js exists at runtime, forces network-only handling for /auth and /session pages, patches frontend layout templates to unregister stale service workers and clear caches on load, and suppresses sidebar rendering whenever app--minimalui is present.
  • The conversations guard patch prevents Cannot read properties of undefined (reading 'find') when the conversation_items collection is temporarily unavailable.
  • The indiekit routes rate-limit patch (ported from rmdes/indiekit-cloudron) keeps strict limits on /session/*, applies generous limits to public API/well-known routes, and removes extra rate limiting from authenticated routes to avoid admin-side 429 spikes.
  • The indiekit error stack patch (ported from rmdes/indiekit-cloudron) suppresses stack traces in production error pages/JSON responses to avoid leaking internal runtime details.
  • The indieauth dev-mode guard patch prevents accidental production auth bypass by requiring explicit INDIEKIT_ALLOW_DEV_AUTH=1 to enable dev auto-login, and broadens safe local redirect validation to allow common path characters (-, ., %) used by routes such as /auth/new-password.
Description
Indiekit deployment config
https://blog.giersig.eu
Readme 65 MiB
Languages
JavaScript 98.3%
Shell 1.7%