Summary
In GH_AUTH_MODE=token (and gh-cli) mode, /api/auth/status reports the token as authenticated, but every dashboard/data endpoint returns 401 {"ok":false,"error":"authentication required","needsAuth":true}. The UI then shows "Connect an account / GITHUB_TOKEN is not available", even though the token is valid and present.
Symptoms
GET /api/auth/status → {"authenticated":true,"login":"…","mode":"token"} ✅
GET /api/repos (and /api/issues, /api/pulls, notifications, CI health) → 401 {"ok":false,"error":"authentication required","needsAuth":true} ❌
GET /api/accounts → {"accounts":[],"activeId":null} (empty)
Root cause
The status endpoint and the data layer resolve auth through two different paths, and only the status path understands env / gh-cli tokens:
/api/auth/status → getProviderStatus() in src/server/authProvider.ts. In token mode it calls loadEnvToken(), validates the env token, and returns authenticated:true. ✅
- Data endpoints →
src/server/dashboardData.ts resolveActive() (~L64) → getActive() in src/server/accountStore.ts. getActive() only returns accounts from the on-disk account store. In pure token/gh-cli mode nothing ever writes to that store, so it returns null → authFail() → 401 needsAuth on every data endpoint.
src/server/accountStore.ts doInit() seeds the store only from accounts.json or a legacy auth.json — there is no code path that seeds it from GITHUB_TOKEN / the gh CLI. So env-token and gh-cli modes leave the store permanently empty.
Notably the infrastructure for a fix already exists: accountStore.getActive() explicitly prefers an ephemeral env-based account (if (s.ephemeral.length > 0) return s.ephemeral[0]) and add() supports ephemeral:true accounts — but no code path ever creates that ephemeral account in token/gh-cli mode.
Reproduction
# empty account store (no ~/.gitdeck/accounts.json, no legacy auth.json)
GH_AUTH_MODE=token GITHUB_TOKEN=<a PAT with repo + read:org> npm run api
curl -s localhost:8765/api/auth/status # {"authenticated":true,...}
curl -s localhost:8765/api/repos # 401 {"ok":false,"error":"authentication required","needsAuth":true}
gh-cli mode (GH_AUTH_MODE=gh-cli with gh auth login done) has the same gap: the status endpoint authenticates but the data endpoints 401.
Suggested fix
When the auth mode is token/gh-cli, register an ephemeral account synthesised from the external credential (providerKind:"github", providerConfigId:"github.com", accessToken = env / gh token, ephemeral:true) so getActive() resolves it and the data endpoints use it. This reuses the ephemeral-account support already present in accountStore.
I have a fix + regression tests ready and will open a PR referencing this issue.
Secondary note (not necessarily a bug)
listOwners in src/server/providers/github.ts (~L265) enumerates owners via /user/orgs. Fine-grained PATs return [] there, so only personal repos are listed — a classic read:org PAT is required to see org repos. This is GitHub token behaviour rather than a GitDeck bug, but it may be worth documenting the classic-token requirement (or allowing explicit orgs/owners to be configured) so fine-grained-PAT users aren't surprised by missing org repos.
Summary
In
GH_AUTH_MODE=token(andgh-cli) mode,/api/auth/statusreports the token as authenticated, but every dashboard/data endpoint returns401 {"ok":false,"error":"authentication required","needsAuth":true}. The UI then shows "Connect an account / GITHUB_TOKEN is not available", even though the token is valid and present.Symptoms
GET /api/auth/status→{"authenticated":true,"login":"…","mode":"token"}✅GET /api/repos(and/api/issues,/api/pulls, notifications, CI health) →401 {"ok":false,"error":"authentication required","needsAuth":true}❌GET /api/accounts→{"accounts":[],"activeId":null}(empty)Root cause
The status endpoint and the data layer resolve auth through two different paths, and only the status path understands env / gh-cli tokens:
/api/auth/status→getProviderStatus()insrc/server/authProvider.ts. Intokenmode it callsloadEnvToken(), validates the env token, and returnsauthenticated:true. ✅src/server/dashboardData.tsresolveActive()(~L64) →getActive()insrc/server/accountStore.ts.getActive()only returns accounts from the on-disk account store. In pure token/gh-cli mode nothing ever writes to that store, so it returnsnull→authFail()→401 needsAuthon every data endpoint.src/server/accountStore.tsdoInit()seeds the store only fromaccounts.jsonor a legacyauth.json— there is no code path that seeds it fromGITHUB_TOKEN/ the gh CLI. So env-token and gh-cli modes leave the store permanently empty.Notably the infrastructure for a fix already exists:
accountStore.getActive()explicitly prefers an ephemeral env-based account (if (s.ephemeral.length > 0) return s.ephemeral[0]) andadd()supportsephemeral:trueaccounts — but no code path ever creates that ephemeral account in token/gh-cli mode.Reproduction
gh-climode (GH_AUTH_MODE=gh-cliwithgh auth logindone) has the same gap: the status endpoint authenticates but the data endpoints 401.Suggested fix
When the auth mode is
token/gh-cli, register an ephemeral account synthesised from the external credential (providerKind:"github",providerConfigId:"github.com",accessToken= env / gh token,ephemeral:true) sogetActive()resolves it and the data endpoints use it. This reuses the ephemeral-account support already present inaccountStore.I have a fix + regression tests ready and will open a PR referencing this issue.
Secondary note (not necessarily a bug)
listOwnersinsrc/server/providers/github.ts(~L265) enumerates owners via/user/orgs. Fine-grained PATs return[]there, so only personal repos are listed — a classicread:orgPAT is required to see org repos. This is GitHub token behaviour rather than a GitDeck bug, but it may be worth documenting the classic-token requirement (or allowing explicit orgs/owners to be configured) so fine-grained-PAT users aren't surprised by missing org repos.