Funkwhale's answers, deletions and NodeInfo are understood

Three shapes Funkwhale 2.0 sends, each of which lost something:
- its Accept is named after the Follow it answers, on our origin (`…#follows/<uuid>/accept`), and was refused as off its
  actor's origin, so no follow of a channel completed: an Accept or Reject whose id extends the id of the activity it
  answers, on our origin, is now taken;
- a channel deletes its uploads in one Delete without an id, their ids in a list as the object's id: each is deleted;
- its NodeInfo discovery names the document under its swagger schema's URL: a link whose path names NodeInfo is taken
  when no rel is known.

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 12:02:25 +02:00
1 parent 3866558d1d
commit 9f0b25e92b
5 files changed
+82 -3

No files matched your search

@@ -43,11 +43,20 @@ namespace PrivaPub.Federation.Inbox.Handlers
public string Type => "Delete";
const int MaxObjects = 50;
public async Task Handle(JsonNode activity, ForeignAvatar actor, CancellationToken token)
{
var delete = activity;
// Funkwhale deletes several uploads at once, naming them in one list as the object's id
var objectUris = activity["object"] is JsonObject { } inner && inner["id"] is JsonArray ids
? ids.Select(Id).Where(id => id != default).Distinct(StringComparer.Ordinal).Take(MaxObjects).ToList()
: new List<string> { Id(activity["object"]) };
foreach (var objectUri in objectUris)
await Handle(activity, actor, objectUri, token);
}
var objectUri = Id(delete["object"]);
async Task Handle(JsonNode activity, ForeignAvatar actor, string objectUri, CancellationToken token)
{
if (!Origin.Same(objectUri, actor.ActorURI))
{
Arrival.Drop("cross-origin");
+4 -1
View File
@@ -222,7 +222,10 @@ namespace PrivaPub.Federation.Inbox
async Task<InboxResult> ShapeProblem(string type, JsonNode activity, string actorUri, CancellationToken token)
{
var activityId = Id(activity);
if (activityId != default && !Origin.Same(activityId, actorUri))
// (Funkwhale names its answer after the activity it answers: `<our Follow's id>/accept`)
if (activityId != default && !Origin.Same(activityId, actorUri)
&& !(type is "Accept" or "Reject" && Id(activity["object"]) is { } answered && Origin.Same(answered, _localActors.BaseAddress)
&& activityId.StartsWith(answered + "/", StringComparison.Ordinal)))
return new(StatusCodes.Status400BadRequest, "the activity's id is not on its actor's origin", Reason: "id-cross-origin");
var inner = activity["object"];