# Friendica 2026.05: a social network speaking ActivityPub beside its own protocols. Posts (Notes, and Articles when # titled), comments, likes and dislikes, follows, events. The official image on the shared MySQL (database friendica), # installed by its autoinstall; its worker is the image's own daemon (cron.sh) in a sidecar sharing its files, or # nothing federates. Its cache, locks and sessions live in the shared Redis (db 7), as upstream's compose has them, so # both containers share them. It trusts Caddy's CA through the bundle mounted as its system store. Its API (Mastodon's # and its own) takes HTTP Basic authentication, so a user's token is "nick:password". FRIENDICA_IMAGE=${FRIENDICA_IMAGE:-docker.io/library/friendica:2026.05-apache} FRIENDICA_PASSWORD=Friendica-Pasture-1 . "$here/peers/shared.sh" friendica_env() { echo -e "MYSQL_HOST=mysql\nMYSQL_USER=pasture\nMYSQL_PASSWORD=pasture\nMYSQL_DATABASE=friendica" echo -e "FRIENDICA_ADMIN_MAIL=admin@friendica.test\nFRIENDICA_URL=https://friendica.test\nFRIENDICA_TZ=UTC\nFRIENDICA_LANG=en" echo -e "FRIENDICA_SITENAME=Pasture Friendica\nREDIS_HOST=redis\nREDIS_DB=7" # no check that an email's or a URL's domain resolves (friendica.test does only once Caddy names it) echo "FRIENDICA_NO_VALIDATION=1" } friendica_console() { podman exec -u www-data pasture-friendica php /var/www/html/bin/console.php "$@"; } friendica_up() { shared_mysql_up shared_redis_up mysql_db friendica mkdir -p "$here/.state/friendica" friendica_env > "$here/.state/friendica/env" podman volume exists pasture-friendica-html || podman volume create --label pasture=1 pasture-friendica-html >/dev/null podman run -d --replace --name pasture-friendica --network $net --env-file "$here/.state/friendica/env" \ -v pasture-friendica-html:/var/www/html -v "$ca/bundle.pem:/etc/ssl/certs/ca-certificates.crt:z,ro" "$FRIENDICA_IMAGE" >/dev/null for _ in $(seq 1 150); do site friendica.test -s -o /dev/null -w '%{http_code}' https://friendica.test:6443/nodeinfo/2.0 2>/dev/null | grep -q 200 && break sleep 3 done podman run -d --replace --name pasture-friendica-cron --network $net --env-file "$here/.state/friendica/env" \ --volumes-from pasture-friendica --entrypoint /cron.sh "$FRIENDICA_IMAGE" >/dev/null friendica_settle echo "friendica: https://friendica.test:6443" } # a named system account, open registrations, a log, and fruser friendica_settle() { # its autoinstall leaves the system user (uid 0) nameless, so the system account that signs every fetch is never made # and every lookup fails ("Could not find owner for uid 0"); a contact a failed attempt left at its address is dropped podman exec pasture-mysql mysql -upasture -ppasture friendica -e "update user set nickname = 'friendica', username = 'System Account' where uid = 0 and nickname = ''; delete from contact where uid = 0 and self = 0 and url = 'https://friendica.test/friendica'" 2>/dev/null # the worker daemon runs in the sidecar, where the web container cannot see its pid, so a queued job never wakes it # and everything waits for its five-minute cron; declared running, the web container signals it through worker-ipc podman exec pasture-mysql mysql -upasture -ppasture friendica -e "replace into \`key-value\` (k, v, updated_at) values ('worker_daemon_mode', '1', unix_timestamp())" 2>/dev/null friendica_console config config register_policy 2 >/dev/null 2>&1 || true # the image names the log (at notice) but leaves the file for Friendica to make, which it cannot, and logs nothing # until debugging is on podman exec pasture-friendica sh -c 'touch /var/www/html/friendica.log && chown www-data /var/www/html/friendica.log' friendica_console config system debugging 1 >/dev/null 2>&1 || true friendica_user fruser echo "fruser:$FRIENDICA_PASSWORD" > "$here/.state/friendica.token" } # friendica_user : an account with the pasture's password that takes followers without asking. Friendica makes # them locked (its normal page type approves every contact by hand); saved through its API with locked=0 (a number: # "false" and "true" both read as 0), it becomes a "soapbox" page, which approves each follower and follows nobody back. # The "automatic friend" type would follow every follower back as a friend. friendica_user() { friendica_console user add "$1" "$1" "$1@friendica.test" en "" >/dev/null 2>&1 || true friendica_console user password "$1" "$FRIENDICA_PASSWORD" >/dev/null 2>&1 || true podman exec pasture-friendica curl -s -o /dev/null -X PATCH -u "$1:$FRIENDICA_PASSWORD" -H "Host: friendica.test" \ -H "X-Forwarded-Proto: https" -d locked=0 http://localhost/api/v1/accounts/update_credentials # Friendica caches its own users' actors (apcontact) only once something reads them. A Follow that arrives first makes # it fetch the user from itself, signed as that user, and checking that signature builds the actor, which checks the # signature again: the request recurses for about five minutes and holds the sender's Follow past any timeout. # Reading the user's outbox once, unsigned, caches it (its own search answers from the contact and caches nothing). podman exec pasture-friendica curl -s -o /dev/null -H "Host: friendica.test" -H "X-Forwarded-Proto: https" \ -H 'Accept: application/activity+json' "http://localhost/outbox/$1" }