Files
tapir/docs/homelab-integration.md
T
mathias 0af2991d79
CI / Lint / Test / Vet (push) Successful in 12s
CI / Build & Import (push) Failing after 0s
CI / Mirror to GitHub (push) Has been skipped
merge: captions-first YouTube VideoSource (Worker C, agent/youtube-source)
2026-06-02 17:22:51 +02:00

152 lines
9.7 KiB
Markdown

# Tapir — Homelab Integration Facts
The concrete endpoints, conventions, and identifiers Tapir depends on, so an independent
session doesn't have to rediscover them. **Verify anything marked "confirm" before relying on
it** — endpoints and aliases drift, and this file is a snapshot (2026-06-02), not a live source.
## Local AI (the Primary in `llm.Router`)
- **LiteLLM gateway:** OpenAI-compatible, lives in k3s namespace `ai-stack` (verified 2026-06-02
from koala via `kubectl get svc -n ai-stack`):
- **In-cluster:** `http://litellm.ai-stack.svc.cluster.local:4000/v1/` (ClusterIP `10.43.159.89:4000`).
- **Off-cluster (host / NodePort):** `http://koala:30401/v1/` — service `litellm-nodeport` (`4000:30401`).
- NOTE: the earlier `31234` was **wrong**`31234` is the `llama-swap` NodePort (`8080:31234`),
not LiteLLM. Use `30401` for off-cluster LiteLLM. `confirm` resolved.
- **Auth key:** treat as a secret via the SecretStore port; do not hardcode in committed code.
NOTE: the documented `sk-local-123` returned `401` on `30401/v1/models` (2026-06-02) — it is
stale or the gateway now enforces the `LITELLM_MASTER_KEY` (1Password `op://HomeLab/LITELLM_MASTER_KEY`).
Resolve the live key from the vault when wiring; `sk-local-123` is no longer valid. `confirm` partially resolved.
- **Model alias format:** `host/name`, e.g. `koala/qwen3-coder-30b`, `koala/phi4-mini`,
`iguana/devstral`, `iguana/deepseek-r1-14b`. **Not** the `ollama/` prefix form.
- **Which alias for summarization:** NOT yet decided. `confirm`. Tapir summarizes transcript
text, so a capable general/instruct model on koala or iguana is the candidate — pick during the
build and record the choice (an ADR if it's load-bearing). Do not assume a coder alias is right
for prose summarization. The summarizer adapter does **not** hardcode an alias: it is config,
env `TAPIR_SUMMARIZER_MODEL` (format `host/name`, e.g. `iguana/deepseek-r1-14b`).
- **Thinking models need an explicit `max_tokens`.** qwen3 / deepseek-r1 spend the budget on
reasoning and return **empty content** if `max_tokens` is too low (or unset). The summarizer's
parser treats an empty summary as an error for exactly this reason. When the alias resolves to a
thinking model, add a generous `max_tokens` to the copied `llm.Client` request (it currently
sends none — change Tapir's copy per ADR-004), or pick a non-thinking instruct model.
This maps directly onto the copied `llm` package: `Client` is the OpenAI-compatible caller,
`Router.Primary` points at this gateway with a chosen alias, `Router.Fallback` is the user's BYO.
## BYO AI (the Fallback in `llm.Router`)
- Per-user, opt-in: Anthropic / OpenAI / Gemini. All reachable as OpenAI-compatible or via a thin
adapter. The user's key is stored only as a secret reference (data-model `AI_CREDENTIAL`).
- Used **only** when Primary fails and the user has configured a provider (see
`docs/use-cases/ai_routing.feature`). With no BYO, work is queued for retry — content is never
sent externally.
## Brain sink (optional delivery target)
- Reached via **brain-mcp** over HTTP, calling the `brain_ingest` tool (ADR-005). Do **not** use
the filesystem `brain` package from `hyperguild/ingestion`.
- **brain-mcp base URL:** **confirm** — not pinned in this snapshot. The homelab convention is
`*-mcp.d-ma.be` ingress hostnames (e.g. `git-mcp.d-ma.be`, `brain-mcp.d-ma.be` is the likely
form), but verify before wiring.
- brain distinguishes `knowledge/` (session-derived) from `wiki/` (stable, manually promoted).
Tapir summaries are session-derived signal → `knowledge/`-style ingestion. `brain_ingest` runs
the LLM extraction pipeline; there is a raw-ingest path (`brain_ingest_raw`) and a reliable
`brain_write` fallback when the local extraction model is unavailable — relevant if ingestion
reliability becomes an issue.
## Auth / identity (Stage 1+, dormant at Stage 0)
- **Dex** (`auth.d-ma.be`) is the homelab OIDC provider. When Tapir needs real user identity
(Stage 1, Future B), it authenticates via Dex — not a new auth system (ADR-002).
- If Tapir ever exposes an MCP surface, use the **`mcp-chassis`** shared Go library
(`gitea.d-ma.be/mathias/mcp-chassis`) for the Dex-JWT validator + Bearer middleware + RFC 9728
metadata, rather than hand-rolling it.
## Secrets (OAuth tokens + BYO keys)
- Convention: **ESO + 1Password** (the homelab's External Secrets Operator against a 1Password
vault). Tables store only an opaque `*_secret_ref`; the secret material lives in the vault and
is surfaced to the workload via ESO.
- **Exact ref format / vault item naming for Tapir:** **confirm / decide during build.** Follow
the pattern existing services use (e.g. how `gitea-mcp` references `GITEA_MCP_DEFAULT_TOKEN`)
rather than inventing a new scheme.
- **Adapter status (YouTube):** the YouTube `VideoSource` adapter does **not** assume a scheme.
The OAuth refresh-token reference is an opaque `Config.TokenSecretRef` resolved through the
`SecretStore` port (`youtube.New(cfg, secrets)`). Pinning the actual vault-item name only
changes wiring/config, not the adapter — so this `confirm` does not block the adapter. Decide
the name when wiring the live connection and record it here.
## Hosts (for reference)
- **koala** — Arch, RTX 5070, k3s single-node, Gitea, GPU inference. Where Tapir most likely runs
and where the local models live.
- **iguana** — Mac Studio M2 Ultra, ollama + mlx-whisper, also serves models via the gateway.
- **piguard** — Pi, NPM perimeter (public HTTPS edge). Note: the May-2026 architecture review has
LiteLLM relocating from piguard into k3s/ai-stack on koala — **confirm current location** if
latency or endpoint matters.
## Network egress the build and runtime assume
If the koala act_runner (or a dev environment) has locked-down egress, these must be reachable
or the build/run fails. Split into build-time and runtime because they're different lists and
fail at different stages.
### Build-time (CI `check`/`build`, and local `go`/`task` work)
- `proxy.golang.org`, `sum.golang.org` — Go module proxy + checksum DB. The default `GOPROXY`.
If a homelab GOPROXY (e.g. Athens) exists, point `GOPROXY` at it and this pair is not needed
directly — **confirm** whether one is run.
- `go.dev`, `dl.google.com` — Go toolchain download (the CI `setup-go` step / `go-version-file`).
Not needed if the runner already has the matching Go installed.
- `raw.githubusercontent.com` — the CI installs golangci-lint via its install script from here
(`.gitea/workflows/ci.yml`).
- `github.com`, `codeload.github.com` — any module not served by the proxy (incl. golangci-lint
and, later, `golang.org/x/oauth2` and its deps) is fetched from GitHub.
- `objects.githubusercontent.com` — GitHub release asset downloads (some installers redirect here).
A configured `GOPROXY` collapses most of the module-fetch entries into one host. Recording the
homelab's actual `GOPROXY`/`GONOSUMCHECK` policy here once known would remove the **confirm**.
### Runtime (the running service)
- `koala:4000` (or the NodePort / post-relocation address) — LiteLLM gateway, the Primary.
- brain-mcp host (`*-mcp.d-ma.be`, **confirm**) — only when the brain sink is enabled.
- `www.googleapis.com` / YouTube Data API + `oauth2.googleapis.com` (token exchange/refresh) —
the YouTube provider adapter (`internal/adapters/youtube`). OAuth is hand-rolled on
`golang.org/x/oauth2` (ADR-006); the Google token endpoint is hardcoded inline to avoid the
heavy `golang.org/x/oauth2/google` dependency.
- **`confirm` — captions.download ownership limitation.** The Data API `captions.download`
endpoint only authorizes the *owner* of the video; for arbitrary subscribed channels it
typically returns `403`. The adapter lists tracks via `captions.list` (no track → `SourceNone`,
ADR-007) and downloads the selected track as WebVTT; a download failure surfaces as an error
(retryable), not a silent skip. If the owner-only limit proves blocking in practice, the
fallback is the unofficial `timedtext` endpoint or the deferred STT path (ADR-007) — a new
ADR, not a default. Verify against a real non-owned video before relying on live caption
download.
- `api.vimeo.com` — the Vimeo provider adapter.
- BYO-AI endpoints, per configured provider and **only when a user opts in**:
`api.anthropic.com`, `api.openai.com`, `generativelanguage.googleapis.com`.
- 1Password / ESO reach for secret resolution follows the homelab's existing ESO egress, not
Tapir-specific — no new rule expected.
Note: a user with **no** BYO configured means the service never needs the third-party AI hosts
(the local-first guarantee, `docs/use-cases/ai_routing.feature`). Don't allow them by default;
allow per-provider when a user connects one.
> The claude.ai chat sandbox (where planning sessions run code) has its **own** separate egress
> allowlist — unrelated to koala. It blocked `go.dev` during scaffolding, which is why the
> scaffold was reviewed but not compile-verified in-chat; verification belongs on koala via
> `task check` regardless.
## Deployment / GitOps (when Tapir reaches deploy)
- The homelab is **Flux GitOps**: manifests in `mathias/infra` under `k3s/`, Flux watches `main`.
- Tapir's k3s manifests will live in `infra/k3s/apps/tapir/` (by convention) — not in this repo.
This repo is the application; `infra` is the deployment source of truth.
- Multi-tenancy primitives (NetworkPolicy per namespace, Kyverno, postgres role-per-tenant,
`tenant=` label) exist in the architecture review (SC7/P6) and apply at Stage 1 — not Stage 0.
---
_Snapshot date 2026-06-02. Items marked **confirm** were not verified to a pinned source at
snapshot time — check brain or the live cluster before depending on them._