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
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-05 20:57:43 +02:00
1 parent f3e8cce2ed
commit 7b0377c85f
19 files changed
+309 -63

No files matched your search

+3 -1
View File
@@ -2,7 +2,7 @@
Generated by `tools/pasture/town.sh backlog --write` from `village-20261005-111857` on 2026-10-05. Do not edit by hand: fix the cause, or record a known gap in `tools/pasture/town/gaps.json`, and generate it again.
Checks: 2557 pass, 4 fail, 1 known gaps, 0 known gaps now passing.
Checks: 2558 pass, 6 fail, 1 known gaps, 0 known gaps now passing.
## New failures
@@ -10,6 +10,8 @@ Checks: 2557 pass, 4 fail, 1 known gaps, 0 known gaps now passing.
- **`count.like.community|*|mastodon|control`**: 1 failing. Example: p161 expected `>=1`, got `0`.
- **`count.boost.public|*|sharkey|control`**: 1 failing. Example: p175 expected `>=1`, got `0`.
- **`edit.text|privapub|gts|out`**: 1 failing. Example: p4 expected `(edited)`, got `l market harvest rain quiet tide garden festival bridge (p4)`.
- **`client.render.image.followers|privapub|decepub|local`**: 1 failing. Example: p15 expected `None`, got `picture 'picture 1 of p15' did not load`.
- **`client.render.image.public|privapub|decepub|local`**: 1 failing. Example: p33 expected `None`, got `picture 'picture 1 of p33' did not load`.
## Known gaps still failing
+25 -18
View File
@@ -142,7 +142,7 @@ Priorities, used throughout:
| Polls (see §3 Misskey for vote shapes) | P1 | `Status.poll`, `/polls/:id`, `/polls/:id/votes`, `poll` notification |
| Custom emoji on posts, names, fields and poll options, proxied, refreshed by `updated` | P1 | `Status.emojis`, `Account.emojis` |
| Link attachments as the card source | P1 | `Status.card` |
| Inbound `Move` with Mastodon's checks (`target` re-fetched, its `alsoKnownAs` lists the old account, 7-day lock); move each persona's follow | P1 | `Account.moved` |
| Inbound `Move` with Mastodon's checks (`target` re-fetched, its `alsoKnownAs` lists the old account); move each persona's follow, lists, mutes and blocks (done 2026-10-05) | P1 | `Account.moved` |
| Re-run WebFinger when `preferredUsername` changes; key accounts on the actor id | P2 | `Account.acct` |
| Inbound `Block`: stop delivering, hide (**done** 2026-10-04) | P2 | `relationship.blocked_by` |
| `Add`/`Remove` featured (pins, tags) | P2 | `GET /accounts/:id/statuses?pinned=true` |
@@ -260,7 +260,9 @@ Such posts are then found in the member's conversations, never by a search on th
**Account moves (2026-10-05, `tools/pasture/scenarios/moves.sh`):** GoToSocial moves an account through its API (the new
one names the old in `alsoKnownAs` first, `/api/v1/accounts/alias`) and sends `Move{object: old, target: new}` to the
old one's followers. PrivaPub reads the new account again, finds the old one among its aliases and shows the old account
`moved`; 6 checks pass. PrivaPub dropped `Move` as an unknown type until then.
`moved`. Since the owner's decision of the same day it also moves alice's follow, as Mastodon does: a Follow to the new
account (locked by GoToSocial, which approves it) and an Undo to the old; 8 checks pass. PrivaPub dropped `Move` as an
unknown type until then.
### Misskey family: Misskey 2026.10.0, Sharkey 2025.4.7, Iceshrimp.NET 2026.1.2-beta, CherryPick 4.17
@@ -515,7 +517,7 @@ on a Page: pins live in `featured`, locks in `Lock`.
| Announces of activities other than Create (votes, moderation): trust the inner activity when the object's own group signed the Announce; refetching every vote does not scale. Keep the origin refetch for Create and Update. Votes and their undoing **done** 2026-10-04 (`AnnounceHandler.Relayed`: trusted from the community's own server or on its own posts, otherwise fetched from the voter's origin); moderation still open | P1 | — |
| Moderation state: removals (reason, by, at, cascade), locks, bans with `endTime`/`removeData`, featured, moderators, `Update{Group}` by a moderator | P1 | removed posts hidden plus `privapub.removed`; `privapub.locked` (replying answers 422); pins as `pinned=true` |
| Link posts: keep `Link.href`, the thumbnail `image` and alt text; build the card (Lemmy sends no title or description for the link) | P1 | `Status.card` |
| `ChatMessage` in and out (out to someone who writes to us that way, done 2026-10-05; a first message waits, G-0008) | P1 | `visibility: direct` |
| `ChatMessage` in and out (out to someone who writes to us that way, and to Lemmy 0.19 and Mbin by NodeInfo, done 2026-10-05) | P1 | `visibility: direct` |
| Outbound shape for Lemmy: both `to` and `cc`; the community in `to`; Public in the object, Create and Announce; votes and comments sent to the community inbox | P1 | — |
| Communities we host: pick `Announce(object)` per peer by NodeInfo (as PieFed does). Announcing to every follower instance, the author's included, is done and needed (pasture evidence below) | P1 | — |
| Flags from a `Service`-typed reporter actor with `to: [community]`; the reporter stays anonymous | P2 | — |
@@ -562,14 +564,14 @@ What it showed:
- 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. Since 2026-10-05 a direct message to someone who writes to us as `ChatMessage`s goes out as one; the
first message to someone who never did is still a `Note` (G-0008). Most of the threadiverse runs 0.19, so this
matters more than Mbin's same rule.
- **Pasture evidence (2026-10-05, Lemmy 0.19.20, `tools/pasture/scenarios/lemmy19.sh`):** 29 checks pass and one gap is
expected (G-0008, a first direct message): 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 and her answer
as a `ChatMessage` (another persona's too), a moderator's lock (replies then refused), unlock, ban and unban
(`blocked_by`) and removal, statistics.
`Note` 400. Its actors don't say so, so since 2026-10-05 (owner decision) a direct message to one account on a server
whose NodeInfo names Lemmy before 1.0 goes out as a `ChatMessage`, a first message too. Most of the threadiverse runs
0.19, so this matters more than Mbin's same rule.
- **Pasture evidence (2026-10-05, Lemmy 0.19.20, `tools/pasture/scenarios/lemmy19.sh`):** 30 checks pass: 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 and her answer as a `ChatMessage` (another persona's too), a first message
to an account that never wrote here, 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
@@ -603,14 +605,19 @@ What it showed:
answers the thread gone.
- **Votes:** an upvote is an `Announce`, a favourite a `Like`. Downvotes stay on Mbin (no `Dislike`), and taking an
upvote back sends nothing (its vote listener announces only the upvote), so a boost counted here stays.
- **Private messages only as `ChatMessage`:** a direct `Note` is dropped ("PM: not implemented", G-0008), and Mbin's
actors say nothing that would let a sender choose; its API never starts a conversation with a remote account.
- **Private messages only as `ChatMessage`:** a direct `Note` is dropped ("PM: not implemented"), and Mbin's actors
say nothing that would let a sender choose, so PrivaPub sends a `ChatMessage` to a server whose NodeInfo names Mbin
(owner decision 2026-10-05). Its API never starts a conversation with a remote account.
- **Mbin addresses a private message to the recipient's profile page** (`apPublicUrl`, the actor's `url`), not to its
id. PrivaPub takes a persona's profile page as its address since 2026-10-05. Mbin also passes on a message it
received as though it were sending it, signed as its remote author; it has no key for that account, so the delivery
fails on Mbin's side and nothing leaves.
- Mbin names what it makes during a request after the request's host, port included.
- **Pasture evidence (2026-10-05, Mbin 1.10.1, `tools/pasture/scenarios/mbin.sh`):** 24 checks pass and one gap is
expected (G-0008): magazines both ways; an Mbin thread in our community arrives titled and ours reaches Mbin as a
thread; a magazine's thread reaches alice's home and her Note becomes a microblog post in it; comments both ways;
Mbin's favourite counts as a like and its upvote as a boost, alice's like and boost count there; a moderator's lock,
unlock and removal; the unfollow; statistics.
- **Pasture evidence (2026-10-05, Mbin 1.10.1, `tools/pasture/scenarios/mbin.sh`):** 26 checks pass: magazines both
ways; an Mbin thread in our community arrives titled and ours reaches Mbin as a thread; a magazine's thread reaches
alice's home and her Note becomes a microblog post in it; comments both ways; Mbin's favourite counts as a like and
its upvote as a boost, alice's like and boost count there; alice's first message and mbuser's answer; a moderator's
lock, unlock and removal; the unfollow; statistics.
- **Gaps:**
- **P1:** Lemmy's P1 set, W2, and tolerating `source` as a string.
- **P2:** galleries; post `Move`; `repliesEnabled`; `nsfl`; flairs; publish `likes`/`shares` totals.
+7 -5
View File
@@ -79,9 +79,9 @@ Written 2026-10-01 from the original 2023 code, the decePubClient UI, a federati
- 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
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). A direct message to someone who writes to us as
`ChatMessage`s goes out as one (Lemmy 0.19, Mbin). Open: a first message to such an account (G-0008, waits for the
owner).
it is kept, and a bare `Lock` from the post's server is taken (Mbin). A direct message goes out as a `ChatMessage`
to someone who writes to us that way, and to Lemmy 0.19 and Mbin as NodeInfo names them (owner decision, G-0008
closed).
- [ ] P7 Threads, communities, moderation, the social graph
- [ ] 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,
@@ -274,6 +274,8 @@ and circles (see Owner decisions).
| Question | Decision |
|---|---|
| May a persona join a remote event? | **Yes.** A persona joins and leaves another server's event (`Join`/`Leave`, sent to the event's organiser only). Its participation is public on that server, as on every platform with events. Events that take participants only by invitation or on another site are refused before anything is sent. |
| A first private message to Lemmy 0.19 or Mbin (G-0008) | **Decide by the server's software.** Lemmy before 1.0 and Mbin take a private message only as a `ChatMessage` and their actors do not say so, so a direct message to one account on a server whose NodeInfo names one of them goes as a `ChatMessage`: the one exception to "a server's software is for display only". |
| An account a persona follows moves | **Move the follow, as Mastodon does.** After a verified `Move` the persona follows the new account (a Follow to its server) and unfollows the old, in the same lists; a mute or a block of the old account carries over. |
| Should PrivaPub pass on replies to a persona's posts? | **Yes, "the fediverse is broken without".** A public or unlisted reply that someone on another server writes to a persona's post goes on to the persona's followers, as Mastodon forwards it: the activity as its author signed it, never to the replier's own server, never for a post that is local-only, in a group or not shown to followers. Its edits and deletion follow the same way. |
## Libraries (researched; no maintained .NET ActivityPub library exists, so Letterbook and Iceshrimp.NET both wrote their own)
@@ -655,8 +657,8 @@ it, raw where it doesn't.
`interaction_policy`; `ReplyRequest`/`LikeRequest`/`AnnounceRequest` where approval is needed; `Accept{result}`
verified and carried; replies a policy does not let in kept only with an authorization.
- **Accounts and follows:**
- inbound `Move` with Mastodon's checks: **done 2026-10-05** (the old account shows `moved`, checked live against
GoToSocial); moving the personas' follows to the new account is left to them until the owner decides;
- inbound `Move` with Mastodon's checks: **done 2026-10-05** (the old account shows `moved`, the personas' follows,
lists, mutes and blocks move to the new one, owner decision; checked live against GoToSocial);
- re-run WebFinger on a rename;
- inbound `Block`, plus `Add`/`Remove` of pins;
- FEP-8fcf followers sync;