diff --git a/Inbound-Activity-Handling.md b/Inbound-Activity-Handling.md new file mode 100644 index 0000000..4e2e53a --- /dev/null +++ b/Inbound-Activity-Handling.md @@ -0,0 +1,73 @@ +# Inbound Activity Handling + +k-ap handles the following inbound activities out of the box. All arrive via `POST /inbox` or `POST /users/{id}/inbox`, after HTTP signature verification. + +--- + +## Handled activities + +| Activity | What k-ap does | +|----------|----------------| +| `Follow` | Saves follower with `Pending` status; checks blocklist first | +| `Accept` | Updates outbound following status to `Accepted` | +| `Reject` | Updates outbound following status to `Rejected` | +| `Undo(Follow)` | Removes the follower record; calls `on_actor_removed` | +| `Undo(Like)` | Calls `on_unlike` | +| `Undo(Announce)` | Removes announce record from `ActorRepository`; calls `on_announce_removed` | +| `Undo(Add)` | No-op | +| `Undo(Block)` | No-op | +| `Create` | Calls `on_create` with the wrapped object JSON; dispatches mentions | +| `Update` | Calls `on_update` with the wrapped object JSON; dispatches mentions | +| `Delete` | Calls `on_delete` with the object URL; if object is an actor, calls `on_actor_removed` | +| `Announce` | Records in `ActorRepository`; calls `on_announce_received` (local object) or `on_announce_of_remote` (remote object) | +| `Like` | Calls `on_like` | +| `Add` | Calls `on_create` with the object JSON | +| `Block` | No-op (you may surface this in your UI separately) | +| `Move` | Verifies `alsoKnownAs`, migrates follower records, re-follows in background | + +--- + +## Deduplication + +Before any handler is called, k-ap calls `ActivityRepository::is_activity_processed` with the activity's `id`. If already processed, the handler is skipped and the inbox returns 200. After successful processing, `mark_activity_processed` is called. + +This makes inbound delivery safe to retry — the sender can retry on network failure without causing duplicate effects. + +--- + +## Mention dispatch + +For `Create` and `Update` activities, k-ap extracts the `tag` array from the wrapped object and looks for `{"type":"Mention","href":""}` entries. For each mention that resolves to a local user, `ApObjectHandler::on_mention` is called with the object's AP ID, the local user's UUID, and the mentioning actor's URL. + +`on_mention` failures are logged and swallowed — a broken notification must not cause the activity to be rejected. + +--- + +## Move (account migration) + +When a `Move` activity is received: + +1. k-ap fetches the target actor's JSON +2. Verifies that the target's `alsoKnownAs` array contains the source actor's URL (all aliases checked) +3. Calls `FollowRepository::migrate_follower_actor` to update follower records +4. Spawns a background task (non-blocking) to re-follow the new actor on behalf of local users who followed the old one + +Step 4 runs in the background so the inbox handler returns immediately. + +--- + +## Error responses + +4xx responses sent to remote servers use generic messages. Internal error details are only logged via `tracing`, never sent to clients. This prevents information leakage to potentially adversarial servers. + +--- + +## Body limit + +Both inbox routes enforce a **1 MB body limit** via axum's `DefaultBodyLimit`. Oversized payloads are rejected with 413 before any processing. + +--- + +## Actor types accepted + +k-ap accepts activities from `Person`, `Service`, `Application`, `Organization`, and `Group` actors. \ No newline at end of file