# Relays as PrivaPub uses them (Federation:Relays in appsettings.Pasture.json): Activity-Relay, which forwards what its # subscribers send, and aode-relay, which announces it. For each, the instance actor subscribes, Mastodon subscribes too, # a public post of a Mastodon account nobody here follows reaches PrivaPub's federated timeline through the relay, and a # persona's public post goes to the relay (owner decision 2026-10-06), nothing less public, and reaches Mastodon through # it: forwarded on its FEP-8b32 proof, or announced. Mastodon leaves each relay # after, so the town sees no relayed posts. Needs the relay, aoderelay and mastodon peers. M=https://mastodon.test:6443 mcurl() { curl -sk --resolve mastodon.test:6443:127.0.0.1 "$@"; } . "$here/peers/mastodon.sh" . "$here/peers/relay.sh" . "$here/peers/aoderelay.sh" p_relay_state() { podman exec pasture-mongo mongosh --quiet PrivaPub --eval 'var s=db.RelaySubscription.findOne({ActorURI:"https://'$1'/actor"}); print(s ? s.State : "none")'; } m_relay() { podman exec pasture-mastodon bin/rails runner 'r = Relay.find_or_create_by!(inbox_url: "https://'$1'/inbox"); r.enable! unless r.accepted? || r.pending?' >/dev/null 2>&1; } m_unrelay() { podman exec pasture-mastodon bin/rails runner 'Relay.where(inbox_url: "https://'$1'/inbox").each { |r| r.disable!; r.destroy }' >/dev/null 2>&1; } p_public_has() { [ "$(curl -s -H "$PH" "$P/api/v1/timelines/public?remote=true&limit=40" | j "print(any(s['uri'] == '$1' for s in d))")" = "True" ]; } # relayed_post : a public post of a fresh Mastodon account nobody here follows, by its uri relayed_post() { local who="$1$run" mastodon_user "$who" mcurl -X POST -H "Authorization: Bearer $(mastodon_token "$who")" "$M/api/v1/statuses" -d "status=a post only the $1 brings $run&visibility=public" | j "print(d['uri'])" } echo "relay" PT=$(privapub_token alice_relay) PH="Authorization: Bearer $PT" [ -n "$PT" ] && ok "PrivaPub token for alice_relay" || { ko "PrivaPub token for alice_relay"; return 1; } run=$(date +%s) echo " Activity-Relay" # PrivaPub subscribes a minute after it starts (and again six hours later while unanswered) until_true 60 'relay_subscribers | grep -qx privapub.test' && ok "PrivaPub's instance actor subscribes to Activity-Relay" || ko "PrivaPub never subscribed ($(relay_subscribers | tr '\n' ' '))" # (1 = Accepted) until_true 30 '[ "$(p_relay_state relay.test)" = "1" ]' && ok "and takes its Accept" || ko "PrivaPub's subscription is $(p_relay_state relay.test)" m_relay relay.test until_true 45 'relay_subscribers | grep -qx mastodon.test' && ok "Mastodon subscribes too" || ko "Mastodon never subscribed" s_uri=$(relayed_post relay) until_true 60 'p_public_has "$s_uri"' && ok "a public post of an account nobody here follows reaches the federated timeline, forwarded" || ko "the forwarded post never arrived" [ "$(curl -s -H "$PH" "$P/api/v1/timelines/home?limit=40" | j "print(any(s['uri'] == '$s_uri' for s in d))")" = "False" ] \ && ok "and no one's home" || ko "the relayed post landed in alice's home" # relay_creates : the Creates PrivaPub has queued for relay.test naming the text relay_creates() { podman exec pasture-mongo mongosh --quiet PrivaPub --eval 'print(db.Job.countDocuments({Host:"relay.test", Payload:/Create/, Payload:/'"$1"'/}))'; } # (Mastodon reads a known account's keys again at most daily: made to read alice's anew, with her Ed25519 key) alice_uri="$(curl -s -H "$PH" "$P/api/v1/accounts/verify_credentials" | j "print(d['url'])" | sed 's|/@|/peasants/|')" podman exec pasture-mastodon bin/rails runner "Account.where(uri: \"$alice_uri\").update_all(last_webfingered_at: nil)" >/dev/null 2>&1 r_post=$(curl -s -X POST -H "$PH" "$P/api/v1/statuses" -d "status=a PrivaPub post for the relay $run&visibility=public" | j "print(d['uri'])") curl -s -o /dev/null -X POST -H "$PH" "$P/api/v1/statuses" -d "status=a quiet PrivaPub post $run&visibility=unlisted" until_true 30 '[ "$(relay_creates "a PrivaPub post for the relay $run")" = "1" ]' && ok "alice's public post goes to the relay" || ko "PrivaPub never sent the relay alice's public post" [ "$(relay_creates "a quiet PrivaPub post $run")" = "0" ] && ok "and nothing less public" || ko "PrivaPub sent the relay an unlisted post" # Activity-Relay forwards her Create as she sent it, signed by the relay: Mastodon takes it on its FEP-8b32 proof until_true 60 '[ "$(podman exec pasture-mastodon bin/rails runner "puts Status.exists?(uri: \"$r_post\")" 2>/dev/null | tail -1)" = "true" ]' \ && ok "alice's public post reaches Mastodon through Activity-Relay, on its proof" || ko "Mastodon dropped alice's post forwarded by Activity-Relay" m_unrelay relay.test until_true 45 '! relay_subscribers | grep -qx mastodon.test' && ok "Mastodon leaves Activity-Relay" || ko "Mastodon is still subscribed to Activity-Relay" echo " aode-relay" until_true 30 '[ "$(p_relay_state aoderelay.test)" = "1" ]' && ok "PrivaPub's instance actor subscribes to aode-relay, which accepts" || ko "PrivaPub's aode-relay subscription is $(p_relay_state aoderelay.test)" m_relay aoderelay.test until_true 45 'aoderelay_connected | grep -q mastodon.test' && ok "Mastodon subscribes too" || ko "Mastodon never subscribed to aode-relay ($(aoderelay_connected | tr '\n' ' '))" a_uri=$(relayed_post aoderelay) until_true 60 'p_public_has "$a_uri"' && ok "a post aode-relay announces reaches the federated timeline as its author's" || ko "the announced post never arrived" [ "$(curl -s -H "$PH" "$P/api/v1/timelines/public?remote=true&limit=40" | j "print(any((s.get('reblog') or {}).get('uri') == '$a_uri' for s in d))")" = "False" ] \ && ok "never as the relay's boost" || ko "the relay's announce shows as a boost" # alice's public post reaches Mastodon through aode-relay's announce, though nobody there follows her a_post=$(curl -s -X POST -H "$PH" "$P/api/v1/statuses" -d "status=a PrivaPub post aode-relay brings $run&visibility=public" | j "print(d['uri'])") until_true 60 '[ "$(podman exec pasture-mastodon bin/rails runner "puts Status.exists?(uri: \"$a_post\")" 2>/dev/null | tail -1)" = "true" ]' \ && ok "alice's public post reaches Mastodon through aode-relay" || ko "alice's post never reached Mastodon through aode-relay" m_unrelay aoderelay.test until_true 45 '! aoderelay_connected | grep -q mastodon.test' && ok "Mastodon leaves aode-relay" || ko "Mastodon is still subscribed to aode-relay"