Once a user is authenticated (#3, Dex/OIDC), surface their own Gitea projects and issues inside cad-atlas — turning the static atlas into a personalized view where the CAD pipeline stages link to the user's real repos/issues/PRs.
Depends on
#3 (auth) — need the authenticated user identity to scope Gitea data. Closed — Authentik forward-auth gates atlas.d-ma.be at the ingress (not in-app OIDC middleware as originally scoped; identity headers X-authentik-username etc. are forwarded but unused here — see decision below).
Decision (2026-07-20) — static PAT, not per-visitor OAuth
cad-atlas is a single-operator homelab tool (one real user: Mathias). True per-user Gitea OAuth
token exchange is heavy machinery for a userbase of one. Went with: a server-held read-only
Gitea PAT (mirrors the DMABE_GITEA_API_TOKEN / tapir-session-secret pattern) driving calls
scoped to Mathias's own repos/issues. Anyone who clears Authentik forward-auth sees Mathias's
data — correct for this deployment, revisit only if cad-atlas ever gets a second real user with
different repo access.
Shipped (2026-07-20, b2761a4) — first slice: open issues on stage 03
internal/atlas/issues.go — IssueNodes(), TDD'd pure parser: Gitea /repos/issues/search
response → one stage node per issue (title, repo tag, clickable html_url).
internal/gitea/client.go — MyIssues(), authenticated GET using GITEA_TOKEN env; returns
an error (skip gracefully) when unset, mirroring the existing liveOverlay fallback pattern.
internal/web/handler.go — wired into liveOverlay(), merges onto stage 03 (Spec → Gitea issue, generate:"gitea-issues" in atlas.json).
Frontend: nodes with a url now render as clickable <a class="node"> instead of <div>.
Verified live end-to-end: real DMABE_GITEA_API_TOKEN against git.d-ma.be, stage 03 showed
8 real open issues across infra/agentsquad/signals/cad-atlas with correct links. Without GITEA_TOKEN set, falls back to the 3 authored placeholder nodes (no crash) — confirmed both
paths.
Not yet shipped
GITEA_TOKEN provisioning in prod — needs an ExternalSecret in mathias/infra k3s/apps/cad-atlas/ (mirror tapir-session-secret) + a scoped read-only PAT. Deferred:
this session's mathias/infra local clone had a stale/expired git credential (auth
failure on git pull), and secret-writes need their own explicit confirmation regardless.
Until this lands, the overlay is inert in prod (graceful fallback, not broken).
User's own repos panel (only issues shipped this slice).
PRs panel.
Recent Actions runs, mapped per-repo (stage 06 already has this for cad-atlas's own repo
via #5; this would generalize it across the user's repos).
Notes
Keep it read-only first. Respect Gitea's own visibility — satisfied here since the PAT is
Mathias's own account, so it only ever sees what Mathias can see.
## Goal
Once a user is authenticated (#3, Dex/OIDC), surface **their own Gitea projects and issues** inside cad-atlas — turning the static atlas into a personalized view where the CAD pipeline stages link to the user's real repos/issues/PRs.
## Depends on
- #3 (auth) — need the authenticated user identity to scope Gitea data. **Closed** — Authentik forward-auth gates `atlas.d-ma.be` at the ingress (not in-app OIDC middleware as originally scoped; identity headers `X-authentik-username` etc. are forwarded but unused here — see decision below).
## Decision (2026-07-20) — static PAT, not per-visitor OAuth
cad-atlas is a single-operator homelab tool (one real user: Mathias). True per-user Gitea OAuth
token exchange is heavy machinery for a userbase of one. Went with: a server-held read-only
Gitea PAT (mirrors the `DMABE_GITEA_API_TOKEN` / tapir-session-secret pattern) driving calls
scoped to Mathias's own repos/issues. Anyone who clears Authentik forward-auth sees Mathias's
data — correct for this deployment, revisit only if cad-atlas ever gets a second real user with
different repo access.
## Shipped (2026-07-20, b2761a4) — first slice: open issues on stage 03
- `internal/atlas/issues.go` — `IssueNodes()`, TDD'd pure parser: Gitea `/repos/issues/search`
response → one stage node per issue (title, repo tag, clickable `html_url`).
- `internal/gitea/client.go` — `MyIssues()`, authenticated GET using `GITEA_TOKEN` env; returns
an error (skip gracefully) when unset, mirroring the existing `liveOverlay` fallback pattern.
- `internal/web/handler.go` — wired into `liveOverlay()`, merges onto stage 03 (`Spec → Gitea
issue`, `generate:"gitea-issues"` in `atlas.json`).
- Frontend: nodes with a `url` now render as clickable `<a class="node">` instead of `<div>`.
- Verified live end-to-end: real `DMABE_GITEA_API_TOKEN` against `git.d-ma.be`, stage 03 showed
8 real open issues across infra/agentsquad/signals/cad-atlas with correct links. Without
`GITEA_TOKEN` set, falls back to the 3 authored placeholder nodes (no crash) — confirmed both
paths.
## Not yet shipped
- [ ] **`GITEA_TOKEN` provisioning in prod** — needs an ExternalSecret in `mathias/infra`
`k3s/apps/cad-atlas/` (mirror `tapir-session-secret`) + a scoped read-only PAT. Deferred:
this session's `mathias/infra` local clone had a stale/expired git credential (auth
failure on `git pull`), and secret-writes need their own explicit confirmation regardless.
Until this lands, the overlay is inert in prod (graceful fallback, not broken).
- [ ] User's own **repos** panel (only issues shipped this slice).
- [ ] **PRs** panel.
- [ ] Recent Actions runs, mapped per-repo (stage 06 already has this for cad-atlas's own repo
via #5; this would generalize it across the user's repos).
## Notes
Keep it read-only first. Respect Gitea's own visibility — satisfied here since the PAT is
Mathias's own account, so it only ever sees what Mathias can see.
Update: auth is now Authentik forward-auth (#3), so the authenticated user identity arrives as request headers — no in-app OIDC needed. The ingress passes X-authentik-username, X-authentik-email, X-authentik-groups, X-authentik-uid to the app.
So this issue simplifies: read those headers → map the Authentik user to their Gitea account → call the Gitea API for that user's projects/issues (pass-through auth pattern, brain knowledge/mcp-pass-through-auth-pattern.md). Open question: how to get the user's Gitea token/identity from the Authentik identity (Gitea also auths via Authentik/Dex — likely a shared OIDC subject or an app-password exchange).
Update: auth is now Authentik **forward-auth** (#3), so the authenticated user identity arrives as request headers — no in-app OIDC needed. The ingress passes `X-authentik-username`, `X-authentik-email`, `X-authentik-groups`, `X-authentik-uid` to the app.
So this issue simplifies: read those headers → map the Authentik user to their Gitea account → call the Gitea API for that user's projects/issues (pass-through auth pattern, brain `knowledge/mcp-pass-through-auth-pattern.md`). Open question: how to get the user's Gitea token/identity from the Authentik identity (Gitea also auths via Authentik/Dex — likely a shared OIDC subject or an app-password exchange).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Goal
Once a user is authenticated (#3, Dex/OIDC), surface their own Gitea projects and issues inside cad-atlas — turning the static atlas into a personalized view where the CAD pipeline stages link to the user's real repos/issues/PRs.
Depends on
atlas.d-ma.beat the ingress (not in-app OIDC middleware as originally scoped; identity headersX-authentik-usernameetc. are forwarded but unused here — see decision below).Decision (2026-07-20) — static PAT, not per-visitor OAuth
cad-atlas is a single-operator homelab tool (one real user: Mathias). True per-user Gitea OAuth
token exchange is heavy machinery for a userbase of one. Went with: a server-held read-only
Gitea PAT (mirrors the
DMABE_GITEA_API_TOKEN/ tapir-session-secret pattern) driving callsscoped to Mathias's own repos/issues. Anyone who clears Authentik forward-auth sees Mathias's
data — correct for this deployment, revisit only if cad-atlas ever gets a second real user with
different repo access.
Shipped (2026-07-20,
b2761a4) — first slice: open issues on stage 03internal/atlas/issues.go—IssueNodes(), TDD'd pure parser: Gitea/repos/issues/searchresponse → one stage node per issue (title, repo tag, clickable
html_url).internal/gitea/client.go—MyIssues(), authenticated GET usingGITEA_TOKENenv; returnsan error (skip gracefully) when unset, mirroring the existing
liveOverlayfallback pattern.internal/web/handler.go— wired intoliveOverlay(), merges onto stage 03 (Spec → Gitea issue,generate:"gitea-issues"inatlas.json).urlnow render as clickable<a class="node">instead of<div>.DMABE_GITEA_API_TOKENagainstgit.d-ma.be, stage 03 showed8 real open issues across infra/agentsquad/signals/cad-atlas with correct links. Without
GITEA_TOKENset, falls back to the 3 authored placeholder nodes (no crash) — confirmed bothpaths.
Not yet shipped
GITEA_TOKENprovisioning in prod — needs an ExternalSecret inmathias/infrak3s/apps/cad-atlas/(mirrortapir-session-secret) + a scoped read-only PAT. Deferred:this session's
mathias/infralocal clone had a stale/expired git credential (authfailure on
git pull), and secret-writes need their own explicit confirmation regardless.Until this lands, the overlay is inert in prod (graceful fallback, not broken).
via #5; this would generalize it across the user's repos).
Notes
Keep it read-only first. Respect Gitea's own visibility — satisfied here since the PAT is
Mathias's own account, so it only ever sees what Mathias can see.
Update: auth is now Authentik forward-auth (#3), so the authenticated user identity arrives as request headers — no in-app OIDC needed. The ingress passes
X-authentik-username,X-authentik-email,X-authentik-groups,X-authentik-uidto the app.So this issue simplifies: read those headers → map the Authentik user to their Gitea account → call the Gitea API for that user's projects/issues (pass-through auth pattern, brain
knowledge/mcp-pass-through-auth-pattern.md). Open question: how to get the user's Gitea token/identity from the Authentik identity (Gitea also auths via Authentik/Dex — likely a shared OIDC subject or an app-password exchange).