docs(ui-spec): add Dex auth details, invite onboarding, summarized-only filter, unavailable channels
This commit is contained in:
+11
-8
@@ -72,7 +72,12 @@ summary_actions
|
||||
and join into the existing `SummaryRow` reads so list/detail show current state.
|
||||
- This column is what makes the Stage-0 metric ("did I act on a summary?") queryable.
|
||||
|
||||
## 6. Auth (Dex OIDC, single-user authz)
|
||||
## 6. Auth (Dex OIDC)
|
||||
|
||||
Authentication is delegated to **Dex** (OIDC provider at `TAPIR_OIDC_ISSUER`, default
|
||||
`https://auth.d-ma.be`). Dex is configured with two connectors: a local-password connector
|
||||
(for invited users created via `tapir invite`) and a Google OIDC upstream connector. Any
|
||||
authenticated Dex subject can register a Tapir account (ADR-012: allowlist removed).
|
||||
|
||||
- **Flow:** standard Authorization Code. Use `coreos/go-oidc` + `golang.org/x/oauth2`
|
||||
(justify the deps in the commit; both are the homelab-standard OIDC libs and small).
|
||||
@@ -92,7 +97,7 @@ summary_actions
|
||||
`TAPIR_OIDC_ISSUER` (`https://auth.d-ma.be`), `TAPIR_DEX_CLIENT_ID`, `TAPIR_DEX_CLIENT_SECRET`,
|
||||
`TAPIR_OIDC_REDIRECT_URL` (`https://tapir.d-ma.be/auth/callback`), `TAPIR_SESSION_SECRET`.
|
||||
Reuses existing `TAPIR_DB_DSN`, `TAPIR_USER_ID` (the StubAuth dev subject only). No secrets
|
||||
committed. (`TAPIR_ALLOWED_SUBJECT` was removed by ADR-012.)
|
||||
committed. (`TAPIR_ALLOWED_SUBJECT` was removed by ADR-012; use the keys above.)
|
||||
|
||||
## 8. Deployment — k3s + Flux GitOps
|
||||
|
||||
@@ -165,10 +170,8 @@ distinguishable.
|
||||
| **Account management** | `/account` page with **disconnect** and **delete account**; delete removes only Tapir-side state and leaves the Dex identity intact. (§2 listed isolation/CRUD as non-goals.) | A real account needs a way out; deletion semantics are deliberately Tapir-side only. | ADR-013; `22eafcf`, `c7624d9`, `17d5e8c` |
|
||||
| **Immediate web summarization** | A "Summarize" button (`POST /v/{id}/summarize`) runs the engine in a background goroutine inside `serve`; the page HTMX-polls `GET /v/{id}/status`. (§2 said "triggering runs from the browser … do NOT build".) | Reading a list you can't act on is half a product; on-demand summarize closes the loop without waiting for a batch `tapir run`. | ADR-012, ADR-014; `25215cb`, `8c6c7ca` |
|
||||
| **Charmbracelet tapir spinner** | An animated in-flight indicator (charm palette) shown while a summarize is processing; an honest "queued/waiting" state under rate-limiting rather than a stuck spinner. | The spinner must tell the truth when the timedtext endpoint rate-limits (429), not imply imminence. | ADR-014; `25215cb`, `a4aeb5e` |
|
||||
| **Auto/manual summarization mode** | Per-user `auto_summarize`; manual (default) lists new videos unsummarized and queues via `summarize_requested`; a mode toggle at `/account/summarize-mode`. | Control over compute/noise — only summarize what the user cares about. | migration 006 (`748d5eb`, `bdbdce7`, `3014ee0`, `a269d4a`) |
|
||||
| **Auto/manual summarization mode** | Per-user `auto_summarize`; manual lists new videos unsummarized and queues via `summarize_requested`; a mode toggle at `/account/summarize-mode`. Default is **true** for new users (migration 011, ADR-018); existing rows back-filled via migration 012. | Control over compute/noise — only summarize what the user cares about. | migration 006 (`748d5eb`, `bdbdce7`, `3014ee0`, `a269d4a`); migration 011/012 |
|
||||
| **Public landing page** | `/welcome` mounted **outside** the auth guard; unauthenticated `/` redirects there; logout returns there (not `/auth/login`). (The spec guarded everything except `/healthz` and `/auth/*`.) | A first-time visitor needs a public "what is this / get started" page before the login wall. | `d83943c`, `0fdf2f7`, `3a27bf1`, `d208110`, `8ca374e`, `f15f57f` |
|
||||
|
||||
The original Stage-0 goals (read summaries, record watch/skip/save actions, Dex login, GitOps
|
||||
deploy) still hold — these are additions over that base, not replacements. The architecture
|
||||
stance is unchanged: every item above is web-surface or store work; the engine/ports/sinks core
|
||||
was not modified (ADR-003).
|
||||
| **Invite onboarding** | Second registration path alongside Google OIDC. `tapir invite <email>` (CLI) creates a Dex local-password CRD in the `auth` namespace and prints an invite URL valid for 7 days. `/invite/{token}` (web) is a public page where the recipient sets a password; on submit, the Dex password is activated and the user is redirected to login. Token expiry is 7 days (`inviteTTL = 7 * 24 * time.Hour` in `cmd/tapir/invite.go`). The token is single-use: `ClaimInvitation` consumes it atomically on POST. | Allows inviting users who do not have a Google account or who should not use the Google OIDC upstream. | migration 009; `cmd/tapir/invite.go`; `internal/web/invite.go` |
|
||||
| **Summarized-only filter** | `?summarized=1` query param on the list view. When set, only videos with a completed summary (`SummaryRow.Summarized = true`) are shown. Rendered as a "Summarized only" checkbox in the filter form. | Lets users focus on videos that are ready to read without scrolling past unsummarized entries. | `internal/web/view.go` (`Filter.OnlySummarized`) |
|
||||
| **Unavailable channels (account page)** | The `/account` page shows a "Unavailable channels" section when any channels returned HTTP 404 on the last discovery pass. Lists channel name, an "unavailable" badge, and the first-seen date. Data sourced from the `channel_errors` table (migration 013). | Surfaces silent failures so users know why some subscribed channels produce no new videos. | migration 013; `internal/web/account.go`; `internal/adapters/youtube/youtube.go` (`domain.ErrChannelUnavailable`) |
|
||||
|
||||
Reference in New Issue
Block a user