commits
- Bug fixes: sidebar handle display, takedown status, password reset
display, create account success card, invite code counts, disable
invite field name, search uses email param per lexicon spec
- RBAC: gate Accounts link and dashboard account count behind
can_view_accounts permission
- Feature: request-crawl page (com.atproto.sync.requestCrawl to relay)
- Feature: dashboard shows phone verification, server links, contact
- Feature: account detail shows repo status, handle resolution,
collections, threat signatures, takedown reference
- Polish: invite code pagination with cursor, copy-to-clipboard buttons
- Add jacquard-api dependency, remove hand-rolled response structs
Problem: Bluesky's reference PDS using a PDS_ADMIN_PASSWORD with basic auth as
an admin api authz. If the PDS is managed by a team, then this
becomes a shared password.
Solution: have pds-gateway manage the PDS_ADMIN_PASSWORD and have the
PDS administration team login to pds-gateway via OAuth with their
corresponding PDS, and then they'll be given a portal scoped to their
capabilities specified by the RBAC scpecification, see ADMIN.md. Admin
portal is inspired by https://github.com/betamax/pds-admin. When an
admin team member wants to perform an operation (e.g. send a new users
an invite code), the team member can click through the portal to do so
and the pds-gateway will perform the operations with the
PDS_ADMIN_PASSWORD on their behalf. This prevents the shared password
problem whilst giving admins a tool to manage their PDS easier!
Add GATEKEEPER_REQUEST_LOGGING=true to enable per-request tracing
with method, path, client_ip, status, and latency_ms. Disabled by
default — existing behavior unchanged.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Firefox rejects credentialed cross-origin requests when
Access-Control-Allow-Headers is `*`, because per the Fetch spec
the wildcard is not treated as a wildcard when credentials are
involved. This causes failures on endpoints like getSession
and createSession when accessed from bsky.app.
Switch from `.allow_headers(Any)` to `.allow_headers(AllowHeaders::mirror_request())`
which echoes the browser's Access-Control-Request-Headers back verbatim,
matching the behaviour of the PDS's Express cors middleware.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
example
```
// Multi-community endpoint
.route("/xrpc/community.shared.moderation.report",
post(report).layer(from_fn_with_state(
with_rules(AuthRules::All(vec![
AuthRules::HandleEndsWithAny(vec![
".blacksky.team".into(),
".bsky.team".into(),
".mod.social".into(),
]),
AuthRules::ScopeEquals("atproto".into()),
]), &state),
auth_middleware
)))
```
Adds two new features to allow PDS admins to gatekeep their PDS from bots creating accounts instead of invite codes
- Bug fixes: sidebar handle display, takedown status, password reset
display, create account success card, invite code counts, disable
invite field name, search uses email param per lexicon spec
- RBAC: gate Accounts link and dashboard account count behind
can_view_accounts permission
- Feature: request-crawl page (com.atproto.sync.requestCrawl to relay)
- Feature: dashboard shows phone verification, server links, contact
- Feature: account detail shows repo status, handle resolution,
collections, threat signatures, takedown reference
- Polish: invite code pagination with cursor, copy-to-clipboard buttons
- Add jacquard-api dependency, remove hand-rolled response structs
Problem: Bluesky's reference PDS using a PDS_ADMIN_PASSWORD with basic auth as
an admin api authz. If the PDS is managed by a team, then this
becomes a shared password.
Solution: have pds-gateway manage the PDS_ADMIN_PASSWORD and have the
PDS administration team login to pds-gateway via OAuth with their
corresponding PDS, and then they'll be given a portal scoped to their
capabilities specified by the RBAC scpecification, see ADMIN.md. Admin
portal is inspired by https://github.com/betamax/pds-admin. When an
admin team member wants to perform an operation (e.g. send a new users
an invite code), the team member can click through the portal to do so
and the pds-gateway will perform the operations with the
PDS_ADMIN_PASSWORD on their behalf. This prevents the shared password
problem whilst giving admins a tool to manage their PDS easier!
Firefox rejects credentialed cross-origin requests when
Access-Control-Allow-Headers is `*`, because per the Fetch spec
the wildcard is not treated as a wildcard when credentials are
involved. This causes failures on endpoints like getSession
and createSession when accessed from bsky.app.
Switch from `.allow_headers(Any)` to `.allow_headers(AllowHeaders::mirror_request())`
which echoes the browser's Access-Control-Request-Headers back verbatim,
matching the behaviour of the PDS's Express cors middleware.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
example
```
// Multi-community endpoint
.route("/xrpc/community.shared.moderation.report",
post(report).layer(from_fn_with_state(
with_rules(AuthRules::All(vec![
AuthRules::HandleEndsWithAny(vec![
".blacksky.team".into(),
".bsky.team".into(),
".mod.social".into(),
]),
AuthRules::ScopeEquals("atproto".into()),
]), &state),
auth_middleware
)))
```