# 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 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 < "$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 : an account (@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 : a request to Forte's web pages as , 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" "$@" }