Background
Since #27, group-scoped query methods (app.certified.group.member.list, app.certified.group.audit.query) and the raw/body-less methods (repo.uploadBlob, group.destroy) name the target group with repo on the querystring (?repo=<handle-or-did>). This follows the AT Protocol convention (stock com.atproto.repo.* queries put repo in the querystring) and is what an unmodified @atproto/api client emits.
Concern
A querystring value commonly lands in:
- server access logs,
- reverse-proxy / CDN logs,
- browser history and
Referer headers.
So repo — the target group's DID — is exposed as metadata (which group's endpoint was queried) to anyone with log access. It is not a high-severity leak:
- the auth token is in the
Authorization header (not the URL),
- the response (the member list / audit entries) is in the body (not the URL),
- a group DID is a public, resolvable identity, not a secret.
But it is a real metadata exposure: for a group whose mere existence is sensitive, its DID appearing in shared logs is a small disclosure. See the "Querystring visibility" subsection in docs/design/aud-deprecation.md.
Why repo can't simply move to the request body
The constraint is the AT Protocol query contract, not raw HTTP. An atproto query lexicon declares a parameters block (→ querystring) and no input schema — there is no place to declare body fields for a query, and the xrpc client/server route queries as GET with params in the querystring. (HTTP GET can carry a body in the abstract, but fetch — what @atproto/api uses — forbids it, and a GET body has no defined semantics per RFC 9110.) So a stock-SDK consumer calling these typed query methods has no API to put repo in a body; the querystring is the only SDK-supported channel. Requiring a body would force callers off the typed SDK into hand-built requests — the exact DX failure #27 exists to remove.
Investigate
Given that a query body is off the table, the realistic options are:
- Accept
repo via an HTTP header (e.g. a CGS-specific header) as an alternative to the querystring, keeping the querystring form for SDK compatibility. Does any standard atproto client set such a header on a typed query call? Likely requires bypassing the typed SDK → DX cost; investigate whether @atproto/api exposes per-call header injection cleanly.
- Re-type these methods as procedures (POST with a body). Non-standard for a read, and breaks "a stock query call just works" — probably a net DX loss, but worth weighing.
- Accept the status quo (atproto parity) and treat the metadata exposure as a known, low-severity, documented tradeoff (current state).
The bar: any mitigation must not regress the stock-@atproto/api-client DX that #27 exists to provide. If none clears that bar, close as "won't fix — atproto parity, documented."
Refs
Background
Since #27, group-scoped query methods (
app.certified.group.member.list,app.certified.group.audit.query) and the raw/body-less methods (repo.uploadBlob,group.destroy) name the target group withrepoon the querystring (?repo=<handle-or-did>). This follows the AT Protocol convention (stockcom.atproto.repo.*queries putrepoin the querystring) and is what an unmodified@atproto/apiclient emits.Concern
A querystring value commonly lands in:
Refererheaders.So
repo— the target group's DID — is exposed as metadata (which group's endpoint was queried) to anyone with log access. It is not a high-severity leak:Authorizationheader (not the URL),But it is a real metadata exposure: for a group whose mere existence is sensitive, its DID appearing in shared logs is a small disclosure. See the "Querystring visibility" subsection in
docs/design/aud-deprecation.md.Why
repocan't simply move to the request bodyThe constraint is the AT Protocol query contract, not raw HTTP. An atproto
querylexicon declares aparametersblock (→ querystring) and noinputschema — there is no place to declare body fields for a query, and the xrpc client/server route queries as GET with params in the querystring. (HTTP GET can carry a body in the abstract, butfetch— what@atproto/apiuses — forbids it, and a GET body has no defined semantics per RFC 9110.) So a stock-SDK consumer calling these typed query methods has no API to putrepoin a body; the querystring is the only SDK-supported channel. Requiring a body would force callers off the typed SDK into hand-built requests — the exact DX failure #27 exists to remove.Investigate
Given that a query body is off the table, the realistic options are:
repovia an HTTP header (e.g. a CGS-specific header) as an alternative to the querystring, keeping the querystring form for SDK compatibility. Does any standard atproto client set such a header on a typed query call? Likely requires bypassing the typed SDK → DX cost; investigate whether@atproto/apiexposes per-call header injection cleanly.The bar: any mitigation must not regress the stock-
@atproto/api-client DX that #27 exists to provide. If none clears that bar, close as "won't fix — atproto parity, documented."Refs
audin CGS (deprecate group-DID-in-aud overload, backwards-compatible migration) #27 (theaud/repotargeting fix that introduced querystringrepo)docs/design/aud-deprecation.md— "Security:repois unsigned" → "Querystring visibility"