Pasture: Lemmy 0.19 joins, built to trust the pasture's CA
Most of the threadiverse runs Lemmy 0.19, whose release trusts only the roots its rustls bundles. images/lemmy19 builds 0.19.20 from its tag with reqwest's rustls-tls-native-roots added, and the pasture runs it as lemmy19.test. scenarios/lemmy19.sh passes 27 checks with no change to PrivaPub (communities, threads, comments, votes, its private message, moderation) and one known gap: 0.19 takes private messages only as ChatMessage and answers our direct Note 400, like Mbin, so G-0008 now covers both and waits for the owner. 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
bd5901d15e
commit
a85ebd30c0
9 files changed
+197
-9
No files matched your search
@@ -547,6 +547,11 @@ tools/pasture/run.sh down # removes e
|
|||||||
API follows one only when named by its handle; a chat room is made, then written in. `scenarios/nodebb.sh`, 23
|
API follows one only when named by its handle; a chat room is made, then written in. `scenarios/nodebb.sh`, 23
|
||||||
checks: a category followed and its topic as a titled thread, replies both ways, nbuser following alice, votes both
|
checks: a category followed and its topic as a titled thread, replies both ways, nbuser following alice, votes both
|
||||||
ways, an edit and a deletion, a chat both ways, the unfollow, statistics.
|
ways, an edit and a deletion, a chat both ways, the unfollow, statistics.
|
||||||
|
- **Lemmy 0.19 (0.19.20), as `lemmy19.test`:** what most of the threadiverse runs. Its release trusts only the roots its
|
||||||
|
rustls bundles, so `images/lemmy19` builds the tag with reqwest's `rustls-tls-native-roots` added (about ten minutes
|
||||||
|
the first time) and the pasture's bundle is mounted as the system's. Its config names the database as `uri` (1.0:
|
||||||
|
`connection`) and its API is v3 (`/api/v3`, `sort=New`, `resolve_object` answering views). It takes private messages
|
||||||
|
only as `ChatMessage` (G-0008). `scenarios/lemmy19.sh`, 27 checks and that gap.
|
||||||
- **Hollo (0.9.19):** Fedify's microblog server on the shared Postgres, set up through its web form (which
|
- **Hollo (0.9.19):** Fedify's microblog server on the shared Postgres, set up through its web form (which
|
||||||
checks `Origin` against `Host`, so the request names `hollo.test` without the port). It needs a 44-character
|
checks `Origin` against `Host`, so the request names `hollo.test` without the port). It needs a 44-character
|
||||||
`SECRET_KEY`, a media directory and a `themeColor`; statuses and votes go as JSON. Town only, no scenario.
|
`SECRET_KEY`, a media directory and a `themeColor`; statuses and votes go as JSON. Town only, no scenario.
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ and every run starting clean, with signed fetches required (as privapub.thepra.d
|
|||||||
- **Misskey 2026.10**
|
- **Misskey 2026.10**
|
||||||
- **Sharkey 2025.4.7**
|
- **Sharkey 2025.4.7**
|
||||||
- **Akkoma 3.20.1**
|
- **Akkoma 3.20.1**
|
||||||
- **Lemmy 1.0.0-beta.2**
|
- **Lemmy 1.0.0-beta.2**, and **Lemmy 0.19.20** (built with the platform's roots, so it trusts the pasture's CA)
|
||||||
- **PeerTube 8.3.1**
|
- **PeerTube 8.3.1**
|
||||||
- **Pixelfed 0.14.4**
|
- **Pixelfed 0.14.4**
|
||||||
- **WordPress 6 with ActivityPub 9.3.1**
|
- **WordPress 6 with ActivityPub 9.3.1**
|
||||||
|
|||||||
+9
-1
@@ -554,7 +554,15 @@ What it showed:
|
|||||||
minute after the server is first seen, at the newest activity and skips everything older. On a clean pasture its
|
minute after the server is first seen, at the newest activity and skips everything older. On a clean pasture its
|
||||||
first Follow of our community was lost, so the scenario waits for the worker (`federation_queue_state`). On the
|
first Follow of our community was lost, so the scenario waits for the worker (`federation_queue_state`). On the
|
||||||
public network the same applies to the first thing a Lemmy sends to a PrivaPub it has just discovered.
|
public network the same applies to the first thing a Lemmy sends to a PrivaPub it has just discovered.
|
||||||
- 0.19 cannot join the pasture: its rustls trusts only its bundled roots, never Caddy's CA.
|
- 0.19's release trusts only the roots its rustls bundles, never Caddy's CA, so the pasture builds 0.19.20 from its tag
|
||||||
|
with reqwest's `rustls-tls-native-roots` added (`tools/pasture/images/lemmy19`) and runs it as `lemmy19.test`.
|
||||||
|
- **0.19 takes private messages only as `ChatMessage`** (`ChatMessageType` has no `Note`) and answers our direct
|
||||||
|
`Note` 400. Most of the threadiverse runs 0.19, so this matters more than Mbin's same rule (G-0008).
|
||||||
|
- **Pasture evidence (2026-10-05, Lemmy 0.19.20, `tools/pasture/scenarios/lemmy19.sh`):** 27 checks pass and one gap is
|
||||||
|
expected (G-0008, direct messages to it), with no change to PrivaPub: communities both ways, threads with titles,
|
||||||
|
comments both ways, votes up and down both ways (relayed in the community's announces, as 1.0 does), its private
|
||||||
|
message to alice, a moderator's lock (replies then refused), unlock, ban and unban (`blocked_by`) and removal,
|
||||||
|
statistics.
|
||||||
|
|
||||||
### PieFed 1.7.17 and Mbin 1.10.1
|
### PieFed 1.7.17 and Mbin 1.10.1
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -69,11 +69,11 @@ Written 2026-10-01 from the original 2023 code, the decePubClient UI, a federati
|
|||||||
Funkwhale serve it; Funkwhale's answers named after our follow and its deletions of several uploads.
|
Funkwhale serve it; Funkwhale's answers named after our follow and its deletions of several uploads.
|
||||||
- GoToSocial's interaction policies both ways; personas join and leave remote events; third-party replies to a
|
- GoToSocial's interaction policies both ways; personas join and leave remote events; third-party replies to a
|
||||||
persona's posts passed on to its followers (owner decisions 2026-10-05).
|
persona's posts passed on to its followers (owner decisions 2026-10-05).
|
||||||
- wave 2, under way: PieFed, Mbin and NodeBB in the pasture with scenarios (2026-10-05). What they showed and was fixed: the
|
- wave 2, under way: PieFed, Mbin, NodeBB and Lemmy 0.19 in the pasture with scenarios (2026-10-05). What they showed and was fixed: the
|
||||||
instance actor answers at the server's root, where PieFed looks for the inbox it announces to; a community's removal
|
instance actor answers at the server's root, where PieFed looks for the inbox it announces to; a community's removal
|
||||||
of a post on its own server is believed at once; a followed group's post its own server sends without announcing
|
of a post on its own server is believed at once; a followed group's post its own server sends without announcing
|
||||||
it is kept, and a bare `Lock` from the post's server is taken (Mbin). Open: direct messages to Mbin, which takes
|
it is kept, and a bare `Lock` from the post's server is taken (Mbin). Open: direct messages to Lemmy 0.19 and Mbin,
|
||||||
them only as `ChatMessage` (G-0008, waits for the owner).
|
which take them only as `ChatMessage` (G-0008, waits for the owner).
|
||||||
- [ ] P7 Threads, communities, moderation, the social graph
|
- [ ] P7 Threads, communities, moderation, the social graph
|
||||||
- [ ] P8 Signatures, discovery, the long tail
|
- [ ] P8 Signatures, discovery, the long tail
|
||||||
- [x] P9 Reading at volume (owner decision 2026-10-04, back from "Cut"): lists, server-side filters, scheduled posts,
|
- [x] P9 Reading at volume (owner decision 2026-10-04, back from "Cut"): lists, server-side filters, scheduled posts,
|
||||||
|
|||||||
@@ -107,3 +107,8 @@ nodebb.test {
|
|||||||
tls internal
|
tls internal
|
||||||
reverse_proxy pasture-nodebb:4567
|
reverse_proxy pasture-nodebb:4567
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lemmy19.test {
|
||||||
|
tls internal
|
||||||
|
reverse_proxy pasture-lemmy19:8536
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Lemmy 0.19.20, built from its tag with one change: reqwest's rustls also trusts the platform's roots
|
||||||
|
# (rustls-tls-native-roots), where the pasture's CA is, besides the roots it bundles. The release image trusts only
|
||||||
|
# those, so 0.19 could not reach any pasture peer.
|
||||||
|
FROM docker.io/library/rust:1.81-bookworm AS build
|
||||||
|
RUN git clone --depth 1 --branch 0.19.20 --recurse-submodules https://github.com/LemmyNet/lemmy.git /lemmy
|
||||||
|
WORKDIR /lemmy
|
||||||
|
RUN sed -i 's/^ "rustls-tls",$/ "rustls-tls",\n "rustls-tls-native-roots",/' Cargo.toml && grep -q '"rustls-tls-native-roots"' Cargo.toml
|
||||||
|
RUN cargo build --release && strip target/release/lemmy_server
|
||||||
|
|
||||||
|
FROM docker.io/library/debian:bookworm-slim
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends libpq5 ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||||
|
COPY --from=build /lemmy/target/release/lemmy_server /usr/local/bin/lemmy_server
|
||||||
|
ENTRYPOINT ["/usr/local/bin/lemmy_server"]
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# Lemmy 0.19.20, the version most of the threadiverse runs, as lemmy19.test: the backend alone on the shared Postgres,
|
||||||
|
# built by images/lemmy19 from its tag with reqwest also trusting the platform's roots (the release trusts only the ones
|
||||||
|
# it bundles), and given the pasture's bundle as the platform's. Its API is v3.
|
||||||
|
LEMMY19_IMAGE=${LEMMY19_IMAGE:-localhost/pasture-lemmy19:0.19.20}
|
||||||
|
. "$here/peers/shared.sh"
|
||||||
|
|
||||||
|
lemmy19_up() {
|
||||||
|
podman image exists "$LEMMY19_IMAGE" || podman build -q -t "$LEMMY19_IMAGE" "$here/images/lemmy19" >/dev/null
|
||||||
|
shared_postgres_up
|
||||||
|
pg_db lemmy19
|
||||||
|
mkdir -p "$here/.state/lemmy19"
|
||||||
|
cat > "$here/.state/lemmy19/config.hjson" <<HJSON
|
||||||
|
{
|
||||||
|
database: { uri: "postgresql://pasture:pasture@postgres:5432/lemmy19" }
|
||||||
|
hostname: "lemmy19.test"
|
||||||
|
bind: "0.0.0.0"
|
||||||
|
port: 8536
|
||||||
|
tls_enabled: true
|
||||||
|
setup: {
|
||||||
|
admin_username: "lemmyuser"
|
||||||
|
admin_password: "Lemmy-Pasture-Pass-1"
|
||||||
|
site_name: "Pasture Lemmy 0.19"
|
||||||
|
admin_email: "lemmyuser@lemmy19.test"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HJSON
|
||||||
|
podman run -d --replace --name pasture-lemmy19 --network $net --label pasture=1 -e LEMMY_CONFIG_LOCATION=/config/config.hjson \
|
||||||
|
-e SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt -e RUST_LOG="${LEMMY_LOG:-warn}" -v "$here/.state/lemmy19:/config:z,ro" \
|
||||||
|
-v "$ca/bundle.pem:/etc/ssl/certs/ca-certificates.crt:z,ro" "$LEMMY19_IMAGE" >/dev/null
|
||||||
|
for _ in $(seq 1 90); do
|
||||||
|
site lemmy19.test -s -o /dev/null -w '%{http_code}' https://lemmy19.test:6443/api/v3/site 2>/dev/null | grep -q 200 && break
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
# Lemmy's default limits would throttle a scripted run; it reads them again when its site is edited
|
||||||
|
podman exec pasture-postgres psql -U pasture -d lemmy19 -qc "update local_site_rate_limit set message=100000, message_per_second=1,
|
||||||
|
post=100000, post_per_second=1, register=100000, register_per_second=1, image=100000, image_per_second=1,
|
||||||
|
comment=100000, comment_per_second=1, search=100000, search_per_second=1" >/dev/null
|
||||||
|
site lemmy19.test -s -X POST https://lemmy19.test:6443/api/v3/user/login -H 'Content-Type: application/json' \
|
||||||
|
-d '{"username_or_email":"lemmyuser","password":"Lemmy-Pasture-Pass-1"}' \
|
||||||
|
| python3 -c "import sys,json; print(json.load(sys.stdin)['jwt'])" > "$here/.state/lemmy19.token" 2>/dev/null || true
|
||||||
|
site lemmy19.test -s -o /dev/null -X PUT https://lemmy19.test:6443/api/v3/site -H "Authorization: Bearer $(cat "$here/.state/lemmy19.token")" \
|
||||||
|
-H 'Content-Type: application/json' -d '{"registration_mode":"Open","require_email_verification":false,"captcha_enabled":false}'
|
||||||
|
echo "lemmy19: https://lemmy19.test:6443"
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
# Lemmy 0.19.20, what most of the threadiverse runs: communities both ways (FEP-1b12), threads with titles, comments both
|
||||||
|
# ways, votes up and down both ways, private messages both ways, a moderator's lock, ban and removal, statistics. Driven
|
||||||
|
# through its v3 API; scenarios/lemmy.sh does the same against Lemmy 1.0.
|
||||||
|
LM=https://lemmy19.test:6443
|
||||||
|
LT=$(cat "$here/.state/lemmy19.token" 2>/dev/null)
|
||||||
|
# lm <method> <path> [json]: a Lemmy v3 API call as lemmyuser
|
||||||
|
lm() {
|
||||||
|
local method=$1 path=$2 body=${3:-}
|
||||||
|
if [ -n "$body" ]; then
|
||||||
|
site lemmy19.test -s -X "$method" "$LM/api/v3/$path" -H "Authorization: Bearer $LT" -H 'Content-Type: application/json' -d "$body"
|
||||||
|
else
|
||||||
|
site lemmy19.test -s -X "$method" "$LM/api/v3/$path" -H "Authorization: Bearer $LT"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
lm_posts() { lm GET "post/list?community_id=$1&sort=New&limit=50" | j "print(json.dumps([p['post'] for p in d.get('posts', [])]))"; }
|
||||||
|
lm_post_by_ap() { lm_posts "$1" | j "print(json.dumps(next((p for p in d if p['ap_id']=='$2'), None)))"; }
|
||||||
|
lm_worker() { podman exec pasture-postgres psql -U pasture -d lemmy19 -tAc "select count(*) from federation_queue_state q join instance i on i.id=q.instance_id where i.domain='privapub.test'"; }
|
||||||
|
|
||||||
|
echo "lemmy19"
|
||||||
|
[ -n "$LT" ] && ok "Lemmy 0.19 token for lemmyuser" || { ko "Lemmy 0.19 token"; return 1; }
|
||||||
|
LAT=$(privapub_token alice_lemmy19)
|
||||||
|
LAH="Authorization: Bearer $LAT"
|
||||||
|
[ -n "$LAT" ] && ok "PrivaPub token for alice_lemmy19" || { ko "PrivaPub token for alice_lemmy19"; return 1; }
|
||||||
|
jwt=$(privapub_root)
|
||||||
|
alice_id=$(curl -s -H "$LAH" "$P/api/v1/accounts/verify_credentials" | j "print(d['id'])")
|
||||||
|
run=$(date +%s)
|
||||||
|
dogs="ldogs$run"
|
||||||
|
cats="lcats$run"
|
||||||
|
|
||||||
|
echo " communities"
|
||||||
|
dogs_gid=$(curl -s -X POST $P/clientapi/group/insert -H 'Content-Type: application/json' -H "Authorization: Bearer $jwt" \
|
||||||
|
-d "{\"avatarId\":\"$alice_id\",\"userName\":\"$dogs\",\"name\":\"Pasture dogs\",\"description\":\"a PrivaPub community\",\"isCommunity\":true}" | j "print(d['id'])")
|
||||||
|
dogs_on_lm=$(lm GET "resolve_object?q=!$dogs@privapub.test" | j "print(d['community']['community']['id'])")
|
||||||
|
[ -n "$dogs_on_lm" ] && ok "Lemmy 0.19 resolves a PrivaPub community" || ko "Lemmy 0.19 cannot resolve the PrivaPub community"
|
||||||
|
# (a newly seen server gets its send worker within a minute; what was queued before it starts is never sent)
|
||||||
|
until_true 45 '[ "$(lm_worker)" = "1" ]' || true
|
||||||
|
lm POST community/follow "{\"community_id\":$dogs_on_lm,\"follow\":true}" >/dev/null
|
||||||
|
until_true 45 '[ "$(lm GET "community?id=$dogs_on_lm" | j "print(d[\"community_view\"][\"subscribed\"])")" = "Subscribed" ]' \
|
||||||
|
&& ok "lemmyuser follows the PrivaPub community (Accept arrived)" || ko "Lemmy 0.19's community follow not accepted"
|
||||||
|
cats_on_lm=$(lm POST community "{\"name\":\"$cats\",\"title\":\"Pasture cats\"}" | j "print(d['community_view']['community']['id'])")
|
||||||
|
cats_on_p=$(curl -s -H "$LAH" "$P/api/v2/search?q=@$cats@lemmy19.test&resolve=true&type=accounts" | j "print(d['accounts'][0]['id'])")
|
||||||
|
[ -n "$cats_on_p" ] && ok "PrivaPub resolves a Lemmy 0.19 community" || ko "PrivaPub cannot resolve the Lemmy 0.19 community"
|
||||||
|
curl -s -o /dev/null -X POST -H "$LAH" "$P/api/v1/accounts/$cats_on_p/follow"
|
||||||
|
until_true 45 '[ "$(curl -s -H "$LAH" "$P/api/v1/accounts/relationships?id[]=$cats_on_p" | j "print(d[0][\"following\"])")" = "True" ]' \
|
||||||
|
&& ok "alice follows the Lemmy 0.19 community (Accept arrived)" || ko "PrivaPub's community follow not accepted"
|
||||||
|
|
||||||
|
echo " threads"
|
||||||
|
lm_thread=$(lm POST post "{\"name\":\"A Lemmy 0.19 thread\",\"body\":\"posted from Lemmy 0.19 into PrivaPub\",\"community_id\":$dogs_on_lm}" | j "print(d['post_view']['post']['ap_id'])")
|
||||||
|
dogs_on_p=$(curl -s -H "$LAH" "$P/api/v1/accounts/lookup?acct=$dogs" | j "print(d['id'])")
|
||||||
|
until_true 45 '[ "$(curl -s -H "$LAH" "$P/api/v1/accounts/$dogs_on_p/statuses" | j "print(any(s[\"uri\"]==\"$lm_thread\" and (s.get(\"privapub\") or {}).get(\"title\")==\"A Lemmy 0.19 thread\" for s in d))")" = "True" ]' \
|
||||||
|
&& ok "a Lemmy 0.19 thread in the PrivaPub community arrives with its title" || ko "Lemmy 0.19's thread missing or untitled on PrivaPub"
|
||||||
|
curl -s -o /dev/null -X POST $P/clientapi/post/insert -H 'Content-Type: application/json' -H "Authorization: Bearer $jwt" \
|
||||||
|
-d "{\"avatarId\":\"$alice_id\",\"title\":\"A PrivaPub community thread $run\",\"text\":\"posted into our own community for Lemmy 0.19\",\"groupId\":\"$dogs_gid\"}"
|
||||||
|
dogs_post=$(podman exec pasture-mongo mongosh --quiet PrivaPub --eval 'print(db.Post.findOne({Text:/posted into our own community for Lemmy 0.19/}, {}, {sort:{_id:-1}}).ObjectURI)')
|
||||||
|
until_true 45 '[ "$(lm_post_by_ap "$dogs_on_lm" "$dogs_post" | j "print(d and d[\"name\"])")" = "A PrivaPub community thread $run" ]' \
|
||||||
|
&& ok "a titled post in the PrivaPub community reaches Lemmy 0.19 through its announce" || ko "PrivaPub community post missing on Lemmy 0.19"
|
||||||
|
lm_cats_thread=$(lm POST post "{\"name\":\"Cats only $run\",\"body\":\"a Lemmy 0.19 community post\",\"community_id\":$cats_on_lm}" | j "print(d['post_view']['post']['ap_id'])")
|
||||||
|
p_home_has() { curl -s -H "$LAH" "$P/api/v1/timelines/home?limit=40" | j "print(next(((s.get('reblog') or s)['id'] for s in d if (s.get('reblog') or s)['uri']=='$1'), ''))"; }
|
||||||
|
until_true 45 '[ -n "$(p_home_has "$lm_cats_thread")" ]' && ok "the Lemmy 0.19 community's announce brings its thread to alice's home" || ko "Lemmy 0.19 community thread missing from alice's home"
|
||||||
|
lm_cats_on_p=$(p_home_has "$lm_cats_thread")
|
||||||
|
p_thread=$(curl -s -X POST -H "$LAH" $P/api/v1/statuses --data-urlencode "status=A PrivaPub thread
|
||||||
|
@$cats@lemmy19.test posted from PrivaPub" -d 'visibility=public')
|
||||||
|
p_thread_id=$(echo "$p_thread" | j "print(d['id'])"); p_thread_uri=$(echo "$p_thread" | j "print(d['uri'])")
|
||||||
|
until_true 45 '[ "$(lm_post_by_ap "$cats_on_lm" "$p_thread_uri")" != "null" ]' && ok "alice's thread lands in the Lemmy 0.19 community" || ko "PrivaPub thread missing from the Lemmy 0.19 community"
|
||||||
|
p_thread_on_lm=$(lm_post_by_ap "$cats_on_lm" "$p_thread_uri" | j "print(d['id'])")
|
||||||
|
|
||||||
|
echo " comments"
|
||||||
|
lm POST comment "{\"post_id\":$p_thread_on_lm,\"content\":\"a Lemmy 0.19 comment on PrivaPub\"}" >/dev/null
|
||||||
|
until_true 45 '[ "$(curl -s -H "$LAH" "$P/api/v1/statuses/$p_thread_id/context" | j "print(any(\"a Lemmy 0.19 comment on PrivaPub\" in s[\"content\"] for s in d[\"descendants\"]))")" = "True" ]' \
|
||||||
|
&& ok "Lemmy 0.19's comment threads under alice's post" || ko "Lemmy 0.19's comment missing on PrivaPub"
|
||||||
|
curl -s -o /dev/null -X POST -H "$LAH" $P/api/v1/statuses -d "status=a PrivaPub comment on Lemmy 0.19&in_reply_to_id=$lm_cats_on_p&visibility=public"
|
||||||
|
lm_cats_id=$(lm GET "resolve_object?q=$lm_cats_thread" | j "print(d['post']['post']['id'])")
|
||||||
|
until_true 45 '[ "$(lm GET "comment/list?post_id=$lm_cats_id" | j "print(any(\"a PrivaPub comment on Lemmy 0.19\" in c[\"comment\"][\"content\"] for c in d.get(\"comments\", [])))")" = "True" ]' \
|
||||||
|
&& ok "alice's reply becomes a comment on Lemmy 0.19" || ko "PrivaPub reply missing as a Lemmy 0.19 comment"
|
||||||
|
|
||||||
|
echo " votes"
|
||||||
|
lm POST post/like "{\"post_id\":$p_thread_on_lm,\"score\":1}" >/dev/null
|
||||||
|
until_true 60 '[ "$(curl -s -H "$LAH" "$P/api/v1/statuses/$p_thread_id" | j "print(d[\"favourites_count\"])")" = "1" ]' \
|
||||||
|
&& ok "Lemmy 0.19's upvote counts as a like on PrivaPub" || ko "Lemmy 0.19's relayed upvote not counted"
|
||||||
|
lm POST post/like "{\"post_id\":$p_thread_on_lm,\"score\":-1}" >/dev/null
|
||||||
|
until_true 60 '[ "$(curl -s -H "$LAH" "$P/api/v1/statuses/$p_thread_id" | j "print(d[\"favourites_count\"], ((d.get(\"privapub\") or {}).get(\"votes\") or {}).get(\"down\"))")" = "0 1" ]' \
|
||||||
|
&& ok "Lemmy 0.19's change to a downvote reaches PrivaPub" || ko "Lemmy 0.19's change to a downvote not applied"
|
||||||
|
curl -s -o /dev/null -X POST -H "$LAH" "$P/api/v1/statuses/$lm_cats_on_p/favourite"
|
||||||
|
until_true 45 '[ "$(lm GET "post?id=$lm_cats_id" | j "print(d[\"post_view\"][\"counts\"][\"upvotes\"])")" = "2" ]' && ok "alice's like is an upvote on Lemmy 0.19" || ko "like not counted as an upvote on Lemmy 0.19"
|
||||||
|
|
||||||
|
echo " private messages"
|
||||||
|
alice_on_lm=$(lm GET "resolve_object?q=@alice_lemmy19@privapub.test" | j "print(d['person']['person']['id'])")
|
||||||
|
lm POST private_message "{\"content\":\"a secret from Lemmy 0.19\",\"recipient_id\":$alice_on_lm}" >/dev/null
|
||||||
|
until_true 45 'curl -s -H "$LAH" "$P/api/v1/conversations" | grep -q "a secret from Lemmy 0.19"' && ok "Lemmy 0.19's private message arrives as a DM" || ko "Lemmy 0.19's private message missing on PrivaPub"
|
||||||
|
# (0.19 takes a private message only as a ChatMessage and answers alice's direct Note 400, G-0008; 1.0 takes a Note)
|
||||||
|
curl -s -o /dev/null -X POST -H "$LAH" $P/api/v1/statuses -d 'status=@lemmyuser@lemmy19.test a secret from PrivaPub&visibility=direct'
|
||||||
|
until_true 30 'lm GET "private_message/list?limit=50" | grep -q "a secret from PrivaPub"' && ok "alice's DM arrives as a Lemmy 0.19 private message" \
|
||||||
|
|| xf "alice's DM never reaches Lemmy 0.19, which takes private messages only as ChatMessage (G-0008)"
|
||||||
|
|
||||||
|
echo " moderation"
|
||||||
|
p_locked() { curl -s -H "$LAH" "$P/api/v1/statuses/$lm_cats_on_p" | j "print((d.get('privapub') or {}).get('locked'))"; }
|
||||||
|
p_reply() { curl -s -o /dev/null -w '%{http_code}' -X POST -H "$LAH" $P/api/v1/statuses -d "status=$1&in_reply_to_id=$lm_cats_on_p&visibility=public"; }
|
||||||
|
p_banned() { curl -s -H "$LAH" "$P/api/v1/accounts/relationships?id[]=$cats_on_p" | j "print(d[0]['blocked_by'])"; }
|
||||||
|
lm POST post/lock "{\"post_id\":$lm_cats_id,\"locked\":true}" >/dev/null
|
||||||
|
until_true 45 '[ "$(p_locked)" = "True" ]' && ok "a moderator's lock reaches PrivaPub" || ko "a moderator's lock did not reach PrivaPub"
|
||||||
|
[ "$(p_reply "too late")" = "422" ] && ok "a reply to the locked thread is refused" || ko "a reply to the locked thread was taken"
|
||||||
|
lm POST post/lock "{\"post_id\":$lm_cats_id,\"locked\":false}" >/dev/null
|
||||||
|
until_true 45 '[ "$(p_locked)" = "False" ]' && ok "a moderator's unlock reaches PrivaPub" || ko "a moderator's unlock did not reach PrivaPub"
|
||||||
|
lm POST community/ban_user "{\"community_id\":$cats_on_lm,\"person_id\":$alice_on_lm,\"ban\":true,\"reason\":\"pasture\"}" >/dev/null
|
||||||
|
until_true 45 '[ "$(p_banned)" = "True" ]' && ok "the community's ban of alice_lemmy19 reaches PrivaPub (blocked_by)" || ko "the community's ban did not reach PrivaPub"
|
||||||
|
[ "$(p_reply "while banned")" = "422" ] && ok "a banned persona's reply in the community is refused" || ko "a banned persona's reply was taken"
|
||||||
|
lm POST community/ban_user "{\"community_id\":$cats_on_lm,\"person_id\":$alice_on_lm,\"ban\":false,\"reason\":\"pasture\"}" >/dev/null
|
||||||
|
until_true 45 '[ "$(p_banned)" = "False" ]' && ok "the community's unban reaches PrivaPub" || ko "the community's unban did not reach PrivaPub"
|
||||||
|
lm POST post/remove "{\"post_id\":$lm_cats_id,\"removed\":true,\"reason\":\"pasture\"}" >/dev/null
|
||||||
|
until_true 45 '[ -z "$(p_home_has "$lm_cats_thread")" ]' && ok "a moderator's removal reaches PrivaPub" || ko "a moderator's removal did not reach PrivaPub"
|
||||||
|
|
||||||
|
echo " statistics"
|
||||||
|
stats_check lemmy19.test lemmy
|
||||||
@@ -110,16 +110,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "G-0008",
|
"id": "G-0008",
|
||||||
"title": "A direct message to an Mbin account never arrives: Mbin takes a private message only as a ChatMessage",
|
"title": "A direct message to a Lemmy 0.19 or Mbin account never arrives: both take a private message only as a ChatMessage",
|
||||||
"match": {
|
"match": {
|
||||||
"feature": "deliver\\.direct",
|
"feature": "deliver\\.direct",
|
||||||
"observer": "mbin"
|
"observer": "(mbin|lemmy19)"
|
||||||
},
|
},
|
||||||
"kind": "server",
|
"kind": "server",
|
||||||
"phase": "P3",
|
"phase": "P3",
|
||||||
"code": "mbin 1.10.1 src/Service/ActivityPub/ActivityPubContent.php getVisibility(): a Note neither public nor to the author's followers throws 'PM: not implemented'; only CreateHandler's ChatMessage branch makes a private message",
|
"code": "lemmy 0.19.20 crates/apub/src/protocol/objects/chat_message.rs: ChatMessageType has ChatMessage alone, so a Create{Note} to a person answers 400 (Lemmy 1.0 takes a Note); mbin 1.10.1 src/Service/ActivityPub/ActivityPubContent.php getVisibility(): a Note neither public nor to the author's followers throws 'PM: not implemented'",
|
||||||
"opened": "2026-10-05",
|
"opened": "2026-10-05",
|
||||||
"status": "open",
|
"status": "open",
|
||||||
"note": "Mbin's actors advertise nothing that says so, and PrivaPub never decides by a server's software name, so choosing ChatMessage for a recipient waits for the owner. Mbin's API also never starts a conversation with an account elsewhere, so nothing comes the other way through it."
|
"note": "Neither server's actors say so, and PrivaPub never decides by a server's software name, so choosing ChatMessage for a recipient waits for the owner. Pleroma and Akkoma file a ChatMessage as a chat apart from direct messages, so sending both would show them twice. Mbin's API also never starts a conversation with an account elsewhere."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
Reference in new issue
Block a user