A sender's digest of its followers here is compared and mended
FEP-8fcf, received. When a delivery's Collection-Synchronization header digests the sender's followers on PrivaPub
otherwise than the personas following it, a job reads the list the header names (on the sender's origin, signed by the
instance actor): a follow the list leaves out ends, only when the list is the one the digest describes; a request it
lists is taken as accepted; a persona it lists that follows nothing there sends Undo{Follow}, as Mastodon does. Each
claiming delivery is compared once.
Checked live (scenarios/followsync.sh, now 15 checks): PrivaPub ends a follow Mastodon lost and undoes one only
Mastodon remembered.
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
bd18de35a6
commit
e0682fa868
11 files changed
+291
-12
No files matched your search
@@ -55,6 +55,23 @@ namespace PrivaPub.Federation.Actors
|
||||
public static string HeaderValue(LocalActor actor, string digest) =>
|
||||
$"collectionId=\"{actor.Followers}\", url=\"{RollCall(actor)}\", digest=\"{digest}\"";
|
||||
|
||||
// what a Collection-Synchronization header claims, written as a Signature header's parameters; default when it
|
||||
// names no collection, list or digest
|
||||
public static SynchronizationClaim Claim(string actorUri, string header)
|
||||
{
|
||||
var values = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
foreach (var part in (header ?? "").Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var equals = part.IndexOf('=');
|
||||
if (equals > 0)
|
||||
values[part[..equals].Trim()] = part[(equals + 1)..].Trim().Trim('"');
|
||||
}
|
||||
return values.TryGetValue("collectionId", out var collection) && values.TryGetValue("url", out var url) && values.TryGetValue("digest", out var digest)
|
||||
&& digest.Length == 64 && digest.All(Uri.IsHexDigit)
|
||||
? new SynchronizationClaim(actorUri, collection, url, digest.ToLowerInvariant())
|
||||
: default;
|
||||
}
|
||||
|
||||
// whether the activity, or the object it carries, is addressed to the actor's followers
|
||||
public static bool ForFollowers(JsonObject activity, LocalActor actor) =>
|
||||
Addressed(activity, actor.Followers) || activity["object"] is JsonObject inner && Addressed(inner, actor.Followers);
|
||||
|
||||
Reference in new issue
Block a user