Pasture: Friendica joins, in the scenarios and the town
Friendica 2026.05 on the shared MySQL and Redis, with its worker daemon as a sidecar. friendica_settle repairs what its install leaves: the system user has no name, so the system account that signs its fetches is never made; the web container cannot see the sidecar's daemon, so a queued job waits for the five-minute cron unless the daemon is declared running; its log needs a file and debugging on. Accounts are saved through its API with locked=0 (a number: "true" reads as 0, unlocked), which makes them soapbox pages that take followers without following back, and each one's outbox is read once: a Follow that reaches an account Friendica has not cached makes it fetch the account from itself, signed, and checking that signature recursed for five minutes, holding PrivaPub's first follow past its timeout. scenarios/friendica.sh passes its 25 checks from a clean install: follows and unfollows, posts (a titled one with its title), comments, likes, Friendica's dislike as a downvote, boosts, edits (through its web editor: its Mastodon API never federates one) and deletes, both ways. The town gets a Friendica driver (HTTP Basic, its MySQL read through mysql_json, edits in the web editor, no bookmark on a reply) and specs/friendica-pair.json, which passes its 275 checks. On the way: - the checker knows Friendica's thread model: a non-public reply reaches an account only under posts it holds, and a Friendica account's non-public reply in a thread another server owns reaches nobody else there; - the seeder answers a follow request the target still holds whatever the follower's server says: Friendica reports a follow of someone already following its account as made at once (and shows that persona's followers-only posts while a locked persona still holds the request); - the selftest skips polls where a platform has none; the shared MySQL helpers move to peers/shared.sh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
e485f7bd47
commit
a5d493a8c9
17 files changed
+513
-31
No files matched your search
@@ -0,0 +1,74 @@
|
||||
# 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 <nick>: 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"
|
||||
}
|
||||
Reference in new issue
Block a user