Switch AtProto:ProfileScope to granular once a PDS accepts the narrow scopes #129

Closed
opened 2026-08-19 21:24:16 +00:00 by claude-bot · 3 comments
Collaborator

/profile (added for #115) can edit a member's display name and avatar, but only with AtProto:ProfileScope=generic, which asks for transition:generic — the member's whole account — to write one record.

The scope the feature actually needs is repo:app.bsky.actor.profile blob:image/*, and the code already builds and recognises it (ProfileScope, mode granular). It is not usable yet: on 2026-08-19, bsky.social, pds.ponente.app and pds.grandiras.net all advertised only

"scopes_supported": ["atproto", "transition:email", "transition:generic", "transition:chat.bsky"]

at /.well-known/oauth-authorization-server, and an authorization request carrying an unknown scope is rejected outright — so asking for it today breaks sign-in for everyone.

What to do when the PDS image grows the scopes:

  1. curl -s https://pds.ponente.app/.well-known/oauth-authorization-server | jq .scopes_supported and check the same for bsky.social (members are not all on our PDS).
  2. Set ATPROTO_PROFILE_SCOPE=granular in deploy/.env.
  3. Everyone signs in once more; the page tells them so.
  4. Update the measurement paragraph in docs/auth.md — it is dated on purpose.

No code change is expected. Worth a periodic re-check: this is the difference between Ponente holding a token that can post as a member and one that can only rename them.


Filed by Claude while working on #115 (run).

`/profile` (added for #115) can edit a member's display name and avatar, but only with `AtProto:ProfileScope=generic`, which asks for `transition:generic` — the member's whole account — to write one record. The scope the feature actually needs is `repo:app.bsky.actor.profile blob:image/*`, and the code already builds and recognises it (`ProfileScope`, mode `granular`). It is not usable yet: on 2026-08-19, `bsky.social`, `pds.ponente.app` and `pds.grandiras.net` all advertised only ``` "scopes_supported": ["atproto", "transition:email", "transition:generic", "transition:chat.bsky"] ``` at `/.well-known/oauth-authorization-server`, and an authorization request carrying an unknown scope is rejected outright — so asking for it today breaks sign-in for everyone. **What to do when the PDS image grows the scopes:** 1. `curl -s https://pds.ponente.app/.well-known/oauth-authorization-server | jq .scopes_supported` and check the same for `bsky.social` (members are not all on our PDS). 2. Set `ATPROTO_PROFILE_SCOPE=granular` in `deploy/.env`. 3. Everyone signs in once more; the page tells them so. 4. Update the measurement paragraph in `docs/auth.md` — it is dated on purpose. No code change is expected. Worth a periodic re-check: this is the difference between Ponente holding a token that can post as a member and one that can only rename them. --- _Filed by Claude while working on #115 ([run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/310))._
Author
Collaborator

🤖 On it — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 On it — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/343); the reply will link the full session transcript. <!-- claude-run:comment --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/issue-129/run-343.md) — updating every ~5 minutes while the task runs.
Author
Collaborator

🤖 On it — follow along in the action run; the reply will link the full session transcript.

📜 Live transcript — updating every ~5 minutes while the task runs.

🤖 On it — follow along in [the action run](https://git.grandiras.net/Grandiras/claude-bot/actions/runs/343); the reply will link the full session transcript. <!-- claude-run:comment --> 📜 [Live transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/issue-129/run-343.md) — updating every ~5 minutes while the task runs.
Author
Collaborator

Re-checked the blocker. The switch is still not possible — no configuration change made.

What I measured (2026-08-21)

$ curl -s https://<host>/.well-known/oauth-authorization-server | jq .scopes_supported
bsky.social        ["atproto","transition:email","transition:generic","transition:chat.bsky"]
pds.grandiras.net  ["atproto","transition:email","transition:generic","transition:chat.bsky"]
pds.ponente.app    ["atproto","transition:email","transition:generic","transition:chat.bsky"]

Identical to the 2026-08-19 measurement in the issue: no repo:*, no blob:*. Setting ATPROTO_PROFILE_SCOPE=granular today would still send an unknown scope in the authorization request and break sign-in for every member.

I also checked whether this is just a stale image, since the answer would be different if it were:

  • Both our PDSes report {"version":"0.4.5027"} at /xrpc/_health.
  • ghcr.io/bluesky-social/pds:latest (which all three compose files use) resolves to org.opencontainers.image.version: 0.4.5027, built 2026-08-11, revision 374cf1d.

So the deployment is already on the newest published PDS image, and the granular scopes are not in it. bsky.social runs ahead of the released image (version is a bare commit sha there) and does not advertise them either — which means members on Bluesky's own PDS would be broken too, not just ours. Nothing to pull, nothing to flip.

What I changed

Branch claude/issue-129 — one paragraph in docs/auth.md. The "Nothing accepts it yet" bullet is dated on purpose, so I recorded the re-measurement rather than leaving a single 2026-08-19 reading that looks unverified two days later, and added the image-version evidence so the next person re-checking doesn't repeat the "maybe we're just on an old image" investigation. No code change, no changelog entry (nothing a member would notice).

Verified with dotnet test Ponente.sln — 380 passed, including DocumentationTests and ProfileScopeTests.

The issue should stay open: it is a standing re-check, and the trigger condition hasn't fired.


⏱ 1m 36s · 14 turns · tokens: 21.2k in / 5.6k out (+429.5k cache-read)
📜 Full session transcript
→ Opened PR: #138

Re-checked the blocker. **The switch is still not possible — no configuration change made.** ## What I measured (2026-08-21) ``` $ curl -s https://<host>/.well-known/oauth-authorization-server | jq .scopes_supported bsky.social ["atproto","transition:email","transition:generic","transition:chat.bsky"] pds.grandiras.net ["atproto","transition:email","transition:generic","transition:chat.bsky"] pds.ponente.app ["atproto","transition:email","transition:generic","transition:chat.bsky"] ``` Identical to the 2026-08-19 measurement in the issue: no `repo:*`, no `blob:*`. Setting `ATPROTO_PROFILE_SCOPE=granular` today would still send an unknown scope in the authorization request and break sign-in for every member. I also checked whether this is just a stale image, since the answer would be different if it were: - Both our PDSes report `{"version":"0.4.5027"}` at `/xrpc/_health`. - `ghcr.io/bluesky-social/pds:latest` (which all three compose files use) resolves to `org.opencontainers.image.version: 0.4.5027`, built 2026-08-11, revision `374cf1d`. So the deployment is already on the newest published PDS image, and the granular scopes are not in it. `bsky.social` runs ahead of the released image (`version` is a bare commit sha there) and does not advertise them either — which means members on Bluesky's own PDS would be broken too, not just ours. Nothing to pull, nothing to flip. ## What I changed Branch **`claude/issue-129`** — one paragraph in `docs/auth.md`. The "Nothing accepts it yet" bullet is dated on purpose, so I recorded the re-measurement rather than leaving a single 2026-08-19 reading that looks unverified two days later, and added the image-version evidence so the next person re-checking doesn't repeat the "maybe we're just on an old image" investigation. No code change, no changelog entry (nothing a member would notice). Verified with `dotnet test Ponente.sln` — 380 passed, including `DocumentationTests` and `ProfileScopeTests`. The issue should stay open: it is a standing re-check, and the trigger condition hasn't fired. --- ⏱ 1m 36s · 14 turns · tokens: 21.2k in / 5.6k out (+429.5k cache-read) 📜 [Full session transcript](https://git.grandiras.net/Grandiras/claude-bot/src/branch/claude-logs/transcripts/Grandiras/Ponente/issue-129/run-343.md) → Opened PR: https://git.grandiras.net/Grandiras/Ponente/pulls/138
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Grandiras/Ponente#129
No description provided.