From eec750a69969bdda0dd853e57462f646297a7e00 Mon Sep 17 00:00:00 2001 From: giles Date: Sun, 22 Feb 2026 08:52:56 +0000 Subject: [PATCH] Fix AP object id: must be on actor's domain (Mastodon origin check) Mastodon verifies the object id domain matches the actor domain. Using the post URL (coop.rose-ash.com) as object id caused silent rejection. Now always uses {activity_id}/object on federation domain. Also adds to/cc on object for visibility determination. Co-Authored-By: Claude Opus 4.6 --- events/handlers/ap_delivery_handler.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/events/handlers/ap_delivery_handler.py b/events/handlers/ap_delivery_handler.py index 001b9ce..702b7db 100644 --- a/events/handlers/ap_delivery_handler.py +++ b/events/handlers/ap_delivery_handler.py @@ -28,15 +28,22 @@ def _build_activity_json(activity: APActivity, actor: ActorProfile, domain: str) obj = dict(activity.object_data or {}) + # Object id MUST be on the actor's domain (Mastodon origin check). + # The post URL (e.g. coop.rose-ash.com/slug/) goes in "url" only. + object_id = activity.activity_id + "/object" + if activity.activity_type == "Delete": # Delete: object is a Tombstone with just id + type + obj.setdefault("id", object_id) obj.setdefault("type", "Tombstone") else: # Create/Update: full object with attribution - obj.setdefault("id", obj.get("url") or (activity.activity_id + "/object")) + obj["id"] = object_id obj.setdefault("type", activity.object_type) obj.setdefault("attributedTo", actor_url) obj.setdefault("published", activity.published.isoformat() if activity.published else None) + obj.setdefault("to", ["https://www.w3.org/ns/activitystreams#Public"]) + obj.setdefault("cc", [f"{actor_url}/followers"]) return { "@context": [