Skip to content

Connect inbox POST handling to feder-core for supported Follow activities#37

Merged
sij411 merged 8 commits into
fedify-dev:mainfrom
sij411:feat/inbox
Jul 7, 2026
Merged

Connect inbox POST handling to feder-core for supported Follow activities#37
sij411 merged 8 commits into
fedify-dev:mainfrom
sij411:feat/inbox

Conversation

@sij411

@sij411 sij411 commented Jul 7, 2026

Copy link
Copy Markdown
Member

Closes #24

  • Adds /users/{username}/inbox POST handling

  • Validates inbox target, content type, body size, JSON shape, and auth policy

  • Parses supported Follow activities into feder-vocab

  • Converts Follow into feder_core::Input and calls FederCore::handle

  • Adds tests for valid Follow, malformed/unsupported/oversized requests, and
    unsigned auth rejection

  • Keeps storage/delivery execution and real HTTP Signature verification out of
    scope

  • Codex 5.5 is used for only refactoring and adding tests

Summary by CodeRabbit

  • New Features

    • Added a runnable single-user server example for quickly starting a local ActivityPub server.
    • Introduced inbox handling for supported Follow requests, with a configurable authentication policy.
    • Added a health check endpoint and WebFinger discovery support in the server router.
  • Bug Fixes

    • Updated test setup and configuration handling for more consistent local server behavior.
  • Documentation

    • Reworked server and example docs to explain how to configure and run the demo.

sij411 added 7 commits July 7, 2026 22:44
  Move the hardcoded single-user demo into examples/single-user-server
  and
  leave feder-runtime-server as reusable Axum/Tokio integration. Expose
  the
  router/config building blocks, remove default local demo config from
  the
  library, and test the reusable routes directly.

  Assisted-by: Codex:gpt-5.5
  Covers invalid inbox targets, unsupported content types, malformed JSON,
  unsupported activities, and oversized request bodies.

  Assisted-by: Codex:gpt-5.5
  Reject unsigned inbox requests before core handling when signed inbox
  auth is required, while keeping explicit insecure dev mode for tests
  and the single-user example.

  Assisted-by: Codex:gpt-5.5
…ding

  the runtime inbox auth policy.

  Assisted-by: Codex:gpt-5.5
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7c76b9a4-f9f7-4dc8-bce6-ed676daf9313

📥 Commits

Reviewing files that changed from the base of the PR and between d5428f8 and 30939a6.

📒 Files selected for processing (1)
  • crates/feder-runtime-server/README.md
✅ Files skipped from review due to trivial changes (1)
  • crates/feder-runtime-server/README.md

📝 Walkthrough

Walkthrough

Adds an ActivityPub inbox handler with configurable auth policy, updates runtime configuration and router wiring around owned config, removes the note-backed server path and standalone binary, and adds a single-user example crate with updated tests and docs.

Changes

Runtime server inbox feature

Layer / File(s) Summary
Core received-follow constructor
crates/feder-core/src/lib.rs
Adds Input::received_follow(follow, accept_id) for building Input::ReceivedFollow.
RuntimeConfig and inbox policy contract
crates/feder-runtime-server/src/config.rs
Adds InboxAuthPolicy, changes RuntimeConfig fields, removes default_local, and adds test_config.
App state and router wiring
crates/feder-runtime-server/src/app.rs
Reworks AppState, rebuilds state from owned config, registers the inbox route and body limit, and adds the health check test.
Inbox handler and tests
crates/feder-runtime-server/src/inbox.rs
Implements inbox validation, Follow parsing, core submission, response mapping, and async tests for success and rejection cases.
Module exports and binary removal
crates/feder-runtime-server/src/lib.rs, crates/feder-runtime-server/src/main.rs
Adds the inbox module export, removes the note module export, re-exports crate root items, and deletes the standalone binary entrypoint.
Tests, docs, and example crate
crates/feder-runtime-server/src/actor.rs, crates/feder-runtime-server/src/webfinger.rs, crates/feder-runtime-server/README.md, Cargo.toml, examples/single-user-server/*
Updates actor/webfinger tests to use test_config(), rewrites the runtime-server README, and adds the new single-user-server workspace package, README, and main program.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant InboxHandler
  participant FederCore
  Client->>InboxHandler: POST /users/{username}/inbox (Follow JSON)
  InboxHandler->>InboxHandler: verify_inbox_request(policy)
  InboxHandler->>InboxHandler: parse JSON as Follow
  InboxHandler->>InboxHandler: accept_id_for_follow(follow)
  InboxHandler->>FederCore: Input::received_follow(follow, accept_id)
  FederCore-->>InboxHandler: state updated
  InboxHandler-->>Client: 202 Accepted
Loading

Possibly related issues

Possibly related PRs

  • fedify-dev/feder#21: Adds the Input/ReceivedFollow core flow that Input::received_follow(...) directly extends.
  • fedify-dev/feder#28: Introduces the RuntimeConfig, AppState, and build_router skeleton that this PR changes.
  • fedify-dev/feder#33: Implements the earlier /notes/{id} server path that this PR removes while reshaping the router.

Suggested labels: phase 1

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately names the new inbox POST handling and core handoff for supported Follow activities.
Linked Issues check ✅ Passed The PR adds the inbox route, validates content type and size, parses Follow requests, calls FederCore::handle, and rejects bad or unsigned requests.
Out of Scope Changes check ✅ Passed The changes stay focused on inbox handling and the supporting example/runtime refactor, with no clear unrelated code added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sij411 sij411 changed the title Feat/inbox Connect inbox POST handling to feder-core for supported Follow activities Jul 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/feder-runtime-server/README.md`:
- Around line 19-38: The README example using RuntimeConfig is missing the
required inbox_auth_policy field, so it does not compile as written. Update the
build_router example to include inbox_auth_policy in the RuntimeConfig literal,
matching the current RuntimeConfig definition in config.rs, and keep the example
aligned with the actual set of required fields.
- Around line 12-13: The README still says ActivityPub inbox handling is
deferred, but this PR now implements it via inbox.rs and wiring in build_router.
Update the documentation text in the README to remove inbox handling from the
list of deferred items, and make sure the remaining wording only mentions the
parts that are still actually postponed.

In `@crates/feder-runtime-server/src/config.rs`:
- Around line 20-24: InboxAuthPolicy::RequireSigned is currently treated as an
unconditional reject in verify_inbox_request, so legitimate signed deliveries
are still denied. Update verify_inbox_request to perform real signature
validation when RequireSigned is selected, using the existing request/auth
helpers in the inbox request path, and only return UNAUTHORIZED when the
signature check fails; if that plumbing does not exist yet, remove or disable
RequireSigned from InboxAuthPolicy until it is implemented.

In `@crates/feder-runtime-server/src/inbox.rs`:
- Around line 108-110: The inbox handler is ignoring the HandleResult from
core.handle(input), so any actions it contains, including SendActivity(Accept),
are never dispatched to the follower’s inbox. Update the handler around
core.handle(input) to execute or forward the returned actions before returning
StatusCode::ACCEPTED, using the existing HandleResult/action handling path in
this module; if delivery is intentionally deferred, add an explicit TODO near
the discarded result instead of silently dropping it.
- Around line 52-57: The verify_inbox_request function currently treats
InboxAuthPolicy::RequireSigned as an enforced signature check, but it always
returns UNAUTHORIZED without inspecting InboxRequest. Update the policy handling
in verify_inbox_request (and any related InboxAuthPolicy naming or docs) so
RequireSigned is clearly marked as a placeholder or renamed to reflect that
signature verification is not implemented yet, rather than implying actual
enforcement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bbb20537-6a51-471a-9660-376dee22e578

📥 Commits

Reviewing files that changed from the base of the PR and between 469982a and d5428f8.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • Cargo.toml
  • crates/feder-core/src/lib.rs
  • crates/feder-runtime-server/Cargo.toml
  • crates/feder-runtime-server/README.md
  • crates/feder-runtime-server/src/actor.rs
  • crates/feder-runtime-server/src/app.rs
  • crates/feder-runtime-server/src/config.rs
  • crates/feder-runtime-server/src/inbox.rs
  • crates/feder-runtime-server/src/lib.rs
  • crates/feder-runtime-server/src/main.rs
  • crates/feder-runtime-server/src/note.rs
  • crates/feder-runtime-server/src/webfinger.rs
  • examples/single-user-server/Cargo.toml
  • examples/single-user-server/README.md
  • examples/single-user-server/src/main.rs
💤 Files with no reviewable changes (2)
  • crates/feder-runtime-server/src/main.rs
  • crates/feder-runtime-server/src/note.rs

Comment thread crates/feder-runtime-server/README.md Outdated
Comment thread crates/feder-runtime-server/README.md
Comment thread crates/feder-runtime-server/src/config.rs
Comment thread crates/feder-runtime-server/src/inbox.rs
Comment on lines +108 to +110
let _result = core.handle(input);

Ok(StatusCode::ACCEPTED.into_response())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect what core.handle returns and how actions are meant to be consumed.
fd -e rs -a . crates/feder-core/src | head
ast-grep run --pattern 'pub fn handle($$$) { $$$ }' --lang rust crates/feder-core/src/lib.rs
rg -nP 'fn handle\s*\(' crates/feder-core/src/lib.rs -A20

Repository: fedify-dev/feder

Length of output: 886


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the inbox handler and how handle results/actions are consumed elsewhere.
ast-grep outline crates/feder-runtime-server/src/inbox.rs --view expanded
printf '\n--- inbox.rs excerpt ---\n'
sed -n '1,220p' crates/feder-runtime-server/src/inbox.rs

printf '\n--- HandleResult / action consumption search ---\n'
rg -n "HandleResult|handle\\(input\\)|delivery_targets\\(|followers\\(|actions" crates -g '*.rs' -A4 -B4

printf '\n--- feder-core lib excerpt ---\n'
sed -n '1,140p' crates/feder-core/src/lib.rs

Repository: fedify-dev/feder

Length of output: 40687


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- search for action execution / HandleResult consumption ---'
rg -n "HandleResult|result\.actions|actions\.iter|Action::SendActivity|Action::StoreFollower|Action::StoreDeliveryTarget|Action::StoreObject|SendActivity" crates -g '*.rs' -A3 -B3

printf '\n%s\n' '--- list runtime server files ---'
fd -e rs -a crates/feder-runtime-server/src

printf '\n%s\n' '--- search for any dispatch/executor helpers ---'
rg -n "execute|dispatch|apply.*Action|perform.*Action|deliver|outbound|send activity" crates -g '*.rs' -A2 -B2

Repository: fedify-dev/feder

Length of output: 15349


Dispatch the Accept action from core.handle
core.handle(input) returns a HandleResult whose actions include SendActivity(Accept) for a valid follow, but this handler drops the result and returns 202 Accepted immediately. The follow is recorded in memory, but the Accept is never sent to the follower’s inbox, so the handshake can’t complete over the wire. The test only covers in-memory state; wire up action execution here or add a TODO if delivery is meant to be deferred.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/feder-runtime-server/src/inbox.rs` around lines 108 - 110, The inbox
handler is ignoring the HandleResult from core.handle(input), so any actions it
contains, including SendActivity(Accept), are never dispatched to the follower’s
inbox. Update the handler around core.handle(input) to execute or forward the
returned actions before returning StatusCode::ACCEPTED, using the existing
HandleResult/action handling path in this module; if delivery is intentionally
deferred, add an explicit TODO near the discarded result instead of silently
dropping it.

@sij411 sij411 requested review from Copilot and dahlia July 7, 2026 13:56
@sij411 sij411 self-assigned this Jul 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR wires an Axum-based inbox POST endpoint into the feder-core boundary for supported ActivityPub Follow activities, adds a runnable single-user example binary, and refactors feder-runtime-server into a reusable library-oriented integration crate.

Changes:

  • Added /users/{username}/inbox POST handler that parses Follow and forwards it to FederCore::handle, with basic content-type/auth-policy checks and a router body-size limit.
  • Introduced InboxAuthPolicy and adjusted runtime config/router construction to be consumer-provided (plus updated/added tests).
  • Added examples/single-user-server as the runnable demo and removed the old feder-runtime-server binary + seeded note endpoint.

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
examples/single-user-server/src/main.rs New demo binary wiring RuntimeConfig into build_router and starting an Axum server.
examples/single-user-server/README.md Run instructions for the new single-user demo.
examples/single-user-server/Cargo.toml Declares the new example crate and its runtime dependencies.
crates/feder-runtime-server/src/webfinger.rs Updates tests to use the new test_config() + build_router(config) API.
crates/feder-runtime-server/src/note.rs Removes the seeded note endpoint and its tests.
crates/feder-runtime-server/src/main.rs Removes the old runnable binary entrypoint (now replaced by the example).
crates/feder-runtime-server/src/lib.rs Exposes the new inbox module and re-exports build_router, RuntimeConfig, and Error.
crates/feder-runtime-server/src/inbox.rs Implements inbox POST handling and adds request/behavior tests.
crates/feder-runtime-server/src/config.rs Adds InboxAuthPolicy and introduces test_config() for crate tests.
crates/feder-runtime-server/src/app.rs Routes the new inbox endpoint, applies body-size limits, and refactors config ownership.
crates/feder-runtime-server/src/actor.rs Updates tests to match router/config refactor.
crates/feder-runtime-server/README.md Updates crate docs to describe library usage and point to the new demo.
crates/feder-runtime-server/Cargo.toml Adjusts dependencies for the new inbox logic (serde_json, percent-encoding) and dev-only tokio.
crates/feder-core/src/lib.rs Adds Input::received_follow constructor helper.
Cargo.toml Adds examples/single-user-server to the workspace members.
Cargo.lock Records new dependencies and the new example crate package.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/feder-runtime-server/src/inbox.rs
Comment thread crates/feder-runtime-server/README.md Outdated
Comment thread crates/feder-runtime-server/README.md
Comment thread crates/feder-runtime-server/src/inbox.rs

@dahlia dahlia left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One longer-term API thought, not a request to change this PR:

build_router(config) is a good simple shape for this vertical slice and the single-user example, but as the runtime grows we may eventually want Feder's Axum integration to be exposed as more composable pieces rather than a router builder that owns the route layout and middleware choices.

For example, it might be useful to expose the actor/webfinger/inbox handlers or route fragments separately, and let embedding applications decide where to mount them and which Axum layers to apply. That would also make things like the inbox body limit easier to configure at the application boundary instead of baking it into build_router.

I don't think this needs to block the current PR. It just seems worth keeping in mind before the feder-runtime-server API becomes more established.

@sij411

sij411 commented Jul 7, 2026

Copy link
Copy Markdown
Member Author

One longer-term API thought, not a request to change this PR:

build_router(config) is a good simple shape for this vertical slice and the single-user example, but as the runtime grows we may eventually want Feder's Axum integration to be exposed as more composable pieces rather than a router builder that owns the route layout and middleware choices.

For example, it might be useful to expose the actor/webfinger/inbox handlers or route fragments separately, and let embedding applications decide where to mount them and which Axum layers to apply. That would also make things like the inbox body limit easier to configure at the application boundary instead of baking it into build_router.

I don't think this needs to block the current PR. It just seems worth keeping in mind before the feder-runtime-server API becomes more established.

Thanks for sharing. I'll keep that in my mind :)

@sij411 sij411 merged commit 43b2224 into fedify-dev:main Jul 7, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Connect incoming inbox handling to feder-core

3 participants