Skip to content

feat(pds): proxy com.atproto.moderation.createReport to labeler#196

Merged
ascorbic merged 2 commits into
mainfrom
feat/create-report-proxy
May 31, 2026
Merged

feat(pds): proxy com.atproto.moderation.createReport to labeler#196
ascorbic merged 2 commits into
mainfrom
feat/create-report-proxy

Conversation

@ascorbic

Copy link
Copy Markdown
Owner

Summary

The Bluesky app's "Report" button calls com.atproto.moderation.createReport against the user's PDS. Today that request falls through to the generic XRPC proxy, which routes anything non-chat to api.bsky.app — the AppView, not a labeler — so reports are silently rejected.

This adds a first-class handler that proxies com.atproto.moderation.createReport to a moderation labeler:

  • Default: routes to Bluesky's moderation service (did:plc:ar7c4by46qjdydhdevvrndac#atproto_labeler) with a service-auth JWT addressed to it (aud = the labeler DID, lxm = com.atproto.moderation.createReport).
  • Client override: if the client sets an atproto-proxy: did:...#atproto_labeler header, routing follows that header and the JWT is addressed there instead. This lets users pick a different labeler from the app.

The pattern mirrors the existing app.bsky.feed.getFeed special-case in xrpc-proxy.ts: special-cased ahead of the catch-all so the outbound JWT lands at the right service.

Internally, handleXrpcProxy now accepts an optional defaultRoute ({ proxyDid, serviceId }) so the createReport handler can synthesize the same DID-doc-driven routing the atproto-proxy header path uses, without touching the catch-all's existing AppView/chat behavior. The catch-all paths and existing scope checks are unchanged.

The labeler DID is a static constant rather than an env var — opinionated default that matches Bluesky's reference PDS, and clients can override per-request. We can add config later if anyone needs a different default.

Test plan

  • Default routing: no atproto-proxy header → request goes to mod.bsky.app (Bluesky's mod service endpoint, resolved via PLC) and the service-auth JWT carries aud = did:plc:ar7c4by46qjdydhdevvrndac and lxm = com.atproto.moderation.createReport.
  • Header override: atproto-proxy: did:web:labeler.example.com#atproto_labeler → request goes to that labeler's resolved endpoint, JWT addressed there.
  • OAuth scope enforcement: a DPoP token scoped for createReport at a different audience returns 403 InsufficientScope and never reaches the labeler.
  • pnpm test:unit in packages/pds — 310 tests pass (20 in proxy.test.ts, up from 17).
  • pnpm check from repo root — green.

Reports default to Bluesky's moderation service
(did:plc:ar7c4by46qjdydhdevvrndac#atproto_labeler) with a service-auth
JWT addressed to that labeler. Clients can override the target labeler
via the atproto-proxy header. Mirrors the established getFeed pattern:
special-cased ahead of the generic AppView proxy so the outbound JWT
is addressed correctly.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented May 31, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
pdscheck 535cf84 May 31 2026, 01:56 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented May 31, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
cirrusdocs 535cf84 Commit Preview URL

Branch Preview URL
May 31 2026, 01:56 PM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented May 31, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
atproto-pds 535cf84 May 31 2026, 01:56 PM

@pkg-pr-new

pkg-pr-new Bot commented May 31, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/create-pds@196
npm i https://pkg.pr.new/@getcirrus/oauth-provider@196
npm i https://pkg.pr.new/@getcirrus/pds@196

commit: 535cf84

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 fixes Bluesky app report submissions by special-casing com.atproto.moderation.createReport so PDS instances proxy reports to a moderation labeler (defaulting to Bluesky’s moderation service) instead of falling through to the generic XRPC proxy behavior that routes most non-chat traffic to the AppView (where reports are rejected).

Changes:

  • Add a dedicated /xrpc/com.atproto.moderation.createReport POST handler that routes to a moderation labeler (default PLC DID) unless overridden via atproto-proxy.
  • Extend handleXrpcProxy to accept an optional DID-doc-resolved defaultRoute, enabling “header-like” routing without changing existing catch-all behavior.
  • Add unit tests covering default routing, atproto-proxy override routing, and DPoP scope enforcement.

Reviewed changes

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

File Description
packages/pds/src/xrpc-proxy.ts Adds default-route support to handleXrpcProxy and introduces a createReport-specific proxy handler targeting a labeler by default.
packages/pds/src/index.ts Wires a first-class createReport POST route ahead of the /xrpc/* catch-all.
packages/pds/test/proxy.test.ts Adds coverage validating routing destination + service-auth JWT claims for createReport, including DPoP scope rejection.
.changeset/create-report-proxy.md Documents the new proxy behavior and ships it as a minor bump for @getcirrus/pds.

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

@ascorbic ascorbic enabled auto-merge (squash) May 31, 2026 13:56
@ascorbic ascorbic merged commit c560f2e into main May 31, 2026
7 checks passed
@ascorbic ascorbic deleted the feat/create-report-proxy branch May 31, 2026 13:56
@mixie-bot mixie-bot Bot mentioned this pull request May 31, 2026
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.

2 participants