Files
thepraandClaude Opus 5.5 3df3c333bf Forte in the pasture
Forte 26.9.10, the ActivityPub-only fork of Streams, built from its tag (images/forte: composer on Hubzilla's PHP image)
with portable identities (did:key actors behind /.well-known/apgateway). scenarios/forte.sh, 21 checks: follows, posts,
comments, likes, edits and deletions both ways, a third channel's comment passed on by the thread's owner, the
unfollow, statistics. INTEROP.md records what it does its own way: follows of profile pages, edits only as
Add{Update} under the Create's id, collections that misname themselves.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 10:58:45 +02:00

104 lines
5.5 KiB
Bash

# Forte 26.9.10: the ActivityPub-only fork of Streams (Hubzilla's lineage), with conversation containers (FEP-171b),
# integrity proofs (FEP-8b32) and portable identities. Built from its tag (images/forte: composer's dependencies on
# Hubzilla's PHP image) and run on the shared MySQL (database forte, its schema from install/schema_mysql.sql), its
# configuration written here and mounted as .htconfig.php, its store in a volume, a cron sidecar. It trusts Caddy's CA
# through the bundle mounted as its system store and as library/cacert.pem, the bundle its curl is given instead. Accounts and channels are made through its own PHP; its API
# (api/z/1.0) takes HTTP Basic authentication by the account's email and password.
FORTE_IMAGE=${FORTE_IMAGE:-localhost/pasture-forte:26.9.10}
FORTE_PASSWORD=Forte-Pasture-1
. "$here/peers/shared.sh"
forte_php() { podman exec -u www-data -w /var/www/html pasture-forte php "$@"; }
# forte_eval <php>: PHP run inside Forte, its CLI start done
forte_eval() { podman exec -i -u www-data -w /var/www/html pasture-forte php -r "require_once('vendor/autoload.php'); require_once('include/cli_startup.php'); cli_startup(); $1"; }
forte_config() {
cat <<EOF
<?php
\$db_host = 'mysql';
\$db_port = 3306;
\$db_user = 'pasture';
\$db_pass = 'pasture';
\$db_data = 'forte';
\$db_type = 0;
\$db_vrsn = '8.4';
App::\$config['system']['timezone'] = 'UTC';
App::\$config['system']['baseurl'] = 'https://forte.test';
App::\$config['system']['sitename'] = 'Pasture Forte';
App::\$config['system']['location_hash'] = '$(openssl rand -hex 32)';
App::\$config['system']['transport_security_header'] = 1;
App::\$config['system']['content_security_policy'] = 1;
App::\$config['system']['ssl_cookie_protection'] = 1;
App::\$config['system']['register_policy'] = REGISTER_OPEN;
App::\$config['system']['register_text'] = '';
App::\$config['system']['admin_email'] = 'ftuser@forte.test';
App::\$config['system']['verify_email'] = 0;
App::\$config['system']['php_path'] = 'php';
App::\$config['system']['directory_mode'] = DIRECTORY_MODE_STANDALONE;
EOF
}
forte_up() {
shared_mysql_up
mysql_db forte
mkdir -p "$here/.state/forte"
[ -s "$here/.state/forte/htconfig.php" ] || forte_config > "$here/.state/forte/htconfig.php"
podman image exists "$FORTE_IMAGE" || podman build -q -t "$FORTE_IMAGE" "$here/images/forte" >/dev/null
podman volume exists pasture-forte-store || podman volume create --label pasture=1 pasture-forte-store >/dev/null
podman run -d --replace --name pasture-forte --network $net --label pasture=1 \
-v "$here/.state/forte/htconfig.php:/var/www/html/.htconfig.php:z,ro" -v pasture-forte-store:/var/www/html/store \
-v "$ca/bundle.pem:/etc/ssl/certs/ca-certificates.crt:z,ro" -v "$ca/bundle.pem:/var/www/html/library/cacert.pem:z,ro" "$FORTE_IMAGE" >/dev/null
podman exec -u root pasture-forte sh -c 'mkdir -p "/var/www/html/store/[data]/smarty3" /var/www/html/cache/smarty3 \
&& chown -R www-data /var/www/html/store /var/www/html/cache'
if [ "$(podman exec pasture-mysql mysql -upasture -ppasture -N forte -e "show tables like 'account'" 2>/dev/null)" != "account" ]; then
podman exec pasture-forte cat /var/www/html/install/schema_mysql.sql | podman exec -i pasture-mysql mysql -upasture -ppasture forte 2>/dev/null
fi
for _ in $(seq 1 60); do
site forte.test -s -o /dev/null -w '%{http_code}' https://forte.test:6443/.well-known/nodeinfo 2>/dev/null | grep -q 200 && break
sleep 2
done
podman run -d --replace --name pasture-forte-cron --network $net --label pasture=1 --volumes-from pasture-forte \
-u www-data -w /var/www/html --entrypoint sh "$FORTE_IMAGE" -c 'while true; do php src/Daemon/Run.php Cron; sleep 60; done' >/dev/null
forte_user ftuser
forte_user ftfriend
echo "forte: https://forte.test:6443"
}
# forte_user <nick>: an account (<nick>@forte.test, the pasture's password; the first is the server's administrator,
# as admin_email names it) with a public channel of that name that takes connections without asking (autoperms, as its
# roles that approve by themselves set it: "social" leaves each one pending)
forte_user() {
forte_eval "
use Code\\Lib\\Account; use Code\\Lib\\Channel;
\$email = '$1@forte.test';
\$r = q(\"select account_id from account where account_email = '%s'\", dbesc(\$email));
if (! \$r) {
\$a = Account::create(['email' => \$email, 'password' => '$FORTE_PASSWORD', 'password2' => '$FORTE_PASSWORD']);
if (empty(\$a['success'])) { echo 'account: ' . \$a['message'] . PHP_EOL; exit(1); }
\$account_id = \$a['account']['account_id'];
} else
\$account_id = \$r[0]['account_id'];
q('update account set account_flags = 0 where account_id = %d', intval(\$account_id));
\$c = Channel::from_username('$1');
if (! \$c) {
\$x = Channel::create(['account_id' => \$account_id, 'nickname' => '$1', 'name' => '$1', 'permissions_role' => 'social', 'publish' => 1]);
if (empty(\$x['success'])) { echo 'channel: ' . \$x['message'] . PHP_EOL; exit(1); }
\$c = Channel::from_username('$1');
}
set_pconfig(\$c['channel_id'], 'system', 'autoperms', 1);
echo 'ok ' . \$c['channel_id'] . PHP_EOL;
"
}
# forte_web <nick> <curl args...>: a request to Forte's web pages as <nick>, signed in through its login form (liking,
# dropping)
forte_web() {
local nick=$1 jar="$here/.state/forte/$1.cookies"; shift
if ! site forte.test -s -b "$jar" https://forte.test:6443/stream 2>/dev/null | grep -qi logout; then
rm -f "$jar"
site forte.test -s -o /dev/null -c "$jar" -X POST https://forte.test:6443/login -d auth-params=login \
-d "username=$nick@forte.test" --data-urlencode "password=$FORTE_PASSWORD"
fi
site forte.test -s -b "$jar" -c "$jar" "$@"
}