PieFed joins the pasture; the instance actor answers at the root

PieFed 1.7.17 (dockurr's image of the release) runs in the pasture with its
Celery worker on the shared Postgres and Redis, and scenarios/piefed.sh
checks it both ways: 29 checks, communities, titled threads, comments, votes
up and down, a community poll and a vote in it, private messages, a
moderator's lock, unlock and removal, the unfollow and statistics.

What it showed:
- PieFed sends a community's announces to the inbox of the Application at a
  peer's root (as Lemmy serves its site actor) and to /inbox otherwise.
  PrivaPub answered 404 at its root, so every announce went to an /inbox it
  does not have. The instance actor now answers at / for ActivityPub
  requests, unsigned under SecureMode as at its own address.
- PieFed keeps serving a thread its moderator removed, so the removal could
  never be checked against the post's origin. A community on the post's own
  server now speaks for it; one elsewhere still waits for the origin to say
  the post is gone.

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 15:44:13 +02:00
1 parent 0646de22bd
commit 47d6e22988
12 files changed
+304 -17

No files matched your search

@@ -74,6 +74,17 @@ namespace PrivaPub.Federation.Controllers
return Activity(ActivityPubRenderer.Actor(local));
}
// The instance actor at the server's root, for whoever asks for ActivityPub there: PieFed reads the inbox it sends a
// community's announces to from the Application at a peer's root, as Lemmy serves its own, and assumes /inbox otherwise
[HttpGet, Route("/")]
public async Task<IActionResult> Root(CancellationToken token)
{
var accept = Request.Headers.Accept.ToString();
if (!accept.Contains("activity+json", StringComparison.OrdinalIgnoreCase) && !accept.Contains("ld+json", StringComparison.OrdinalIgnoreCase))
return NotFound();
return Activity(ActivityPubRenderer.Actor(await _localActors.GetInstanceActor(token)));
}
[HttpGet, Route("{actor}/anus")]
public async Task<IActionResult> Outbox(string actor, [FromQuery] bool page, [FromQuery(Name = "max_id")] string maxId,
CancellationToken token)
@@ -448,7 +459,7 @@ namespace PrivaPub.Federation.Controllers
Response.Headers.Vary = "Accept";
// SecureMode asks every reader of ActivityPub documents for a signature, except for the instance actor, whose key
// peers need first, and except for browsers, which only get redirected to the public pages
if (_federation.CurrentValue.SecureMode && HttpMethods.IsGet(Request.Method) && !WantsHtml()
if (_federation.CurrentValue.SecureMode && HttpMethods.IsGet(Request.Method) && !WantsHtml() && Request.Path.Value != "/"
&& !string.Equals(context.RouteData.Values["actor"] as string, LocalActorService.InstanceUserName, StringComparison.OrdinalIgnoreCase)
&& await _fetches.Requester(Request, HttpContext.RequestAborted) == default)
{