Fix per-app AP delivery, NULL uniqueness, and reverse discovery
- Delivery handler now signs/delivers using the per-app domain that matches the follower's subscription (not always federation domain) - app_domain is NOT NULL with default 'federation' (sentinel replaces NULL to avoid uniqueness constraint edge case) - Aggregate actor advertises per-app actors via alsoKnownAs - Migration backfills existing NULL rows to 'federation' Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -64,8 +64,8 @@ def create_activitypub_blueprint(app_name: str) -> Blueprint:
|
||||
domain = _ap_domain(app_name)
|
||||
fed_domain = _federation_domain()
|
||||
aggregate = _is_aggregate(app_name)
|
||||
# For per-app follows, store app_domain; for federation aggregate, NULL
|
||||
follower_app_domain: str | None = None if aggregate else app_name
|
||||
# For per-app follows, store app_domain; for federation, "federation"
|
||||
follower_app_domain: str = app_name
|
||||
# For per-app outboxes, filter by origin_app; for federation, show all
|
||||
outbox_origin_app: str | None = None if aggregate else app_name
|
||||
|
||||
@@ -201,8 +201,16 @@ def create_activitypub_blueprint(app_name: str) -> Blueprint:
|
||||
"url": actor_url,
|
||||
}
|
||||
|
||||
# Per-app actors link back to the aggregate federation actor
|
||||
if not aggregate and domain != fed_domain:
|
||||
if aggregate:
|
||||
# Aggregate actor advertises all per-app actors
|
||||
also_known = [
|
||||
f"https://{_ap_domain(a)}/users/{username}"
|
||||
for a in AP_APPS if a != "federation"
|
||||
]
|
||||
if also_known:
|
||||
actor_json["alsoKnownAs"] = also_known
|
||||
else:
|
||||
# Per-app actors link back to the aggregate federation actor
|
||||
actor_json["alsoKnownAs"] = [
|
||||
f"https://{fed_domain}/users/{username}",
|
||||
]
|
||||
|
||||
@@ -144,7 +144,7 @@ async def handle_follow(
|
||||
body: dict,
|
||||
from_actor_url: str,
|
||||
domain: str,
|
||||
app_domain: str | None = None,
|
||||
app_domain: str = "federation",
|
||||
) -> None:
|
||||
"""Process a Follow activity: add follower, send Accept, backfill."""
|
||||
remote_actor = await fetch_remote_actor(from_actor_url)
|
||||
@@ -204,8 +204,8 @@ async def handle_follow(
|
||||
await send_accept(actor_row, body, follower_inbox, domain)
|
||||
|
||||
# Backfill: deliver recent posts (filtered by origin_app for per-app follows)
|
||||
origin_app = app_domain if app_domain and app_domain != "federation" else None
|
||||
await backfill_follower(session, actor_row, follower_inbox, domain, origin_app=origin_app)
|
||||
backfill_origin = app_domain if app_domain != "federation" else None
|
||||
await backfill_follower(session, actor_row, follower_inbox, domain, origin_app=backfill_origin)
|
||||
|
||||
|
||||
async def handle_undo(
|
||||
@@ -213,7 +213,7 @@ async def handle_undo(
|
||||
actor_row: ActorProfile,
|
||||
body: dict,
|
||||
from_actor_url: str,
|
||||
app_domain: str | None = None,
|
||||
app_domain: str = "federation",
|
||||
) -> None:
|
||||
"""Process an Undo activity (typically Undo Follow)."""
|
||||
inner = body.get("object")
|
||||
@@ -485,7 +485,7 @@ async def dispatch_inbox_activity(
|
||||
body: dict,
|
||||
from_actor_url: str,
|
||||
domain: str,
|
||||
app_domain: str | None = None,
|
||||
app_domain: str = "federation",
|
||||
) -> None:
|
||||
"""Route an inbox activity to the correct handler."""
|
||||
activity_type = body.get("type", "")
|
||||
|
||||
Reference in New Issue
Block a user