Files
SocialPub/tools/pasture/scenarios/moves.sh
T
thepraandClaude Opus 5.5 7b0377c85f Moves carry follows over; first DMs to Lemmy 0.19 and Mbin go as ChatMessage
Two owner decisions of 2026-10-05, both recorded in ROADMAP:
- After a verified Move the personas following the old account follow the new one, in the same lists, and a mute or
  block of the old account carries over, as Mastodon does it.
- A direct message to one account on a server whose NodeInfo names Lemmy before 1.0 or Mbin goes as a ChatMessage,
  the one place PrivaPub decides by a server's software (invariant 17). G-0008 is closed.

Mbin addresses its private messages to the recipient's profile page, so a Create addressed to a persona's /@name now
reaches the persona. Checked live: moves 8/8, Lemmy 0.19 30/30, Mbin 26/26 with messages both ways. The software
theory runs alone, since every test's peer shares 127.0.0.1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-05 20:57:43 +02:00

59 lines
5.2 KiB
Bash

# Account moves into PrivaPub's view: an account alice follows on GoToSocial moves to a new account there. The new one
# names the old among its aliases (/api/v1/accounts/alias), the old moves (/api/v1/accounts/move), GoToSocial sends its
# Move to the old one's followers, PrivaPub shows the old account moved to the new (`moved`) and moves alice's follow
# there. Needs the gts peer.
G=https://gts.test:6443
gcurl() { curl -k --resolve gts.test:6443:127.0.0.1 "$@"; }
MOVE_PASSWORD='Gts-Pasture-Pass-1!'
# gts_account <name>: a confirmed GoToSocial account and its token
gts_account() {
podman exec pasture-gts /gotosocial/gotosocial admin account create --username "$1" --email "$1@gts.test" --password "$MOVE_PASSWORD" >/dev/null 2>&1
podman exec pasture-gts /gotosocial/gotosocial admin account confirm --username "$1" >/dev/null 2>&1
local app id secret code jar
jar=$(mktemp); app=$(gcurl -s -X POST $G/api/v1/apps -d 'client_name=pasture&redirect_uris=urn:ietf:wg:oauth:2.0:oob&scopes=read+write+follow')
id=$(echo "$app" | j "print(d['client_id'])"); secret=$(echo "$app" | j "print(d['client_secret'])")
gcurl -s -o /dev/null -c "$jar" -b "$jar" "$G/oauth/authorize?client_id=$id&redirect_uri=urn:ietf:wg:oauth:2.0:oob&response_type=code&scope=read+write+follow"
gcurl -s -o /dev/null -c "$jar" -b "$jar" -X POST $G/auth/sign_in --data-urlencode "username=$1@gts.test" --data-urlencode "password=$MOVE_PASSWORD"
code=$(gcurl -s -o /dev/null -w '%{redirect_url}' -c "$jar" -b "$jar" -X POST "$G/oauth/authorize" | sed -n 's/.*[?&]code=\([^&]*\).*/\1/p')
rm -f "$jar"
gcurl -s -X POST $G/oauth/token -d "grant_type=authorization_code&code=$code&client_id=$id&client_secret=$secret&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=read+write+follow" | j "print(d['access_token'])"
}
echo "moves"
PT=$(privapub_token alice)
PH="Authorization: Bearer $PT"
[ -n "$PT" ] && ok "PrivaPub token for alice" || { ko "PrivaPub token"; return 1; }
run=$(date +%s)
old="mvold$run"; new="mvnew$run"
OT=$(gts_account "$old"); NT=$(gts_account "$new")
[ -n "$OT" ] && [ -n "$NT" ] && ok "two GoToSocial accounts, the one that moves and the one it moves to" || { ko "GoToSocial accounts"; return 1; }
echo " following the old account"
old_on_p=$(curl -s -H "$PH" "$P/api/v2/search?q=$old@gts.test&resolve=true&type=accounts" | j "print(d['accounts'][0]['id'])")
curl -s -o /dev/null -X POST -H "$PH" "$P/api/v1/accounts/$old_on_p/follow"
# (GoToSocial makes its accounts locked: the old one approves alice)
until_true 30 '[ -n "$(gcurl -s -H "Authorization: Bearer $OT" "$G/api/v1/follow_requests" | j "print(d[0][\"id\"])")" ]'
gcurl -s -o /dev/null -X POST -H "Authorization: Bearer $OT" "$G/api/v1/follow_requests/$(gcurl -s -H "Authorization: Bearer $OT" "$G/api/v1/follow_requests" | j "print(d[0]['id'])")/authorize"
until_true 45 '[ "$(curl -s -H "$PH" "$P/api/v1/accounts/relationships?id[]=$old_on_p" | j "print(d[0][\"following\"])")" = "True" ]' \
&& ok "alice follows the account that will move" || ko "alice's follow of the old account never took"
echo " the move"
gcurl -s -o /dev/null -X POST -H "Authorization: Bearer $NT" "$G/api/v1/accounts/alias" -H 'Content-Type: application/json' \
-d "{\"also_known_as_uris\":[\"https://gts.test/users/$old\"]}"
moved=$(gcurl -s -o /dev/null -w '%{http_code}' -X POST -H "Authorization: Bearer $OT" "$G/api/v1/accounts/move" -H 'Content-Type: application/json' \
-d "{\"password\":\"$MOVE_PASSWORD\",\"moved_to_uri\":\"https://gts.test/users/$new\"}")
[ "$moved" = "202" ] || [ "$moved" = "200" ] && ok "the old account moves on GoToSocial" || ko "GoToSocial refused the move ($moved)"
until_true 60 '[ "$(curl -s -H "$PH" "$P/api/v1/accounts/$old_on_p" | j "print((d.get(\"moved\") or {}).get(\"acct\"))")" = "$new@gts.test" ]' \
&& ok "PrivaPub shows the old account moved to the new one" || ko "PrivaPub does not show the move ($(curl -s -H "$PH" "$P/api/v1/accounts/$old_on_p" | j "print(d.get('moved'))"))"
# as Mastodon does it (owner decision 2026-10-05): alice's follow moves to the new account, which GoToSocial makes
# locked, so it arrives as a request the new account approves
new_on_p=$(curl -s -H "$PH" "$P/api/v1/accounts/$old_on_p" | j "print((d.get('moved') or {}).get('id'))")
until_true 45 '[ "$(curl -s -H "$PH" "$P/api/v1/accounts/relationships?id[]=$new_on_p" | j "print(d[0][\"following\"] or d[0][\"requested\"])")" = "True" ]' \
&& ok "PrivaPub moves alice's follow to the new account" || ko "alice does not follow the new account"
[ "$(curl -s -H "$PH" "$P/api/v1/accounts/relationships?id[]=$old_on_p" | j "print(d[0]['following'] or d[0]['requested'])")" = "False" ] \
&& ok "and no longer follows the old one" || ko "alice still follows the old account"
until_true 30 '[ -n "$(gcurl -s -H "Authorization: Bearer $NT" "$G/api/v1/follow_requests" | j "print(d[0][\"id\"])")" ]'
gcurl -s -o /dev/null -X POST -H "Authorization: Bearer $NT" "$G/api/v1/follow_requests/$(gcurl -s -H "Authorization: Bearer $NT" "$G/api/v1/follow_requests" | j "print(d[0]['id'])")/authorize"
until_true 45 '[ "$(curl -s -H "$PH" "$P/api/v1/accounts/relationships?id[]=$new_on_p" | j "print(d[0][\"following\"])")" = "True" ]' \
&& ok "the new account lets alice in" || ko "the new account never accepted alice"