Deliveries to followers carry a digest of them, per server
Owner decision of 2026-10-06 (FEP-8fcf). A persona's delivery addressed to its followers carries a signed
Collection-Synchronization header naming its followers, its roll-call (…/groupies/roll-call) and the digest of its
accepted followers on the receiving server only. The roll-call answers a signed request with the persona's followers
on the signer's server and nobody else's.
Mastodon gives every Undo{Follow} it sends after reading a roll-call the same id (…#follows//undo), so a second one
looked like a copy: an Undo of a Follow that comes again while the follow it ends exists again is now kept once per
follow.
Checked live (scenarios/followsync.sh): Mastodon drops a follow PrivaPub lost, and undoes one it lost itself.
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
bf37223849
commit
bd18de35a6
10 files changed
+385
-4
No files matched your search
@@ -17,7 +17,8 @@ namespace PrivaPub.Federation.Signing
|
||||
|
||||
public static string Digest(byte[] body) => "SHA-256=" + Convert.ToBase64String(SHA256.HashData(body));
|
||||
|
||||
public static void Sign(HttpRequestMessage request, LocalActor signer, byte[] body)
|
||||
// extra: further headers the request carries, signed with the rest (FEP-8fcf's Collection-Synchronization)
|
||||
public static void Sign(HttpRequestMessage request, LocalActor signer, byte[] body, params (string Name, string Value)[] extra)
|
||||
{
|
||||
var date = DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture);
|
||||
var signed = new List<(string Name, string Value)>
|
||||
@@ -35,6 +36,12 @@ namespace PrivaPub.Federation.Signing
|
||||
signed.Add(("digest", digest));
|
||||
}
|
||||
|
||||
foreach (var (name, value) in extra)
|
||||
{
|
||||
request.Headers.TryAddWithoutValidation(name, value);
|
||||
signed.Add((name.ToLowerInvariant(), value));
|
||||
}
|
||||
|
||||
var signingString = string.Join("\n", signed.Select(h => $"{h.Name}: {h.Value}"));
|
||||
using var rsa = RSA.Create();
|
||||
rsa.ImportFromPem(signer.PrivateKeyPem);
|
||||
|
||||
Reference in new issue
Block a user