GET /api/v1/repos/{owner}/{repo}/refs returns an empty list with a 200 when the owner is given in the full did:key: form. The same repository under the bare key form returns its refs. The request is authorized either way, so the caller gets a successful response that is missing data rather than an error explaining why.
Cause
branch_cids.repo is written as {normalize_owner_key(owner_did)}/{name}. The push path builds that slug from the repo record in crates/gitlawb-node/src/api/repos.rs before calling upsert_branch_cid.
list_refs in the same file builds its filter from the raw URL path segments instead:
let (_record, _rules) =
crate::api::authorize_repo_read(&state, &owner, &repo, caller, "/").await?;
let repo_slug = format!("{owner}/{repo}");
let refs = state.db.list_branch_cids(&repo_slug).await?;
get_repo normalizes the owner before matching, so did:key:zX and bare zX both resolve to the same repository and both pass the read gate. list_branch_cids then does an exact WHERE repo = $1, which the full-DID string never matches. The authorization step normalizes and the query step does not.
Reproduction
Seed a repository owned by did:key:zX and record a branch CID under the slug the push path writes (zX/name), then request both forms:
GET /api/v1/repos/zX/name/refs -> 200 {"count":1,...}
GET /api/v1/repos/did:key:zX/name/refs -> 200 {"count":0,"refs":[]}
Confirmed against bfc44f92 with a #[sqlx::test] driving both forms through build_router.
Notes
Present since the initial public release. list_refs has no test coverage today, which is why it went unnoticed.
The same shape was recently fixed in the Arweave anchors handler, and api/events.rs already builds its slug from the authorized record. Fixing this the same way keeps the three consistent.
GET /api/v1/repos/{owner}/{repo}/refsreturns an empty list with a 200 when the owner is given in the fulldid:key:form. The same repository under the bare key form returns its refs. The request is authorized either way, so the caller gets a successful response that is missing data rather than an error explaining why.Cause
branch_cids.repois written as{normalize_owner_key(owner_did)}/{name}. The push path builds that slug from the repo record incrates/gitlawb-node/src/api/repos.rsbefore callingupsert_branch_cid.list_refsin the same file builds its filter from the raw URL path segments instead:get_reponormalizes the owner before matching, sodid:key:zXand barezXboth resolve to the same repository and both pass the read gate.list_branch_cidsthen does an exactWHERE repo = $1, which the full-DID string never matches. The authorization step normalizes and the query step does not.Reproduction
Seed a repository owned by
did:key:zXand record a branch CID under the slug the push path writes (zX/name), then request both forms:Confirmed against
bfc44f92with a#[sqlx::test]driving both forms throughbuild_router.Notes
Present since the initial public release.
list_refshas no test coverage today, which is why it went unnoticed.The same shape was recently fixed in the Arweave anchors handler, and
api/events.rsalready builds its slug from the authorized record. Fixing this the same way keeps the three consistent.