Files
indiekit-server/memory/project_architecture.md
Sven 981e4833d6
Deploy Indiekit Server / deploy (push) Successful in 1m23s
docs: document hairpin NAT problem and git URL rewrite solution
2026-05-14 20:17:31 +02:00

3.8 KiB

Server Architecture

Infrastructure

  • FreeBSD jails — Indiekit runs in an isolated jail
  • nginx — reverse proxy; must forward Host: blog.giersig.eu and X-Forwarded-Proto: https for Fedify to construct correct canonical URLs (see patch-ap-federation-bridge-base-url)
  • MongoDB10.100.0.20:27017, database indiekit, auth source admin
  • Redis — optional; URL via REDIS_URL env var; used for AP activity queue

Publication URLs

  • publicationBaseUrl = https://blog.giersig.eu (from PUBLICATION_URL env or hardcoded default)
  • applicationBaseUrl = same (from INDIEKIT_URL env)
  • GitHub repo: svemagie/blog, branch main

Internal Fetch

Several patches rewrite outbound HTTP fetches to use internal jail addresses instead of going through the public internet / nginx:

  • patch-micropub-fetch-internal-url — Micropub post creation fetches
  • patch-bluesky-syndicator-internal-url — Bluesky syndicator
  • _toInternalUrl() helper in microsub/activitypub controllers

Hairpin NAT — git.wildwuchs.work from inside jails

Problem: pf's rdr rules only fire on $ext_if (vtnet0). Traffic originating from a jail (node-Jail 10.100.0.20, gitea-Jail 10.100.0.90) to the public IP never hits the RDR rule, so https://git.wildwuchs.work is unreachable from inside any jail. This breaks:

  • actions/checkout@v4 in the act_runner (gitea-Jail) — can't clone the repo
  • npm ci fetching git+https://git.wildwuchs.work/... package dependencies (node-Jail)
  • git fetch origin in deploy scripts that use the public URL

Solution — git URL rewrite in each affected jail:

Each jail that runs git operations has a .gitconfig that rewrites the public URL to the internal Gitea address:

[url "http://10.100.0.90:3000/"]
    insteadOf = https://git.wildwuchs.work/
Jail User .gitconfig path
gitea-Jail (act_runner) git (home: /usr/local/git) /usr/local/bastille/jails/gitea/root/usr/local/git/.gitconfig
node-Jail (indiekit) indiekit (home: /usr/local/indiekit) /usr/local/bastille/jails/node/root/usr/local/indiekit/.gitconfig

/etc/hosts in the gitea-Jail also has 10.100.0.90 git.wildwuchs.work for DNS resolution.

If a new jail needs git access to git.wildwuchs.work: add the same [url] block to the relevant user's .gitconfig on the host at /usr/local/bastille/jails/<name>/root/<home>/.gitconfig.

Collections (MongoDB)

Collection Contents
posts Micropub post data (path + properties)
ap_timeline Incoming + outgoing AP posts; key: uid
ap_notifications Mentions, replies, likes, boosts received
ap_followers Follower actor URLs
ap_following Following actor URLs
ap_activities Activity log (outbound + inbound)
ap_profile Own actor profile (name, icon, url)
ap_interactions Likes and boosts performed by own account

ActivityPub Actor

  • Handle: activityPubHandle from AP_HANDLE env → GITHUB_USERNAME (svemagie) → hostname prefix
  • Full handle: @svemagie@blog.giersig.eu
  • Actor URL: https://blog.giersig.eu/activitypub/actor
  • AP objects served at: https://blog.giersig.eu/activitypub/objects/note/{+id}
    • Own reply posts: /activitypub/objects/note/replies/{slug}

Patch Infrastructure

Patches live in scripts/patch-*.mjs. Each script:

  1. Checks if already applied (MARKER string)
  2. Looks for OLD_SNIPPET in node_modules target file
  3. Replaces with NEW_SNIPPET if found
  4. Reports result to stdout

Both postinstall and serve scripts in package.json run all patches in order. Some patches (e.g. patch-microsub-reader-ap-dispatch) only appear in serve, not postinstall. New AP patches are appended at the end of the AP patch chain (after patch-ap-federation-bridge-base-url).