Commit Graph
19 Commits
Author SHA1 Message Date
mathias fa57ee0532 docs(homelab): Stage-1 facts — RLS non-super DSN, per-user token PVC, web connect URI
CI / Lint / Test / Vet (push) Successful in 10s
CI / Build & Import (push) Successful in 10s
CI / Mirror to GitHub (push) Failing after 2s
Per the docs-currency gate (ADR-012 shipped): record the Stage-1 deployment
facts that future sessions would otherwise rediscover the hard way.
2026-06-03 18:28:29 +02:00
mathiasandClaude Opus 4.8 2aad79b2a8 feat(web): web-initiated YouTube OAuth connect flow (ADR-006)
CI / Lint / Test / Vet (push) Successful in 10s
CI / Build & Import (push) Successful in 10s
CI / Mirror to GitHub (push) Failing after 3s
Add GET /oauth/youtube/connect and /oauth/youtube/callback, mounted inside
the login + registration guard so CurrentUserID is always set and every
connection binds to the authenticated tapir user.

- connect: generate a per-user CSRF state (single-use, short TTL, bound to
  the user), redirect to Google consent with access_type=offline and
  prompt=consent so a refresh token comes back.
- callback: verify the state belongs to this user, exchange the code via the
  existing auth.Exchange, persist the refresh token under a PER-USER ref
  (web.YouTubeTokenRef = "youtube/<userID>/refresh_token") so tenants never
  collide, then UpsertConnection (provider=youtube, status=active). Any
  failure renders a clean error page and leaves no half-written state.

Reuses auth.Exchange and adds auth.AuthCodeURL (offline + consent) rather
than the CLI's listener/terminal flow (ADR-006: web flow, not CLI). The
ConnectHandler depends on a narrow web.Connections port, not the concrete
store. Wired in cmdServe only when YT client credentials are present;
TAPIR_YT_CONNECT_REDIRECT_URL configures the callback URL. Per-user token-ref
scheme documented in docs/homelab-integration.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 16:12:42 +02:00
mathiasandClaude Opus 4.8 e62df0027d refactor(oidc): drop single-subject allowlist, authenticate-only (ADR-012)
ADR-011's single-user authz (ID-token subject must equal AllowedSubject,
else 403) is replaced by ADR-012's model: Dex authentication is the only
gate — any Dex-authenticated subject may establish a session. Whether that
subject has a tapir user, and routing to registration if not, is decided
downstream in internal/web (next commit).

Removals (noted): oidc.Config.AllowedSubject + its required-field check + the
callback 403 branch; config.Config.AllowedSubject + TAPIR_ALLOWED_SUBJECT env
wiring; the AllowedSubject arg in cmdServe. ui-spec.md updated to reflect the
supersession. Sessions, cookie signing, login/callback/logout unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 15:56:13 +02:00
mathias 0f0d72ca8c docs(ux): after-screenshots of the polished Stage-0 reader
CI / Lint / Test / Vet (push) Failing after 7s
CI / Build & Import (push) Has been skipped
CI / Mirror to GitHub (push) Has been skipped
Light + dark list, mobile (375px) card reflow, and the reader detail page in
both schemes — captured via web-shot against the seeded serve. Visible evidence
that the Top 5 UX fixes landed.
2026-06-03 00:20:22 +02:00
mathiasandClaude Opus 4.8 ae239c02c2 docs(ux): Stage-0 reader UX review with screenshots
Reviewer pass against running `tapir serve` UI, seeded with 11
representative summaries (5 channels, rich+sparse, local+fallback,
varied action states). Captured via Playwright on koala k3s across
desktop/mobile and light/dark color schemes.

Two blocking findings: dark mode is unreadable (summary text near-black
on a dark canvas — `color-scheme: light dark` declared but `--fg`
hardcoded and no body background), and the list is a 6-column table that
does not reflow on mobile. Plus a sub-4.5:1 muted color, an unstyled
"admin table" surface, and small correctness nits (stray `· — ·` meta
join, cramped fallback badge). HTMX action toggles verified working.

Includes UX-REVIEW.md (severity-tagged findings + Top-5 sleek list) and
16 screenshots. No code changed — drives the next UI iteration.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 00:06:54 +02:00
mathiasandClaude Opus 4.8 13663c220b docs(ui): add Stage-0 Web UI spec + ADR-011 (Dex authn, action signal, tapir.d-ma.be GitOps)
CI / Build & Import (push) Failing after 0s
CI / Mirror to GitHub (push) Has been skipped
CI / Lint / Test / Vet (push) Successful in 6s
Reader + watch/skip/save (instruments the Stage-0 'acts on a summary' metric),
HTMX+Templ over the existing store, Dex OIDC login with single-user allowlist
authz (authn now, tenancy deferred), deployed at tapir.d-ma.be via Flux GitOps
with ESO secrets and in-cluster postgres18. Build decomposed into 4 gated lanes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 23:30:41 +02:00
mathiasandClaude Opus 4.8 92da25d01e docs(research): Vimeo + Whisper transcript feasibility findings
CI / Lint / Test / Vet (push) Successful in 6s
CI / Build & Import (push) Failing after 1s
CI / Mirror to GitHub (push) Has been skipped
Vimeo texttracks API is owner-only (worse than YouTube, no public timedtext) → defer.
Whisper viable as a no-caption fallback; berget/whisper-large-v3 already on the gateway
(cloud), local options on iguana/koala carry setup+GPU-contention cost (ADR-007). Both
investigated inline (session sub-agents are network-sandboxed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 23:14:03 +02:00
mathiasandClaude Opus 4.8 61796db16b feat(cmd): wire auth/run dispatcher + demo docs
main.go dispatches `tapir auth` (interactive OAuth → persist refresh token via
SecretStore) and `tapir run` (wire YouTube source + local summarizer + store
sink, build engine, run the dedup-aware loop). Config-driven so live creds plug
in at demo time; SIGINT stops the loop cleanly. Block kept minimal so Worker E's
list/show cases union cleanly at merge.

Add .env.example documenting every TAPIR_* var and a README demo runbook. Pin
the summarizer alias-as-config decision and record the max_tokens fix in
docs/homelab-integration.md (clears two `confirm` items).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 21:03:21 +02:00
mathias 0af2991d79 merge: captions-first YouTube VideoSource (Worker C, agent/youtube-source)
CI / Lint / Test / Vet (push) Successful in 12s
CI / Build & Import (push) Failing after 0s
CI / Mirror to GitHub (push) Has been skipped
2026-06-02 17:22:51 +02:00
mathiasandClaude Opus 4.8 39e8756e77 docs(homelab): note YouTube secret-ref is parameterized + captions.download owner-only caveat
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 17:06:40 +02:00
mathiasandClaude Opus 4.8 6da9d61e63 feat(adapters): add Summarizer backed by local-first llm routing
Implement ports.Summarizer in internal/adapters/summarizer. It routes through a
local Primary endpoint first and an optional BYO Fallback, owning the routing
itself (not delegating to llm.Router) so it can record AIProvider, AIModel, and
FallbackUsed on domain.Summary. Prompt asks for JSON {summary, highlights,
takeaways}; the parser tolerates thinking-model fences/reasoning and rejects an
empty summary.

The summarizer is the single egress point for content toward an AI model, so it
enforces the local-first guarantee from ai_routing.feature: with no BYO
configured (nil fallback) there is no external endpoint, so content reaches the
local stack and nowhere else. Tests assert all four scenarios via a fake client.

Model alias is config (TAPIR_SUMMARIZER_MODEL, host/name) — not hardcoded;
docs/homelab-integration.md notes it stays `confirm` and that thinking models
need an explicit max_tokens or they return empty content.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 17:04:29 +02:00
mathiasandClaude Opus 4.8 bb47f54a41 docs(homelab): pin verified LiteLLM endpoint (NodePort 30401, not 31234)
CI / Lint / Test / Vet (push) Successful in 4s
CI / Build & Import (push) Failing after 0s
CI / Mirror to GitHub (push) Has been skipped
Conductor verified against the live koala k3s cluster before launching the
adapter workers: LiteLLM is in ns ai-stack, ClusterIP 10.43.159.89:4000,
off-cluster NodePort 30401. The doc's 31234 was actually llama-swap. Also
flag that sk-local-123 now 401s — use LITELLM_MASTER_KEY from the vault.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 16:58:07 +02:00
mathias b211cd08f2 docs: add build + runtime network egress requirements
CI / Lint / Test / Vet (push) Failing after 2s
CI / Build & Import (push) Has been skipped
CI / Mirror to GitHub (push) Has been skipped
Lists the egress the build assumes (Go module proxy, toolchain download,
raw.githubusercontent for golangci-lint, github for non-proxied modules) and the
runtime assumes (LiteLLM gateway, brain-mcp, YouTube/Vimeo APIs, per-user BYO-AI
hosts only on opt-in), so a locked-down koala act_runner or dev env knows what to
allow or which GOPROXY to set. Notes the claude.ai sandbox allowlist is separate
and unrelated.
2026-06-02 11:40:50 +00:00
mathias 32e06843f1 docs: add homelab integration facts reference
Pins the concrete endpoints/conventions Tapir depends on so independent sessions
don't rediscover them: LiteLLM gateway (http://koala:4000/v1/, sk-local-123, host/name
alias format), BYO fallback, brain-mcp HTTP sink, Dex identity, ESO+1Password secrets,
hosts, and Flux GitOps deploy location. Verified facts recorded as such; unconfirmed
items (brain-mcp URL, exact secret-ref naming, post-relocation LiteLLM location,
summarization model alias) explicitly flagged 'confirm' rather than invented.
2026-06-02 10:37:55 +00:00
mathias 9848156cb3 docs(bdd): add account-connection feature
Gherkin spec for connecting YouTube/Vimeo accounts and configuring optional
per-provider BYO AI credentials: connections sync subscriptions, tokens/keys are
stored only as secret references (never in the clear), revocation stops watching
but preserves history. Encodes the secrets-by-reference and data-isolation
guardrails (ADR-002, ADR-006, data-model).
2026-06-02 10:32:48 +00:00
mathias 5c99c49a50 docs(bdd): add AI-routing feature (local-first, BYO fallback)
Gherkin spec for the routing guardrail: local produces the summary by default;
on local failure, fall back only to a user-configured BYO provider; with no BYO,
queue for retry and never send content to a third-party model. Encodes the
local-first/user-owned principle (VISION) as executable behavior.
2026-06-02 10:32:37 +00:00
mathias 0b124a648b docs(bdd): add summarize-new-video feature
Gherkin spec for the core use case: captioned video is summarized and delivered;
no-transcript video is recorded as skipped; unsubscribed channels are ignored;
already-summarized videos are not reprocessed. These scenarios seed the use-case
test suite (Clean Architecture core tested through fake adapters).
2026-06-02 10:32:27 +00:00
mathias 4456d25651 docs: add data model (Stage 0 / Stage 1 scope)
Per-user-isolated entities (no global cross-tenant video table per the S5/grill
correction), secrets stored by reference only (ESO/1Password, never the token),
brain delivery modelled as a sink_delivery row rather than brain-specific tables.
fallback_used recorded per summary as the Stage 0 quality signal. Future C dedup
and sharding explicitly out of scope.
2026-06-02 10:32:14 +00:00
mathias af38999ee2 docs: add C4 architecture + sequence diagrams (Mermaid)
Context and container diagrams, sequence diagrams for the core summarize-new-video
use case and the local-first/BYO AI routing fallback, and the Clean Architecture
layering. Ports & adapters keep the engine provider- and sink-agnostic, making
standalone-vs-homelab a wiring choice (ADR-003), not two codebases.
2026-06-02 10:31:37 +00:00